1
2
3
4
5 package pgo
6
7 import (
8 "bytes"
9 "internal/profile"
10 "strings"
11 "testing"
12 )
13
14 func testProfile(sampleTypes []*profile.ValueType, defaultSampleType string, values []int64) *profile.Profile {
15 main := &profile.Function{ID: 1, Name: "main.main", StartLine: 10}
16 foo := &profile.Function{ID: 2, Name: "main.foo", StartLine: 20}
17 mainLoc := &profile.Location{ID: 1, Line: []profile.Line{{Function: main, Line: 12}}}
18 fooLoc := &profile.Location{ID: 2, Line: []profile.Line{{Function: foo, Line: 22}}}
19 return &profile.Profile{
20 SampleType: sampleTypes,
21 DefaultSampleType: defaultSampleType,
22 Function: []*profile.Function{main, foo},
23 Location: []*profile.Location{mainLoc, fooLoc},
24 Sample: []*profile.Sample{
25
26 {Location: []*profile.Location{fooLoc, mainLoc}, Value: values},
27 },
28 }
29 }
30
31 func serializeProfile(t *testing.T, p *profile.Profile) *bytes.Reader {
32 t.Helper()
33 var buf bytes.Buffer
34 if err := p.Write(&buf); err != nil {
35 t.Fatalf("writing profile: %v", err)
36 }
37 return bytes.NewReader(buf.Bytes())
38 }
39
40 func TestFromPProfSampleType(t *testing.T) {
41 perf := []*profile.ValueType{
42 {Type: "cycles_sample", Unit: "count"},
43 {Type: "cycles_event", Unit: "count"},
44 }
45
46 tests := []struct {
47 name string
48 sampleTypes []*profile.ValueType
49 defaultType string
50 values []int64
51 wantErr string
52 wantTotalWeight int64
53 }{
54 {
55 name: "native samples/count",
56 sampleTypes: []*profile.ValueType{{Type: "samples", Unit: "count"}, {Type: "cpu", Unit: "nanoseconds"}},
57 values: []int64{5, 500},
58 wantTotalWeight: 5,
59 },
60 {
61 name: "native cpu/nanoseconds",
62 sampleTypes: []*profile.ValueType{{Type: "cpu", Unit: "nanoseconds"}},
63 values: []int64{500},
64 wantTotalWeight: 500,
65 },
66 {
67 name: "perf via default sample type",
68 sampleTypes: perf,
69 defaultType: "cycles_event",
70 values: []int64{5, 500},
71 wantTotalWeight: 500,
72 },
73 {
74 name: "perf without default uses last value type",
75 sampleTypes: perf,
76 values: []int64{5, 500},
77 wantTotalWeight: 500,
78 },
79 {
80 name: "heap profile rejected",
81 sampleTypes: []*profile.ValueType{{Type: "alloc_objects", Unit: "count"}, {Type: "inuse_space", Unit: "bytes"}},
82 defaultType: "inuse_space",
83 values: []int64{1, 2},
84 wantErr: "Go heap profile",
85 },
86 {
87 name: "mutex profile rejected",
88 sampleTypes: []*profile.ValueType{{Type: "contentions", Unit: "count"}, {Type: "delay", Unit: "nanoseconds"}},
89 values: []int64{1, 2},
90 wantErr: "Go mutex or block profile",
91 },
92 {
93 name: "goroutine profile rejected",
94 sampleTypes: []*profile.ValueType{{Type: "goroutine", Unit: "count"}},
95 values: []int64{1},
96 wantErr: "Go goroutine profile",
97 },
98 }
99
100 for _, tc := range tests {
101 t.Run(tc.name, func(t *testing.T) {
102 got, err := FromPProf(serializeProfile(t, testProfile(tc.sampleTypes, tc.defaultType, tc.values)))
103 if tc.wantErr != "" {
104 if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
105 t.Fatalf("FromPProf got err %v, want error containing %q", err, tc.wantErr)
106 }
107 return
108 }
109 if err != nil {
110 t.Fatalf("FromPProf: %v", err)
111 }
112 if len(got.NamedEdgeMap.ByWeight) == 0 {
113 t.Error("empty edge map, want a call edge")
114 }
115 if got.TotalWeight != tc.wantTotalWeight {
116 t.Errorf("TotalWeight = %d, want %d", got.TotalWeight, tc.wantTotalWeight)
117 }
118 })
119 }
120 }
121
122 func TestSampleValueIndexNoSampleTypes(t *testing.T) {
123 if _, err := sampleValueIndex(&profile.Profile{}); err == nil {
124 t.Error("sampleValueIndex succeeded on a profile with no sample types, want error")
125 }
126 }
127
View as plain text