|
@@ -101,7 +101,29 @@ var inlinesProfile = &Profile{
|
101
|
101
|
},
|
102
|
102
|
}
|
103
|
103
|
|
104
|
|
-func TestFilter(t *testing.T) {
|
|
104
|
+var emptyLinesLocs = []*Location{
|
|
105
|
+ {ID: 1, Mapping: mappings[0], Address: 0x1000, Line: []Line{{Function: functions[0], Line: 1}, {Function: functions[1], Line: 1}}},
|
|
106
|
+ {ID: 2, Mapping: mappings[0], Address: 0x2000, Line: []Line{}},
|
|
107
|
+ {ID: 3, Mapping: mappings[1], Address: 0x2000, Line: []Line{}},
|
|
108
|
+}
|
|
109
|
+
|
|
110
|
+var emptyLinesProfile = &Profile{
|
|
111
|
+ TimeNanos: 10000,
|
|
112
|
+ PeriodType: &ValueType{Type: "cpu", Unit: "milliseconds"},
|
|
113
|
+ Period: 1,
|
|
114
|
+ DurationNanos: 10e9,
|
|
115
|
+ SampleType: []*ValueType{{Type: "samples", Unit: "count"}},
|
|
116
|
+ Mapping: mappings,
|
|
117
|
+ Function: functions,
|
|
118
|
+ Location: emptyLinesLocs,
|
|
119
|
+ Sample: []*Sample{
|
|
120
|
+ {Value: []int64{1}, Location: []*Location{emptyLinesLocs[0], emptyLinesLocs[1]}},
|
|
121
|
+ {Value: []int64{2}, Location: []*Location{emptyLinesLocs[2]}},
|
|
122
|
+ {Value: []int64{3}, Location: []*Location{}},
|
|
123
|
+ },
|
|
124
|
+}
|
|
125
|
+
|
|
126
|
+func TestFilterSamplesByName(t *testing.T) {
|
105
|
127
|
for _, tc := range []struct {
|
106
|
128
|
// name is the name of the test case.
|
107
|
129
|
name string
|
|
@@ -392,6 +414,130 @@ func TestFilter(t *testing.T) {
|
392
|
414
|
}
|
393
|
415
|
}
|
394
|
416
|
|
|
417
|
+func TestShowFrom(t *testing.T) {
|
|
418
|
+ for _, tc := range []struct {
|
|
419
|
+ name string
|
|
420
|
+ profile *Profile
|
|
421
|
+ showFrom *regexp.Regexp
|
|
422
|
+ // wantMatch is the expected return value.
|
|
423
|
+ wantMatch bool
|
|
424
|
+ // wantSampleFuncs contains expected stack functions and sample value after
|
|
425
|
+ // filtering, in the same order as in the profile. The format is as
|
|
426
|
+ // returned by sampleFuncs function below, which is "callee caller: <num>".
|
|
427
|
+ wantSampleFuncs []string
|
|
428
|
+ }{
|
|
429
|
+ {
|
|
430
|
+ name: "nil showFrom keeps all frames",
|
|
431
|
+ profile: noInlinesProfile,
|
|
432
|
+ wantMatch: false,
|
|
433
|
+ wantSampleFuncs: allNoInlinesSampleFuncs,
|
|
434
|
+ },
|
|
435
|
+ {
|
|
436
|
+ name: "showFrom with no matches drops all samples",
|
|
437
|
+ profile: noInlinesProfile,
|
|
438
|
+ showFrom: regexp.MustCompile("unknown"),
|
|
439
|
+ wantMatch: false,
|
|
440
|
+ },
|
|
441
|
+ {
|
|
442
|
+ name: "showFrom matches function names",
|
|
443
|
+ profile: noInlinesProfile,
|
|
444
|
+ showFrom: regexp.MustCompile("fun1"),
|
|
445
|
+ wantMatch: true,
|
|
446
|
+ wantSampleFuncs: []string{
|
|
447
|
+ "fun0 fun1: 1",
|
|
448
|
+ "fun4 fun5 fun1: 2",
|
|
449
|
+ "fun9 fun4 fun10: 4",
|
|
450
|
+ },
|
|
451
|
+ },
|
|
452
|
+ {
|
|
453
|
+ name: "showFrom matches file names",
|
|
454
|
+ profile: noInlinesProfile,
|
|
455
|
+ showFrom: regexp.MustCompile("file1"),
|
|
456
|
+ wantMatch: true,
|
|
457
|
+ wantSampleFuncs: []string{
|
|
458
|
+ "fun0 fun1: 1",
|
|
459
|
+ "fun4 fun5 fun1: 2",
|
|
460
|
+ "fun9 fun4 fun10: 4",
|
|
461
|
+ },
|
|
462
|
+ },
|
|
463
|
+ {
|
|
464
|
+ name: "showFrom matches mapping names",
|
|
465
|
+ profile: noInlinesProfile,
|
|
466
|
+ showFrom: regexp.MustCompile("map1"),
|
|
467
|
+ wantMatch: true,
|
|
468
|
+ wantSampleFuncs: []string{
|
|
469
|
+ "fun9 fun4 fun10: 4",
|
|
470
|
+ },
|
|
471
|
+ },
|
|
472
|
+ {
|
|
473
|
+ name: "showFrom drops frames above highest of multiple matches",
|
|
474
|
+ profile: noInlinesProfile,
|
|
475
|
+ showFrom: regexp.MustCompile("fun[12]"),
|
|
476
|
+ wantMatch: true,
|
|
477
|
+ wantSampleFuncs: []string{
|
|
478
|
+ "fun0 fun1 fun2: 1",
|
|
479
|
+ "fun4 fun5 fun1: 2",
|
|
480
|
+ "fun9 fun4 fun10: 4",
|
|
481
|
+ },
|
|
482
|
+ },
|
|
483
|
+ {
|
|
484
|
+ name: "showFrom matches inline functions",
|
|
485
|
+ profile: inlinesProfile,
|
|
486
|
+ showFrom: regexp.MustCompile("fun0|fun5"),
|
|
487
|
+ wantMatch: true,
|
|
488
|
+ wantSampleFuncs: []string{
|
|
489
|
+ "fun0: 1",
|
|
490
|
+ "fun4 fun5: 2",
|
|
491
|
+ },
|
|
492
|
+ },
|
|
493
|
+ {
|
|
494
|
+ name: "showFrom drops frames above highest of multiple inline matches",
|
|
495
|
+ profile: inlinesProfile,
|
|
496
|
+ showFrom: regexp.MustCompile("fun[1245]"),
|
|
497
|
+ wantMatch: true,
|
|
498
|
+ wantSampleFuncs: []string{
|
|
499
|
+ "fun0 fun1 fun2: 1",
|
|
500
|
+ "fun4 fun5: 2",
|
|
501
|
+ },
|
|
502
|
+ },
|
|
503
|
+ {
|
|
504
|
+ name: "showFrom keeps all lines when matching mapping and function",
|
|
505
|
+ profile: inlinesProfile,
|
|
506
|
+ showFrom: regexp.MustCompile("map0|fun5"),
|
|
507
|
+ wantMatch: true,
|
|
508
|
+ wantSampleFuncs: []string{
|
|
509
|
+ "fun0 fun1 fun2 fun3: 1",
|
|
510
|
+ "fun4 fun5 fun6: 2",
|
|
511
|
+ },
|
|
512
|
+ },
|
|
513
|
+ {
|
|
514
|
+ name: "showFrom matches location with empty lines",
|
|
515
|
+ profile: emptyLinesProfile,
|
|
516
|
+ showFrom: regexp.MustCompile("map1"),
|
|
517
|
+ wantMatch: true,
|
|
518
|
+ wantSampleFuncs: []string{
|
|
519
|
+ ": 2",
|
|
520
|
+ },
|
|
521
|
+ },
|
|
522
|
+ } {
|
|
523
|
+ t.Run(tc.name, func(t *testing.T) {
|
|
524
|
+ p := tc.profile.Copy()
|
|
525
|
+
|
|
526
|
+ if gotMatch := p.ShowFrom(tc.showFrom); gotMatch != tc.wantMatch {
|
|
527
|
+ t.Errorf("match got %+v, want %+v", gotMatch, tc.wantMatch)
|
|
528
|
+ }
|
|
529
|
+
|
|
530
|
+ if got, want := strings.Join(sampleFuncs(p), "\n")+"\n", strings.Join(tc.wantSampleFuncs, "\n")+"\n"; got != want {
|
|
531
|
+ diff, err := proftest.Diff([]byte(want), []byte(got))
|
|
532
|
+ if err != nil {
|
|
533
|
+ t.Fatalf("failed to get diff: %v", err)
|
|
534
|
+ }
|
|
535
|
+ t.Errorf("profile samples got diff(want->got):\n%s", diff)
|
|
536
|
+ }
|
|
537
|
+ })
|
|
538
|
+ }
|
|
539
|
+}
|
|
540
|
+
|
395
|
541
|
// sampleFuncs returns a slice of strings where each string represents one
|
396
|
542
|
// profile sample in the format "<fun1> <fun2> <fun3>: <value>". This allows
|
397
|
543
|
// the expected values for test cases to be specifed in human-readable strings.
|