1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package profile
16
17 import (
18 "bytes"
19 "fmt"
20 "testing"
21
22 "github.com/google/pprof/internal/proftest"
23 )
24
25 func TestMapMapping(t *testing.T) {
26 pm := &profileMerger{
27 p: &Profile{},
28 mappings: make(map[mappingKey]*Mapping),
29 mappingsByID: make(map[uint64]mapInfo),
30 }
31 for _, tc := range []struct {
32 desc string
33 m1 Mapping
34 m2 Mapping
35 wantMerged bool
36 }{
37 {
38 desc: "same file name",
39 m1: Mapping{
40 ID: 1,
41 File: "test-file-1",
42 },
43 m2: Mapping{
44 ID: 2,
45 File: "test-file-1",
46 },
47 wantMerged: true,
48 },
49 {
50 desc: "same build ID",
51 m1: Mapping{
52 ID: 3,
53 BuildID: "test-build-id-1",
54 },
55 m2: Mapping{
56 ID: 4,
57 BuildID: "test-build-id-1",
58 },
59 wantMerged: true,
60 },
61 {
62 desc: "same fake mapping",
63 m1: Mapping{
64 ID: 5,
65 },
66 m2: Mapping{
67 ID: 6,
68 },
69 wantMerged: true,
70 },
71 {
72 desc: "different start",
73 m1: Mapping{
74 ID: 7,
75 Start: 0x1000,
76 Limit: 0x2000,
77 BuildID: "test-build-id-2",
78 },
79 m2: Mapping{
80 ID: 8,
81 Start: 0x3000,
82 Limit: 0x4000,
83 BuildID: "test-build-id-2",
84 },
85 wantMerged: true,
86 },
87 {
88 desc: "different file name",
89 m1: Mapping{
90 ID: 9,
91 File: "test-file-2",
92 },
93 m2: Mapping{
94 ID: 10,
95 File: "test-file-3",
96 },
97 },
98 {
99 desc: "different build id",
100 m1: Mapping{
101 ID: 11,
102 BuildID: "test-build-id-3",
103 },
104 m2: Mapping{
105 ID: 12,
106 BuildID: "test-build-id-4",
107 },
108 },
109 {
110 desc: "different size",
111 m1: Mapping{
112 ID: 13,
113 Start: 0x1000,
114 Limit: 0x3000,
115 BuildID: "test-build-id-5",
116 },
117 m2: Mapping{
118 ID: 14,
119 Start: 0x1000,
120 Limit: 0x5000,
121 BuildID: "test-build-id-5",
122 },
123 },
124 {
125 desc: "different offset",
126 m1: Mapping{
127 ID: 15,
128 Offset: 1,
129 BuildID: "test-build-id-6",
130 },
131 m2: Mapping{
132 ID: 16,
133 Offset: 2,
134 BuildID: "test-build-id-6",
135 },
136 },
137 } {
138 t.Run(tc.desc, func(t *testing.T) {
139 info1 := pm.mapMapping(&tc.m1)
140 info2 := pm.mapMapping(&tc.m2)
141 gotM1, gotM2 := *info1.m, *info2.m
142
143 wantM1 := tc.m1
144 wantM1.ID = gotM1.ID
145 if gotM1 != wantM1 {
146 t.Errorf("first mapping got %v, want %v", gotM1, wantM1)
147 }
148
149 if tc.wantMerged {
150 if gotM1 != gotM2 {
151 t.Errorf("first mapping got %v, second mapping got %v, want equal", gotM1, gotM2)
152 }
153 if info1.offset != 0 {
154 t.Errorf("first mapping info got offset %d, want 0", info1.offset)
155 }
156 if wantOffset := int64(tc.m1.Start) - int64(tc.m2.Start); wantOffset != info2.offset {
157 t.Errorf("second mapping info got offset %d, want %d", info2.offset, wantOffset)
158 }
159 } else {
160 if gotM1.ID == gotM2.ID {
161 t.Errorf("first mapping got %v, second mapping got %v, want different IDs", gotM1, gotM2)
162 }
163 wantM2 := tc.m2
164 wantM2.ID = gotM2.ID
165 if gotM2 != wantM2 {
166 t.Errorf("second mapping got %v, want %v", gotM2, wantM2)
167 }
168 }
169 })
170 }
171 }
172
173 func TestLocationIDMap(t *testing.T) {
174 ids := []uint64{1, 2, 5, 9, 10, 11, 100, 1000, 1000000}
175 missing := []uint64{3, 4, 200}
176
177
178 idmap := makeLocationIDMap(10)
179 for _, id := range ids {
180 loc := &Location{ID: id}
181 idmap.set(id, loc)
182 }
183
184
185 for _, id := range ids {
186 loc := idmap.get(id)
187 if loc == nil {
188 t.Errorf("No location found for %d", id)
189 } else if loc.ID != id {
190 t.Errorf("Wrong location %d found for %d", loc.ID, id)
191 }
192 }
193
194
195 for _, id := range missing {
196 loc := idmap.get(id)
197 if loc != nil {
198 t.Errorf("Unexpected location %d found for %d", loc.ID, id)
199 }
200 }
201 }
202
203 func BenchmarkMerge(b *testing.B) {
204 data := proftest.LargeProfile(b)
205 for n := 1; n <= 2; n++ {
206 b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
207 list := make([]*Profile, n)
208 for i := 0; i < n; i++ {
209 prof, err := Parse(bytes.NewBuffer(data))
210 if err != nil {
211 b.Fatal(err)
212 }
213 list[i] = prof
214 }
215 b.ResetTimer()
216 for i := 0; i < b.N; i++ {
217 _, err := Merge(list)
218 if err != nil {
219 b.Fatal(err)
220 }
221 }
222 })
223 }
224 }
225
226 func TestCompatibilizeSampleTypes(t *testing.T) {
227 for _, tc := range []struct {
228 desc string
229 ps []*Profile
230 want []*Profile
231 wantError bool
232 }{
233 {
234 desc: "drop first sample types",
235 ps: []*Profile{
236 {
237 DefaultSampleType: "delete1",
238 SampleType: []*ValueType{
239 {Type: "delete1", Unit: "Unit1"},
240 {Type: "delete2", Unit: "Unit2"},
241 {Type: "keep1", Unit: "Unit3"},
242 {Type: "keep2", Unit: "Unit4"},
243 {Type: "keep3", Unit: "Unit5"},
244 },
245 Sample: []*Sample{
246 {Value: []int64{1, 2, 3, 4, 5}},
247 {Value: []int64{10, 20, 30, 40, 50}},
248 },
249 },
250 {
251 DefaultSampleType: "keep1",
252 SampleType: []*ValueType{
253 {Type: "keep1", Unit: "Unit3"},
254 {Type: "keep2", Unit: "Unit4"},
255 {Type: "keep3", Unit: "Unit5"},
256 },
257 Sample: []*Sample{
258 {Value: []int64{1, 2, 3}},
259 {Value: []int64{10, 20, 30}},
260 },
261 },
262 },
263 want: []*Profile{
264 {
265 DefaultSampleType: "keep1",
266 SampleType: []*ValueType{
267 {Type: "keep1", Unit: "Unit3"},
268 {Type: "keep2", Unit: "Unit4"},
269 {Type: "keep3", Unit: "Unit5"},
270 },
271 Sample: []*Sample{
272 {Value: []int64{3, 4, 5}},
273 {Value: []int64{30, 40, 50}},
274 },
275 },
276 {
277 DefaultSampleType: "keep1",
278 SampleType: []*ValueType{
279 {Type: "keep1", Unit: "Unit3"},
280 {Type: "keep2", Unit: "Unit4"},
281 {Type: "keep3", Unit: "Unit5"},
282 },
283 Sample: []*Sample{
284 {Value: []int64{1, 2, 3}},
285 {Value: []int64{10, 20, 30}},
286 },
287 },
288 },
289 },
290 {
291 desc: "drop last sample types",
292 ps: []*Profile{
293 {
294 DefaultSampleType: "delete2",
295 SampleType: []*ValueType{
296 {Type: "keep1", Unit: "Unit3"},
297 {Type: "keep2", Unit: "Unit4"},
298 {Type: "keep3", Unit: "Unit5"},
299 {Type: "delete1", Unit: "Unit1"},
300 {Type: "delete2", Unit: "Unit2"},
301 },
302 Sample: []*Sample{
303 {Value: []int64{1, 2, 3, 4, 5}},
304 {Value: []int64{10, 20, 30, 40, 50}},
305 },
306 },
307 {
308 DefaultSampleType: "keep2",
309 SampleType: []*ValueType{
310 {Type: "keep1", Unit: "Unit3"},
311 {Type: "keep2", Unit: "Unit4"},
312 {Type: "keep3", Unit: "Unit5"},
313 },
314 Sample: []*Sample{
315 {Value: []int64{1, 2, 3}},
316 {Value: []int64{10, 20, 30}},
317 },
318 },
319 },
320 want: []*Profile{
321 {
322 DefaultSampleType: "keep1",
323 SampleType: []*ValueType{
324 {Type: "keep1", Unit: "Unit3"},
325 {Type: "keep2", Unit: "Unit4"},
326 {Type: "keep3", Unit: "Unit5"},
327 },
328 Sample: []*Sample{
329 {Value: []int64{1, 2, 3}},
330 {Value: []int64{10, 20, 30}},
331 },
332 },
333 {
334 DefaultSampleType: "keep2",
335 SampleType: []*ValueType{
336 {Type: "keep1", Unit: "Unit3"},
337 {Type: "keep2", Unit: "Unit4"},
338 {Type: "keep3", Unit: "Unit5"},
339 },
340 Sample: []*Sample{
341 {Value: []int64{1, 2, 3}},
342 {Value: []int64{10, 20, 30}},
343 },
344 },
345 },
346 },
347 {
348 desc: "drop sample types and reorder",
349 ps: []*Profile{
350 {
351 DefaultSampleType: "keep3",
352 SampleType: []*ValueType{
353 {Type: "delete1", Unit: "Unit1"},
354 {Type: "keep1", Unit: "Unit3"},
355 {Type: "delete2", Unit: "Unit2"},
356 {Type: "keep2", Unit: "Unit4"},
357 {Type: "keep3", Unit: "Unit5"},
358 },
359 Sample: []*Sample{
360 {Value: []int64{1, 2, 3, 4, 5}},
361 {Value: []int64{10, 20, 30, 40, 50}},
362 },
363 },
364 {
365 DefaultSampleType: "keep2",
366 SampleType: []*ValueType{
367 {Type: "keep3", Unit: "Unit5"},
368 {Type: "keep2", Unit: "Unit4"},
369 {Type: "keep1", Unit: "Unit3"},
370 },
371 Sample: []*Sample{
372 {Value: []int64{1, 2, 3}},
373 {Value: []int64{10, 20, 30}},
374 },
375 },
376 },
377 want: []*Profile{
378 {
379 DefaultSampleType: "keep3",
380 SampleType: []*ValueType{
381 {Type: "keep1", Unit: "Unit3"},
382 {Type: "keep2", Unit: "Unit4"},
383 {Type: "keep3", Unit: "Unit5"},
384 },
385 Sample: []*Sample{
386 {Value: []int64{2, 4, 5}},
387 {Value: []int64{20, 40, 50}},
388 },
389 },
390 {
391 DefaultSampleType: "keep2",
392 SampleType: []*ValueType{
393 {Type: "keep1", Unit: "Unit3"},
394 {Type: "keep2", Unit: "Unit4"},
395 {Type: "keep3", Unit: "Unit5"},
396 },
397 Sample: []*Sample{
398 {Value: []int64{3, 2, 1}},
399 {Value: []int64{30, 20, 10}},
400 },
401 },
402 },
403 },
404 {
405 desc: "empty common types",
406 ps: []*Profile{
407 {
408 SampleType: []*ValueType{
409 {Type: "keep1", Unit: "Unit1"},
410 {Type: "keep2", Unit: "Unit2"},
411 {Type: "keep3", Unit: "Unit3"},
412 },
413 },
414 {
415 SampleType: []*ValueType{
416 {Type: "keep4", Unit: "Unit4"},
417 {Type: "keep5", Unit: "Unit5"},
418 },
419 },
420 },
421 wantError: true,
422 },
423 } {
424 t.Run(tc.desc, func(t *testing.T) {
425 err := CompatibilizeSampleTypes(tc.ps)
426 if (err != nil) != tc.wantError {
427 t.Fatalf("CompatibilizeSampleTypes() returned error: %v, want any error=%t", err, tc.wantError)
428 }
429 if err != nil {
430 return
431 }
432 for i := 0; i < len(tc.want); i++ {
433 gotStr := tc.ps[i].String()
434 wantStr := tc.want[i].String()
435 if gotStr != wantStr {
436 d, err := proftest.Diff([]byte(wantStr), []byte(gotStr))
437 if err != nil {
438 t.Fatalf("failed to get diff: %v", err)
439 }
440 t.Errorf("CompatibilizeSampleTypes(): profile[%d] got diff (-want +got)\n%s", i, string(d))
441 }
442 }
443 })
444 }
445 }
446
View as plain text