1 // Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdint.h>
6 #include <sys/capability.h>
7 #include <sys/mount.h>
8 #include <sys/sysmacros.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11
12 #include <algorithm>
13 #include <map>
14 #include <set>
15 #include <string>
16 #include <vector>
17
18 #include "base/logging.h"
19
20 #include "compat/string.h"
21 #include "compat/test.h"
22 #include "compat/thread.h"
23 #include "dso_test_utils.h"
24 #include "perf_data_utils.h"
25 #include "perf_parser.h"
26 #include "perf_reader.h"
27 #include "perf_serializer.h"
28 #include "perf_test_files.h"
29 #include "scoped_temp_path.h"
30 #include "test_perf_data.h"
31 #include "test_utils.h"
32
33 namespace quipper {
34
35 using SampleEvent = PerfDataProto_SampleEvent;
36 using SampleInfo = PerfDataProto_SampleInfo;
37 using PerfEvent = PerfDataProto_PerfEvent;
38
39 namespace {
40
CheckChronologicalOrderOfEvents(const PerfReader & reader)41 void CheckChronologicalOrderOfEvents(const PerfReader &reader) {
42 if (reader.events().empty()) return;
43 const auto &events = reader.events();
44 uint64_t prev_time = GetTimeFromPerfEvent(events.Get(0));
45 for (int i = 1; i < events.size(); ++i) {
46 uint64_t new_time = GetTimeFromPerfEvent(events.Get(i));
47 CHECK_LE(prev_time, new_time);
48 prev_time = new_time;
49 }
50 }
51
CheckNoDuplicates(const std::vector<string> & list)52 void CheckNoDuplicates(const std::vector<string> &list) {
53 std::set<string> list_as_set(list.begin(), list.end());
54 if (list.size() != list_as_set.size())
55 ADD_FAILURE() << "Given list has at least one duplicate";
56 }
57
CreateFilenameToBuildIDMap(const std::vector<string> & filenames,unsigned int seed,std::map<string,string> * filenames_to_build_ids)58 void CreateFilenameToBuildIDMap(
59 const std::vector<string> &filenames, unsigned int seed,
60 std::map<string, string> *filenames_to_build_ids) {
61 srand(seed);
62 // Only use every other filename, so that half the filenames are unused.
63 for (size_t i = 0; i < filenames.size(); i += 2) {
64 u8 build_id[kBuildIDArraySize];
65 for (size_t j = 0; j < kBuildIDArraySize; ++j) build_id[j] = rand_r(&seed);
66
67 (*filenames_to_build_ids)[filenames[i]] =
68 RawDataToHexString(build_id, kBuildIDArraySize);
69 }
70 }
71
72 // Given a PerfReader that has already consumed an input perf data file, inject
73 // new build IDs for the MMAP'd files in the perf data and check that they have
74 // been correctly injected.
CheckFilenameAndBuildIDMethods(PerfReader * reader,const string & output_perf_data_prefix,unsigned int seed)75 void CheckFilenameAndBuildIDMethods(PerfReader *reader,
76 const string &output_perf_data_prefix,
77 unsigned int seed) {
78 // Check filenames.
79 std::vector<string> filenames;
80 reader->GetFilenames(&filenames);
81
82 ASSERT_FALSE(filenames.empty());
83 CheckNoDuplicates(filenames);
84
85 std::set<string> filename_set;
86 reader->GetFilenamesAsSet(&filename_set);
87
88 // Make sure all MMAP filenames are in the set.
89 for (const auto &event : reader->events()) {
90 if (event.header().type() == PERF_RECORD_MMAP) {
91 EXPECT_TRUE(filename_set.find(event.mmap_event().filename()) !=
92 filename_set.end())
93 << event.mmap_event().filename()
94 << " is not present in the filename set";
95 }
96 }
97
98 std::map<string, string> expected_map;
99 reader->GetFilenamesToBuildIDs(&expected_map);
100
101 // Inject some made up build ids.
102 std::map<string, string> filenames_to_build_ids;
103 CreateFilenameToBuildIDMap(filenames, seed, &filenames_to_build_ids);
104 ASSERT_TRUE(reader->InjectBuildIDs(filenames_to_build_ids));
105
106 // Reader should now correctly populate the filenames to build ids map.
107 std::map<string, string>::const_iterator it;
108 for (it = filenames_to_build_ids.begin(); it != filenames_to_build_ids.end();
109 ++it) {
110 expected_map[it->first] = it->second;
111 }
112 std::map<string, string> reader_map;
113 reader->GetFilenamesToBuildIDs(&reader_map);
114 ASSERT_EQ(expected_map, reader_map);
115
116 string output_perf_data1 = output_perf_data_prefix + ".parse.inject.out";
117 ASSERT_TRUE(reader->WriteFile(output_perf_data1));
118
119 // Perf should find the same build ids.
120 std::map<string, string> perf_build_id_map;
121 ASSERT_TRUE(GetPerfBuildIDMap(output_perf_data1, &perf_build_id_map));
122 ASSERT_EQ(expected_map, perf_build_id_map);
123
124 std::map<string, string> build_id_localizer;
125 // Only localize the first half of the files which have build ids.
126 for (size_t j = 0; j < filenames.size() / 2; ++j) {
127 string old_filename = filenames[j];
128 if (expected_map.find(old_filename) == expected_map.end()) continue;
129 string build_id = expected_map[old_filename];
130
131 string new_filename = old_filename + ".local";
132 filenames[j] = new_filename;
133 build_id_localizer[build_id] = new_filename;
134 expected_map[new_filename] = build_id;
135 expected_map.erase(old_filename);
136 }
137 reader->Localize(build_id_localizer);
138
139 // Filenames should be the same.
140 std::vector<string> new_filenames;
141 reader->GetFilenames(&new_filenames);
142 std::sort(filenames.begin(), filenames.end());
143 ASSERT_EQ(filenames, new_filenames);
144
145 // Build ids should be updated.
146 reader_map.clear();
147 reader->GetFilenamesToBuildIDs(&reader_map);
148 ASSERT_EQ(expected_map, reader_map);
149
150 string output_perf_data2 = output_perf_data_prefix + ".parse.localize.out";
151 ASSERT_TRUE(reader->WriteFile(output_perf_data2));
152
153 perf_build_id_map.clear();
154 ASSERT_TRUE(GetPerfBuildIDMap(output_perf_data2, &perf_build_id_map));
155 EXPECT_EQ(expected_map, perf_build_id_map);
156
157 std::map<string, string> filename_localizer;
158 // Only localize every third filename.
159 for (size_t j = 0; j < filenames.size(); j += 3) {
160 string old_filename = filenames[j];
161 string new_filename = old_filename + ".local2";
162 filenames[j] = new_filename;
163 filename_localizer[old_filename] = new_filename;
164
165 if (expected_map.find(old_filename) != expected_map.end()) {
166 string build_id = expected_map[old_filename];
167 expected_map[new_filename] = build_id;
168 expected_map.erase(old_filename);
169 }
170 }
171 reader->LocalizeUsingFilenames(filename_localizer);
172
173 // Filenames should be the same.
174 new_filenames.clear();
175 reader->GetFilenames(&new_filenames);
176 std::sort(filenames.begin(), filenames.end());
177 EXPECT_EQ(filenames, new_filenames);
178
179 // Build ids should be updated.
180 reader_map.clear();
181 reader->GetFilenamesToBuildIDs(&reader_map);
182 EXPECT_EQ(expected_map, reader_map);
183
184 string output_perf_data3 = output_perf_data_prefix + ".parse.localize2.out";
185 ASSERT_TRUE(reader->WriteFile(output_perf_data3));
186
187 perf_build_id_map.clear();
188 ASSERT_TRUE(GetPerfBuildIDMap(output_perf_data3, &perf_build_id_map));
189 EXPECT_EQ(expected_map, perf_build_id_map);
190 }
191
CopyActualEvents(const std::vector<ParsedEvent> & events,PerfDataProto * out)192 void CopyActualEvents(const std::vector<ParsedEvent> &events,
193 PerfDataProto *out) {
194 for (const auto &ev : events) {
195 if (ev.event_ptr == nullptr) {
196 continue;
197 }
198 *out->add_events() = *ev.event_ptr;
199 }
200 }
201
202 } // namespace
203
TEST(PerfParserTest,TestDSOAndOffsetConstructor)204 TEST(PerfParserTest, TestDSOAndOffsetConstructor) {
205 // DSOAndOffset contains a pointer to a dso info struct. Make sure this is
206 // initialized in a way such that DSOAndOffset::dso_name() executes without
207 // segfault and returns an empty string.
208 ParsedEvent::DSOAndOffset dso_and_offset;
209 EXPECT_TRUE(dso_and_offset.dso_name().empty());
210 }
211
212 class PerfDataFiles : public ::testing::TestWithParam<const char *> {};
213 class PerfPipedDataFiles : public ::testing::TestWithParam<const char *> {};
214
TEST_P(PerfDataFiles,NormalPerfData)215 TEST_P(PerfDataFiles, NormalPerfData) {
216 ScopedTempDir output_dir;
217 ASSERT_FALSE(output_dir.path().empty());
218 string output_path = output_dir.path();
219
220 int seed = 0;
221 string test_file = GetParam();
222 string input_perf_data = GetTestInputFilePath(test_file);
223 LOG(INFO) << "Testing " << input_perf_data;
224
225 PerfReader reader;
226 ASSERT_TRUE(reader.ReadFile(input_perf_data));
227
228 // Test the PerfReader stage of the processing before continuing.
229 string pr_output_perf_data = output_path + test_file + ".pr.out";
230 ASSERT_TRUE(reader.WriteFile(pr_output_perf_data));
231 EXPECT_TRUE(CheckPerfDataAgainstBaseline(pr_output_perf_data));
232
233 // Run it through PerfParser.
234 PerfParserOptions options = GetTestOptions();
235 options.sort_events_by_time = true;
236 PerfParser parser(&reader, options);
237 ASSERT_TRUE(parser.ParseRawEvents());
238
239 CHECK_GT(parser.parsed_events().size(), 0U);
240 CheckChronologicalOrderOfEvents(reader);
241
242 // Check perf event stats.
243 const PerfEventStats &stats = parser.stats();
244 EXPECT_GT(stats.num_sample_events, 0U);
245 EXPECT_GT(stats.num_mmap_events, 0U);
246 EXPECT_GT(stats.num_sample_events_mapped, 0U);
247 EXPECT_FALSE(stats.did_remap);
248
249 string parsed_perf_data = output_path + test_file + ".parse.out";
250 ASSERT_TRUE(reader.WriteFile(parsed_perf_data));
251
252 EXPECT_TRUE(CheckPerfDataAgainstBaseline(parsed_perf_data));
253 EXPECT_TRUE(ComparePerfBuildIDLists(input_perf_data, parsed_perf_data));
254
255 // Run the event parsing again, this time with remapping.
256 options = PerfParserOptions();
257 options.do_remap = true;
258 parser.set_options(options);
259 ASSERT_TRUE(parser.ParseRawEvents());
260
261 // Check perf event stats.
262 EXPECT_GT(stats.num_sample_events, 0U);
263 EXPECT_GT(stats.num_mmap_events, 0U);
264 EXPECT_GT(stats.num_sample_events_mapped, 0U);
265 EXPECT_TRUE(stats.did_remap);
266
267 // Remapped addresses should not match the original addresses.
268 string remapped_perf_data = output_path + test_file + ".parse.remap.out";
269 ASSERT_TRUE(reader.WriteFile(remapped_perf_data));
270 EXPECT_TRUE(CheckPerfDataAgainstBaseline(remapped_perf_data));
271
272 // Remapping again should produce the same addresses.
273 LOG(INFO) << "Reading in remapped data: " << remapped_perf_data;
274 PerfReader remap_reader;
275 ASSERT_TRUE(remap_reader.ReadFile(remapped_perf_data));
276
277 PerfParser remap_parser(&remap_reader, options);
278 ASSERT_TRUE(remap_parser.ParseRawEvents());
279
280 const PerfEventStats &remap_stats = remap_parser.stats();
281 EXPECT_GT(remap_stats.num_sample_events, 0U);
282 EXPECT_GT(remap_stats.num_mmap_events, 0U);
283 EXPECT_GT(remap_stats.num_sample_events_mapped, 0U);
284 EXPECT_TRUE(remap_stats.did_remap);
285
286 ASSERT_EQ(stats.num_sample_events, remap_stats.num_sample_events);
287 ASSERT_EQ(stats.num_mmap_events, remap_stats.num_mmap_events);
288 ASSERT_EQ(stats.num_sample_events_mapped,
289 remap_stats.num_sample_events_mapped);
290
291 string remapped_perf_data2 = output_path + test_file + ".parse.remap2.out";
292 ASSERT_TRUE(remap_reader.WriteFile(remapped_perf_data2));
293
294 // No need to call CheckPerfDataAgainstBaseline again. Just compare
295 // ParsedEvents.
296 const auto &parser_events = parser.parsed_events();
297 const auto &remap_parser_events = remap_parser.parsed_events();
298 EXPECT_EQ(parser_events.size(), remap_parser_events.size());
299 EXPECT_TRUE(std::equal(parser_events.begin(), parser_events.end(),
300 remap_parser_events.begin()));
301 EXPECT_TRUE(ComparePerfBuildIDLists(remapped_perf_data, remapped_perf_data2));
302
303 // This must be called when |reader| is no longer going to be used, as it
304 // modifies the contents of |reader|.
305 CheckFilenameAndBuildIDMethods(&reader, output_path + test_file, seed);
306 ++seed;
307 }
308
TEST_P(PerfPipedDataFiles,PipedModePerfData)309 TEST_P(PerfPipedDataFiles, PipedModePerfData) {
310 ScopedTempDir output_dir;
311 ASSERT_FALSE(output_dir.path().empty());
312 string output_path = output_dir.path();
313
314 int seed = 0;
315 const string test_file = GetParam();
316 string input_perf_data = GetTestInputFilePath(test_file);
317 LOG(INFO) << "Testing " << input_perf_data;
318 string output_perf_data = output_path + test_file + ".pr.out";
319
320 PerfReader reader;
321 ASSERT_TRUE(reader.ReadFile(input_perf_data));
322
323 // Check results from the PerfReader stage.
324 ASSERT_TRUE(reader.WriteFile(output_perf_data));
325 EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data));
326
327 PerfParserOptions options = GetTestOptions();
328 options.do_remap = true;
329 options.sort_events_by_time = true;
330 PerfParser parser(&reader, options);
331 ASSERT_TRUE(parser.ParseRawEvents());
332
333 EXPECT_GT(parser.stats().num_sample_events, 0U);
334 EXPECT_GT(parser.stats().num_mmap_events, 0U);
335 EXPECT_GT(parser.stats().num_sample_events_mapped, 0U);
336 EXPECT_TRUE(parser.stats().did_remap);
337
338 // This must be called when |reader| is no longer going to be used, as it
339 // modifies the contents of |reader|.
340 CheckFilenameAndBuildIDMethods(&reader, output_path + test_file, seed);
341 ++seed;
342 }
343
344 INSTANTIATE_TEST_CASE_P(
345 PerfParserTest, PerfDataFiles,
346 ::testing::ValuesIn(perf_test_files::GetPerfDataFiles()));
347 INSTANTIATE_TEST_CASE_P(
348 PerfParserTest, PerfPipedDataFiles,
349 ::testing::ValuesIn(perf_test_files::GetPerfPipedDataFiles()));
350
TEST(PerfParserTest,MapsSampleEventIp)351 TEST(PerfParserTest, MapsSampleEventIp) {
352 std::stringstream input;
353
354 // header
355 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
356
357 // data
358
359 // PERF_RECORD_HEADER_ATTR
360 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
361 true /*sample_id_all*/)
362 .WriteTo(&input);
363
364 // PERF_RECORD_MMAP
365 testing::ExampleMmapEvent(1001, 0x1c1000, 0x1000, 0, "/usr/lib/foo.so",
366 testing::SampleInfo().Tid(1001))
367 .WriteTo(&input); // 0
368 // becomes: 0x0000, 0x1000, 0
369 testing::ExampleMmapEvent(1001, 0x1c3000, 0x2000, 0x2000, "/usr/lib/bar.so",
370 testing::SampleInfo().Tid(1001))
371 .WriteTo(&input); // 1
372 // becomes: 0x1000, 0x2000, 0
373
374 // PERF_RECORD_MMAP2
375 testing::ExampleMmap2Event(1002, 0x2c1000, 0x2000, 0, "/usr/lib/baz.so",
376 testing::SampleInfo().Tid(1002))
377 .WriteTo(&input); // 2
378 // becomes: 0x0000, 0x2000, 0
379 testing::ExampleMmap2Event(1002, 0x2c3000, 0x1000, 0x3000, "/usr/lib/xyz.so",
380 testing::SampleInfo().Tid(1002))
381 .WriteTo(&input); // 3
382 // becomes: 0x1000, 0x1000, 0
383
384 // PERF_RECORD_SAMPLE
385 testing::ExamplePerfSampleEvent(
386 testing::SampleInfo().Ip(0x00000000001c1000).Tid(1001))
387 .WriteTo(&input); // 4
388 testing::ExamplePerfSampleEvent(
389 testing::SampleInfo().Ip(0x00000000001c100a).Tid(1001))
390 .WriteTo(&input); // 5
391 testing::ExamplePerfSampleEvent(
392 testing::SampleInfo().Ip(0x00000000001c3fff).Tid(1001))
393 .WriteTo(&input); // 6
394 testing::ExamplePerfSampleEvent(
395 testing::SampleInfo().Ip(0x00000000001c2bad).Tid(1001))
396 .WriteTo(&input); // 7 (not mapped)
397 testing::ExamplePerfSampleEvent(
398 testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002))
399 .WriteTo(&input); // 8
400 testing::ExamplePerfSampleEvent(
401 testing::SampleInfo().Ip(0x00000000002c5bad).Tid(1002))
402 .WriteTo(&input); // 9 (not mapped)
403 testing::ExamplePerfSampleEvent(
404 testing::SampleInfo().Ip(0x00000000002c300b).Tid(1002))
405 .WriteTo(&input); // 10
406
407 // not mapped yet:
408 testing::ExamplePerfSampleEvent(
409 testing::SampleInfo().Ip(0x00000000002c400b).Tid(1002))
410 .WriteTo(&input); // 11
411 testing::ExampleMmap2Event(1002, 0x2c4000, 0x1000, 0, "/usr/lib/new.so",
412 testing::SampleInfo().Tid(1002))
413 .WriteTo(&input); // 12
414 testing::ExamplePerfSampleEvent(
415 testing::SampleInfo().Ip(0x00000000002c400b).Tid(1002))
416 .WriteTo(&input); // 13
417
418 //
419 // Parse input.
420 //
421
422 PerfReader reader;
423 EXPECT_TRUE(reader.ReadFromString(input.str()));
424
425 PerfParserOptions options;
426 options.sample_mapping_percentage_threshold = 0;
427 options.do_remap = true;
428 PerfParser parser(&reader, options);
429 EXPECT_TRUE(parser.ParseRawEvents());
430
431 EXPECT_EQ(5, parser.stats().num_mmap_events);
432 EXPECT_EQ(9, parser.stats().num_sample_events);
433 EXPECT_EQ(6, parser.stats().num_sample_events_mapped);
434
435 const std::vector<ParsedEvent> &events = parser.parsed_events();
436 ASSERT_EQ(14, events.size());
437
438 // MMAPs
439
440 EXPECT_EQ(PERF_RECORD_MMAP, events[0].event_ptr->header().type());
441 EXPECT_EQ("/usr/lib/foo.so", events[0].event_ptr->mmap_event().filename());
442 EXPECT_EQ(0x0000, events[0].event_ptr->mmap_event().start());
443 EXPECT_EQ(0x1000, events[0].event_ptr->mmap_event().len());
444 EXPECT_EQ(0, events[0].event_ptr->mmap_event().pgoff());
445
446 EXPECT_EQ(PERF_RECORD_MMAP, events[1].event_ptr->header().type());
447 EXPECT_EQ("/usr/lib/bar.so", events[1].event_ptr->mmap_event().filename());
448 EXPECT_EQ(0x1000, events[1].event_ptr->mmap_event().start());
449 EXPECT_EQ(0x2000, events[1].event_ptr->mmap_event().len());
450 EXPECT_EQ(0x2000, events[1].event_ptr->mmap_event().pgoff());
451
452 EXPECT_EQ(PERF_RECORD_MMAP2, events[2].event_ptr->header().type());
453 EXPECT_EQ("/usr/lib/baz.so", events[2].event_ptr->mmap_event().filename());
454 EXPECT_EQ(0x0000, events[2].event_ptr->mmap_event().start());
455 EXPECT_EQ(0x2000, events[2].event_ptr->mmap_event().len());
456 EXPECT_EQ(0, events[2].event_ptr->mmap_event().pgoff());
457
458 EXPECT_EQ(PERF_RECORD_MMAP2, events[3].event_ptr->header().type());
459 EXPECT_EQ("/usr/lib/xyz.so", events[3].event_ptr->mmap_event().filename());
460 EXPECT_EQ(0x2000, events[3].event_ptr->mmap_event().start());
461 EXPECT_EQ(0x1000, events[3].event_ptr->mmap_event().len());
462 EXPECT_EQ(0x3000, events[3].event_ptr->mmap_event().pgoff());
463
464 // SAMPLEs
465
466 EXPECT_EQ(PERF_RECORD_SAMPLE, events[4].event_ptr->header().type());
467 EXPECT_EQ("/usr/lib/foo.so", events[4].dso_and_offset.dso_name());
468 EXPECT_EQ(0x0, events[4].dso_and_offset.offset());
469 EXPECT_EQ(0x0, events[4].event_ptr->sample_event().ip());
470
471 EXPECT_EQ(PERF_RECORD_SAMPLE, events[5].event_ptr->header().type());
472 EXPECT_EQ("/usr/lib/foo.so", events[5].dso_and_offset.dso_name());
473 EXPECT_EQ(0xa, events[5].dso_and_offset.offset());
474 EXPECT_EQ(0xa, events[5].event_ptr->sample_event().ip());
475
476 EXPECT_EQ(PERF_RECORD_SAMPLE, events[6].event_ptr->header().type());
477 EXPECT_EQ("/usr/lib/bar.so", events[6].dso_and_offset.dso_name());
478 EXPECT_EQ(0x2fff, events[6].dso_and_offset.offset());
479 EXPECT_EQ(0x1fff, events[6].event_ptr->sample_event().ip());
480
481 EXPECT_EQ(PERF_RECORD_SAMPLE, events[7].event_ptr->header().type());
482 EXPECT_EQ(0x00000000001c2bad, events[7].event_ptr->sample_event().ip());
483
484 EXPECT_EQ(PERF_RECORD_SAMPLE, events[8].event_ptr->header().type());
485 EXPECT_EQ("/usr/lib/baz.so", events[8].dso_and_offset.dso_name());
486 EXPECT_EQ(0xa, events[8].dso_and_offset.offset());
487 EXPECT_EQ(0xa, events[8].event_ptr->sample_event().ip());
488
489 EXPECT_EQ(PERF_RECORD_SAMPLE, events[9].event_ptr->header().type());
490 EXPECT_EQ(0x00000000002c5bad, events[9].event_ptr->sample_event().ip());
491
492 EXPECT_EQ(PERF_RECORD_SAMPLE, events[10].event_ptr->header().type());
493 EXPECT_EQ("/usr/lib/xyz.so", events[10].dso_and_offset.dso_name());
494 EXPECT_EQ(0x300b, events[10].dso_and_offset.offset());
495 EXPECT_EQ(0x200b, events[10].event_ptr->sample_event().ip());
496
497 // not mapped yet:
498 EXPECT_EQ(PERF_RECORD_SAMPLE, events[11].event_ptr->header().type());
499 EXPECT_EQ(0x00000000002c400b, events[11].event_ptr->sample_event().ip());
500
501 EXPECT_EQ(PERF_RECORD_MMAP2, events[12].event_ptr->header().type());
502 EXPECT_EQ("/usr/lib/new.so", events[12].event_ptr->mmap_event().filename());
503 EXPECT_EQ(0x3000, events[12].event_ptr->mmap_event().start());
504 EXPECT_EQ(0x1000, events[12].event_ptr->mmap_event().len());
505 EXPECT_EQ(0, events[12].event_ptr->mmap_event().pgoff());
506
507 EXPECT_EQ(PERF_RECORD_SAMPLE, events[13].event_ptr->header().type());
508 EXPECT_EQ("/usr/lib/new.so", events[13].dso_and_offset.dso_name());
509 EXPECT_EQ(0xb, events[13].dso_and_offset.offset());
510 EXPECT_EQ(0x300b, events[13].event_ptr->sample_event().ip());
511 }
512
TEST(PerfParserTest,DsoInfoHasBuildId)513 TEST(PerfParserTest, DsoInfoHasBuildId) {
514 std::stringstream input;
515
516 // header
517 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
518
519 // data
520
521 // PERF_RECORD_HEADER_ATTR
522 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
523 true /*sample_id_all*/)
524 .WriteTo(&input);
525
526 // PERF_RECORD_MMAP
527 testing::ExampleMmapEvent(1001, 0x1c1000, 0x1000, 0, "/usr/lib/foo.so",
528 testing::SampleInfo().Tid(1001))
529 .WriteTo(&input); // 0
530 // becomes: 0x0000, 0x1000, 0
531 testing::ExampleMmapEvent(1001, 0x1c3000, 0x2000, 0x2000, "/usr/lib/bar.so",
532 testing::SampleInfo().Tid(1001))
533 .WriteTo(&input); // 1
534 // becomes: 0x1000, 0x2000, 0
535
536 // PERF_RECORD_HEADER_BUILDID // N/A
537 string build_id_filename("/usr/lib/foo.so\0", 2 * sizeof(u64));
538 ASSERT_EQ(0, build_id_filename.size() % sizeof(u64)) << "Sanity check";
539 const size_t event_size =
540 sizeof(struct build_id_event) + build_id_filename.size();
541 const struct build_id_event event = {
542 .header =
543 {
544 .type = PERF_RECORD_HEADER_BUILD_ID,
545 .misc = 0,
546 .size = static_cast<u16>(event_size),
547 },
548 .pid = -1,
549 .build_id = {0xde, 0xad, 0xf0, 0x0d},
550 };
551 input.write(reinterpret_cast<const char *>(&event), sizeof(event));
552 input.write(build_id_filename.data(), build_id_filename.size());
553
554 // PERF_RECORD_SAMPLE
555 testing::ExamplePerfSampleEvent(
556 testing::SampleInfo().Ip(0x00000000001c1000).Tid(1001))
557 .WriteTo(&input); // 2
558 testing::ExamplePerfSampleEvent(
559 testing::SampleInfo().Ip(0x00000000001c300a).Tid(1001))
560 .WriteTo(&input); // 3
561
562 //
563 // Parse input.
564 //
565
566 PerfReader reader;
567 EXPECT_TRUE(reader.ReadFromString(input.str()));
568
569 PerfParserOptions options;
570 options.sample_mapping_percentage_threshold = 0;
571 PerfParser parser(&reader, options);
572 EXPECT_TRUE(parser.ParseRawEvents());
573
574 EXPECT_EQ(2, parser.stats().num_mmap_events);
575 EXPECT_EQ(2, parser.stats().num_sample_events);
576 EXPECT_EQ(2, parser.stats().num_sample_events_mapped);
577
578 const std::vector<ParsedEvent> &events = parser.parsed_events();
579 ASSERT_EQ(4, events.size());
580
581 EXPECT_EQ("/usr/lib/foo.so", events[2].dso_and_offset.dso_name());
582 EXPECT_EQ("deadf00d00000000000000000000000000000000",
583 events[2].dso_and_offset.build_id());
584 EXPECT_EQ("/usr/lib/bar.so", events[3].dso_and_offset.dso_name());
585 EXPECT_EQ("", events[3].dso_and_offset.build_id());
586 }
587
588 // Check the process has a Linux capability. See libcap(3) and capabilities(7).
HaveCapability(cap_value_t capability)589 bool HaveCapability(cap_value_t capability) {
590 cap_t capabilities = cap_get_proc();
591 cap_flag_value_t value;
592 CHECK_EQ(cap_get_flag(capabilities, capability, CAP_EFFECTIVE, &value), 0);
593 cap_free(capabilities);
594 return value == CAP_SET;
595 }
596
597 class RunInMountNamespaceThread : public quipper::Thread {
598 public:
RunInMountNamespaceThread(string tmpdir,string mntdir)599 explicit RunInMountNamespaceThread(string tmpdir, string mntdir)
600 : quipper::Thread("MntNamespace"),
601 tmpdir_(std::move(tmpdir)),
602 mntdir_(std::move(mntdir)) {}
603
Start()604 void Start() override {
605 quipper::Thread::Start();
606 ready.Wait();
607 }
608
Join()609 void Join() override {
610 exit.Notify();
611 quipper::Thread::Join();
612 }
613
614 private:
Run()615 void Run() override {
616 CHECK_EQ(unshare(CLONE_NEWNS), 0);
617 CHECK_EQ(mount(tmpdir_.c_str(), mntdir_.c_str(), nullptr, MS_BIND, nullptr),
618 0);
619 ready.Notify();
620 exit.Wait();
621 }
622
623 Notification ready;
624 Notification exit;
625 string tmpdir_;
626 string mntdir_;
627 };
628
629 // Root task <pid>/<pid> Filesystem:
630 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
631 // "/tmp/quipper_mnt.../file_in_namespace" (Doesn't exist)
632 // Container task <pid>/<tid> Filesystem:
633 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
634 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: deadbeef ino: X
635 // <path> = marked with *
636 // MMAP2: <pid+10>/<tid+1>, <path>, ino: X
637 // MMAP2: <pid>/<tid>, <path>, ino: X
638 // Reject (doesn't exist): /proc/<tid+10>/root/<path>
639 // Reject (doesn't exist): /proc/<pid+1>/root/<path>
640 // Accept: /proc/<tid>/root/<path>
641 // (Not tried): /proc/<pid>/root/<path>
642 // (Not tried): /<path>
643 // Expected buildid for <path>: "deadbeef"
TEST(PerfParserTest,ReadsBuildidsInMountNamespace)644 TEST(PerfParserTest, ReadsBuildidsInMountNamespace) {
645 if (!HaveCapability(CAP_SYS_ADMIN)) return; // Skip test.
646 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
647 ScopedTempDir mntdir("/tmp/quipper_mnt.");
648 RunInMountNamespaceThread thread(tmpdir.path(), mntdir.path());
649 thread.Start();
650 const pid_t pid = getpid();
651 const pid_t tid = thread.tid();
652
653 const string tmpfile = tmpdir.path() + "file_in_namespace";
654 const string tmpfile_in_ns = mntdir.path() + "file_in_namespace";
655 InitializeLibelf();
656 testing::WriteElfWithBuildid(tmpfile, ".note.gnu.build-id",
657 "\xde\xad\xbe\xef");
658 struct stat tmp_stat;
659 ASSERT_NE(stat(tmpfile_in_ns.c_str(), &tmp_stat), 0);
660 ASSERT_EQ(stat(tmpfile.c_str(), &tmp_stat), 0);
661
662 // Create perf.data
663 std::stringstream input;
664
665 // header
666 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
667
668 // data
669
670 // PERF_RECORD_HEADER_ATTR
671 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
672 true /*sample_id_all*/)
673 .WriteTo(&input);
674
675 // PERF_RECORD_MMAP2
676 // - mmap from a process and thread that doesn't exist
677 testing::ExampleMmap2Event(pid, tid, 0x1c1000, 0x1000, 0, tmpfile_in_ns,
678 testing::SampleInfo().Tid(pid + 10, tid + 1))
679 .WithDeviceInfo(major(tmp_stat.st_dev), minor(tmp_stat.st_dev),
680 tmp_stat.st_ino)
681 .WriteTo(&input); // 0
682 // - mmap from a running thread
683 testing::ExampleMmap2Event(pid, tid, 0x1c2000, 0x1000, 0, tmpfile_in_ns,
684 testing::SampleInfo().Tid(pid, tid))
685 .WithDeviceInfo(major(tmp_stat.st_dev), minor(tmp_stat.st_dev),
686 tmp_stat.st_ino)
687 .WriteTo(&input); // 1
688
689 // PERF_RECORD_SAMPLE
690 testing::ExamplePerfSampleEvent(
691 testing::SampleInfo().Ip(0x00000000001c1000).Tid(pid, tid))
692 .WriteTo(&input); // 2
693
694 //
695 // Parse input.
696 //
697
698 PerfReader reader;
699 EXPECT_TRUE(reader.ReadFromString(input.str()));
700
701 PerfParserOptions options;
702 options.read_missing_buildids = true;
703 options.sample_mapping_percentage_threshold = 0;
704 PerfParser parser(&reader, options);
705 EXPECT_TRUE(parser.ParseRawEvents());
706
707 EXPECT_EQ(2, parser.stats().num_mmap_events);
708 EXPECT_EQ(1, parser.stats().num_sample_events);
709 EXPECT_EQ(1, parser.stats().num_sample_events_mapped);
710
711 const std::vector<ParsedEvent> &events = parser.parsed_events();
712 ASSERT_EQ(3, events.size());
713
714 EXPECT_EQ(tmpfile_in_ns, events[2].dso_and_offset.dso_name());
715 EXPECT_EQ("deadbeef", events[2].dso_and_offset.build_id());
716
717 thread.Join();
718 }
719
720 class RunInMountNamespaceProcess {
721 public:
RunInMountNamespaceProcess(string tmpdir,string mntdir)722 RunInMountNamespaceProcess(string tmpdir, string mntdir)
723 : pid_(0), tmpdir_(std::move(tmpdir)), mntdir_(std::move(mntdir)) {}
724
Start()725 void Start() {
726 int pipe_fd[2];
727 int nonce = 0;
728 CHECK_EQ(pipe(pipe_fd), 0) << "pipe: " << strerror(errno);
729
730 pid_ = fork();
731 CHECK_NE(-1, pid_) << "fork: " << strerror(errno);
732 if (pid_ == 0) { // child
733 close(pipe_fd[0]);
734 CHECK_EQ(unshare(CLONE_NEWNS), 0);
735 CHECK_EQ(
736 mount(tmpdir_.c_str(), mntdir_.c_str(), nullptr, MS_BIND, nullptr),
737 0);
738 // Tell parent mounting is done.
739 CHECK_EQ(write(pipe_fd[1], &nonce, sizeof(nonce)),
740 static_cast<ssize_t>(sizeof(nonce)));
741 close(pipe_fd[1]);
742 pause(); // Wait to be killed. (So morbid.)
743 std::_Exit(-1);
744 }
745 close(pipe_fd[1]);
746
747 // Wait for child to tell us it has mounted the dir.
748 ssize_t sz = read(pipe_fd[0], &nonce, sizeof(nonce));
749 CHECK_EQ(sz, static_cast<ssize_t>(sizeof(nonce)))
750 << "read: " << strerror(errno);
751 close(pipe_fd[0]);
752 }
753
Exit()754 void Exit() {
755 kill(pid_, SIGTERM);
756 waitpid(pid_, nullptr, 0);
757 }
758
pid()759 pid_t pid() { return pid_; }
760
761 private:
762 pid_t pid_;
763 string tmpdir_;
764 string mntdir_;
765 };
766
767 // Root task <pid>/<pid> Filesystem:
768 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
769 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: baadf00d ino: Y
770 // Container task <pid2>/<pid2> Filesystem:
771 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
772 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: deadbeef ino: X
773 // <path> = marked with *
774 // MMAP2: <pid2>/<pid2+1>, <path>, ino: X
775 // Reject (doesn't exist): /proc/<pid2+1>/root/<path>
776 // Accept: /proc/<pid2>/root/<path>
777 // (Not tried): /<path>
778 // Expected buildid for <path>: "deadbeef"
TEST(PerfParserTest,ReadsBuildidsInMountNamespace_TriesOwningProcess)779 TEST(PerfParserTest, ReadsBuildidsInMountNamespace_TriesOwningProcess) {
780 if (!HaveCapability(CAP_SYS_ADMIN)) return; // Skip test.
781 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
782 ScopedTempDir mntdir("/tmp/quipper_mnt.");
783 RunInMountNamespaceProcess process(tmpdir.path(), mntdir.path());
784 process.Start();
785
786 // Pretend we launched a thread in the other process, it mapped the file,
787 // and then exited. Let's make up a tid for it that's not likely to exist.
788 const pid_t pid = process.pid();
789 const pid_t tid = pid + 1;
790
791 const string tmpfile = tmpdir.path() + "file_in_namespace";
792 const string tmpfile_in_ns = mntdir.path() + "file_in_namespace";
793 InitializeLibelf();
794 testing::WriteElfWithBuildid(tmpfile, ".note.gnu.build-id",
795 "\xde\xad\xbe\xef");
796 // It's a trap! If "baadf00d" is seen, we read the wrong file.
797 testing::WriteElfWithBuildid(tmpfile_in_ns, ".note.gnu.build-id",
798 "\xba\xad\xf0\x0d");
799 struct stat tmp_stat;
800 ASSERT_EQ(stat(tmpfile.c_str(), &tmp_stat), 0);
801
802 // Create perf.data
803 std::stringstream input;
804
805 // header
806 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
807
808 // data
809
810 // PERF_RECORD_HEADER_ATTR
811 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
812 true /*sample_id_all*/)
813 .WriteTo(&input);
814
815 // PERF_RECORD_MMAP2
816 testing::ExampleMmap2Event(pid, tid, 0x1c1000, 0x1000, 0, tmpfile_in_ns,
817 testing::SampleInfo().Tid(pid, tid))
818 .WithDeviceInfo(major(tmp_stat.st_dev), minor(tmp_stat.st_dev),
819 tmp_stat.st_ino)
820 .WriteTo(&input); // 0
821
822 // PERF_RECORD_SAMPLE
823 testing::ExamplePerfSampleEvent(
824 testing::SampleInfo().Ip(0x00000000001c1000).Tid(pid, tid))
825 .WriteTo(&input); // 1
826
827 //
828 // Parse input.
829 //
830
831 PerfReader reader;
832 EXPECT_TRUE(reader.ReadFromString(input.str()));
833
834 PerfParserOptions options;
835 options.read_missing_buildids = true;
836 options.sample_mapping_percentage_threshold = 0;
837 PerfParser parser(&reader, options);
838 EXPECT_TRUE(parser.ParseRawEvents());
839
840 EXPECT_EQ(1, parser.stats().num_mmap_events);
841 EXPECT_EQ(1, parser.stats().num_sample_events);
842 EXPECT_EQ(1, parser.stats().num_sample_events_mapped);
843
844 const std::vector<ParsedEvent> &events = parser.parsed_events();
845 ASSERT_EQ(2, events.size());
846
847 EXPECT_EQ(tmpfile_in_ns, events[1].dso_and_offset.dso_name());
848 EXPECT_EQ("deadbeef", events[1].dso_and_offset.build_id());
849
850 process.Exit();
851 }
852
853 // Root task <pid>/<pid> Filesystem:
854 // * "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
855 // "/tmp/quipper_mnt.../file_in_namespace" buildid: baadf00d ino: Y
856 // Container task <pid>/<tid> Filesystem:
857 // * "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
858 // "/tmp/quipper_mnt.../file_in_namespace" buildid: deadbeef ino: X
859 // <path> = marked with *
860 // MMAP2: <pid+10>/<tid+1>, <path>, ino: X
861 // Reject (doesn't exist): /proc/<tid+1>/root/<path>
862 // Reject (doesn't exist): /proc/<pid+10>/root/<path>
863 // Accept (same inode): /<path>
864 // Expected buildid for <path>: "deadbeef"
TEST(PerfParserTest,ReadsBuildidsInMountNamespace_TriesRootFs)865 TEST(PerfParserTest, ReadsBuildidsInMountNamespace_TriesRootFs) {
866 if (!HaveCapability(CAP_SYS_ADMIN)) return; // Skip test.
867 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
868 ScopedTempDir mntdir("/tmp/quipper_mnt.");
869 RunInMountNamespaceThread thread(tmpdir.path(), mntdir.path());
870 thread.Start();
871 const pid_t pid = getpid();
872 const pid_t tid = thread.tid();
873
874 const string tmpfile = tmpdir.path() + "file_in_namespace";
875 const string tmpfile_in_ns = mntdir.path() + "file_in_namespace";
876 InitializeLibelf();
877 testing::WriteElfWithBuildid(tmpfile, ".note.gnu.build-id",
878 "\xde\xad\xbe\xef");
879 // It's a trap! If "baadf00d" is seen, we read the wrong file.
880 testing::WriteElfWithBuildid(tmpfile_in_ns, ".note.gnu.build-id",
881 "\xba\xad\xf0\x0d");
882 struct stat tmp_stat;
883 ASSERT_EQ(stat(tmpfile.c_str(), &tmp_stat), 0);
884
885 // Create perf.data
886 std::stringstream input;
887
888 // header
889 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
890
891 // data
892
893 // PERF_RECORD_HEADER_ATTR
894 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
895 true /*sample_id_all*/)
896 .WriteTo(&input);
897
898 // PERF_RECORD_MMAP2
899 // - Process doesn't exist, but file exists in our own namespace.
900 testing::ExampleMmap2Event(pid, pid, 0x1c1000, 0x1000, 0, tmpfile,
901 testing::SampleInfo().Tid(pid + 10, tid + 1))
902 .WithDeviceInfo(major(tmp_stat.st_dev), minor(tmp_stat.st_dev),
903 tmp_stat.st_ino)
904 .WriteTo(&input); // 0
905
906 // PERF_RECORD_SAMPLE
907 testing::ExamplePerfSampleEvent(
908 testing::SampleInfo().Ip(0x00000000001c1000).Tid(pid))
909 .WriteTo(&input); // 1
910
911 //
912 // Parse input.
913 //
914
915 PerfReader reader;
916 EXPECT_TRUE(reader.ReadFromString(input.str()));
917
918 PerfParserOptions options;
919 options.read_missing_buildids = true;
920 options.sample_mapping_percentage_threshold = 0;
921 PerfParser parser(&reader, options);
922 EXPECT_TRUE(parser.ParseRawEvents());
923
924 EXPECT_EQ(1, parser.stats().num_mmap_events);
925 EXPECT_EQ(1, parser.stats().num_sample_events);
926 EXPECT_EQ(1, parser.stats().num_sample_events_mapped);
927
928 const std::vector<ParsedEvent> &events = parser.parsed_events();
929 ASSERT_EQ(2, events.size());
930
931 EXPECT_EQ(tmpfile, events[1].dso_and_offset.dso_name());
932 // Finds file in root FS.
933 EXPECT_EQ("deadbeef", events[1].dso_and_offset.build_id());
934
935 thread.Join();
936 }
937
938 // Root task <pid>/<pid> Filesystem:
939 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
940 // "/tmp/quipper_mnt.../file_in_namespace" buildid: baadf00d ino: Y
941 // Container task <pid>/<tid> Filesystem:
942 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
943 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: deadbeef ino: X
944 // <path> = marked with *
945 // MMAP2: <pid>/<tid>, <path>, ino: X+1
946 // Reject (wrong inode): /proc/<tid>/root/<path>
947 // Reject (wrong inode): /proc/<pid>/root/<path>
948 // Reject (wrong inode): /<path>
949 // Expected buildid for <path>: ""
TEST(PerfParserTest,ReadsBuildidsInMountNamespace_TriesRootFsRejectsInode)950 TEST(PerfParserTest, ReadsBuildidsInMountNamespace_TriesRootFsRejectsInode) {
951 if (!HaveCapability(CAP_SYS_ADMIN)) return; // Skip test.
952 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
953 ScopedTempDir mntdir("/tmp/quipper_mnt.");
954 RunInMountNamespaceThread thread(tmpdir.path(), mntdir.path());
955 thread.Start();
956 const pid_t pid = getpid();
957 const pid_t tid = thread.tid();
958
959 const string tmpfile = tmpdir.path() + "file_in_namespace";
960 const string tmpfile_in_ns = mntdir.path() + "file_in_namespace";
961 InitializeLibelf();
962 testing::WriteElfWithBuildid(tmpfile, ".note.gnu.build-id",
963 "\xde\xad\xbe\xef");
964 // It's a trap! If "baadf00d" is seen, we read the wrong file.
965 testing::WriteElfWithBuildid(tmpfile_in_ns, ".note.gnu.build-id",
966 "\xba\xad\xf0\x0d");
967 struct stat tmp_stat;
968 struct stat tmp_in_ns_stat;
969 ASSERT_EQ(stat(tmpfile.c_str(), &tmp_stat), 0);
970 ASSERT_EQ(stat(tmpfile_in_ns.c_str(), &tmp_in_ns_stat), 0);
971 // inodes are often issued sequentially, so go backwards rather than forwards.
972 const ino_t bad_ino = tmp_stat.st_ino - 1;
973 ASSERT_NE(bad_ino, tmp_in_ns_stat.st_ino);
974
975 // Create perf.data
976 std::stringstream input;
977
978 // header
979 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
980
981 // data
982
983 // PERF_RECORD_HEADER_ATTR
984 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
985 true /*sample_id_all*/)
986 .WriteTo(&input);
987
988 // PERF_RECORD_MMAP2
989 // - Process doesn't exist, but file exists in our own namespace.
990 testing::ExampleMmap2Event(pid, pid, 0x1c1000, 0x1000, 0, tmpfile_in_ns,
991 testing::SampleInfo().Tid(pid, tid))
992 .WithDeviceInfo(major(tmp_stat.st_dev), minor(tmp_stat.st_dev), bad_ino)
993 .WriteTo(&input); // 0
994
995 // PERF_RECORD_SAMPLE
996 testing::ExamplePerfSampleEvent(
997 testing::SampleInfo().Ip(0x00000000001c1000).Tid(pid))
998 .WriteTo(&input); // 1
999
1000 //
1001 // Parse input.
1002 //
1003
1004 PerfReader reader;
1005 EXPECT_TRUE(reader.ReadFromString(input.str()));
1006
1007 PerfParserOptions options;
1008 options.read_missing_buildids = true;
1009 options.sample_mapping_percentage_threshold = 0;
1010 PerfParser parser(&reader, options);
1011 EXPECT_TRUE(parser.ParseRawEvents());
1012
1013 EXPECT_EQ(1, parser.stats().num_mmap_events);
1014 EXPECT_EQ(1, parser.stats().num_sample_events);
1015 EXPECT_EQ(1, parser.stats().num_sample_events_mapped);
1016
1017 const std::vector<ParsedEvent> &events = parser.parsed_events();
1018 ASSERT_EQ(2, events.size());
1019
1020 EXPECT_EQ(tmpfile_in_ns, events[1].dso_and_offset.dso_name());
1021 // Wrong inode, so rejects all candidates.
1022 EXPECT_EQ("", events[1].dso_and_offset.build_id());
1023
1024 thread.Join();
1025 }
1026
1027 // Root task <pid>/<pid> Filesystem:
1028 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
1029 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: baadf00d ino: Y
1030 // Container task <pid>/<tid> Filesystem:
1031 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
1032 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: deadbeef ino: X
1033 // <path> = marked with *
1034 // MMAP: <pid+10>/<tid+1>, <path>, ino: Not available
1035 // Reject (not found): /proc/<tid+1>/root/<path>
1036 // Reject (not found): /proc/<pid+10>/root/<path>
1037 // Accept (falsely): /<path>
1038 // Expected buildid for <path>: "baadf00d" (even though incorrect)
TEST(PerfParserTest,ReadsBuildidsInMountNamespace_TriesRootFsNoInodeToReject)1039 TEST(PerfParserTest, ReadsBuildidsInMountNamespace_TriesRootFsNoInodeToReject) {
1040 if (!HaveCapability(CAP_SYS_ADMIN)) return; // Skip test.
1041 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
1042 ScopedTempDir mntdir("/tmp/quipper_mnt.");
1043 RunInMountNamespaceThread thread(tmpdir.path(), mntdir.path());
1044 thread.Start();
1045 const pid_t pid = getpid();
1046 const pid_t tid = thread.tid();
1047
1048 const string tmpfile = tmpdir.path() + "file_in_namespace";
1049 const string tmpfile_in_ns = mntdir.path() + "file_in_namespace";
1050 InitializeLibelf();
1051 testing::WriteElfWithBuildid(tmpfile, ".note.gnu.build-id",
1052 "\xde\xad\xf0\x0d");
1053 // It's a trap! If "baadf00d" is seen, we read the wrong file.
1054 testing::WriteElfWithBuildid(tmpfile_in_ns, ".note.gnu.build-id",
1055 "\xba\xad\xf0\x0d");
1056 struct stat tmp_stat;
1057 ASSERT_EQ(stat(tmpfile.c_str(), &tmp_stat), 0);
1058
1059 // Create perf.data
1060 std::stringstream input;
1061
1062 // header
1063 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1064
1065 // data
1066
1067 // PERF_RECORD_HEADER_ATTR
1068 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1069 true /*sample_id_all*/)
1070 .WriteTo(&input);
1071
1072 // PERF_RECORD_MMAP
1073 // - Process & thread don't exist, but file exists in our own namespace.
1074 testing::ExampleMmapEvent(pid, 0x1c1000, 0x1000, 0, tmpfile_in_ns,
1075 testing::SampleInfo().Tid(pid + 10, tid + 1))
1076 .WriteTo(&input); // 0
1077
1078 // PERF_RECORD_SAMPLE
1079 testing::ExamplePerfSampleEvent(
1080 testing::SampleInfo().Ip(0x00000000001c1000).Tid(pid))
1081 .WriteTo(&input); // 1
1082
1083 //
1084 // Parse input.
1085 //
1086
1087 PerfReader reader;
1088 EXPECT_TRUE(reader.ReadFromString(input.str()));
1089
1090 PerfParserOptions options;
1091 options.read_missing_buildids = true;
1092 options.sample_mapping_percentage_threshold = 0;
1093 PerfParser parser(&reader, options);
1094 EXPECT_TRUE(parser.ParseRawEvents());
1095
1096 EXPECT_EQ(1, parser.stats().num_mmap_events);
1097 EXPECT_EQ(1, parser.stats().num_sample_events);
1098 EXPECT_EQ(1, parser.stats().num_sample_events_mapped);
1099
1100 const std::vector<ParsedEvent> &events = parser.parsed_events();
1101 ASSERT_EQ(2, events.size());
1102
1103 EXPECT_EQ(tmpfile_in_ns, events[1].dso_and_offset.dso_name());
1104 // We'll read the wrong file b/c we couldn't reject based on inode:
1105 EXPECT_EQ("baadf00d", events[1].dso_and_offset.build_id());
1106
1107 thread.Join();
1108 }
1109
1110 // Root task <pid>/<pid> Filesystem:
1111 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
1112 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: baadf00d ino: Y
1113 // Container task <pid>/<tid> Filesystem:
1114 // "/tmp/quipper_tmp.../file_in_namespace" buildid: deadbeef ino: X
1115 // * "/tmp/quipper_mnt.../file_in_namespace" buildid: deadbeef ino: X
1116 // <path> = marked with *
1117 // MMAP2(0): <pid>/<tid>, <path>, <maj+1>/<min>, ino: X
1118 // MMAP2(1): <pid>/<tid>, <path>, <maj>/<min+1>, ino: X
1119 // MMAP2(2): <pid>/<tid>, <path>, <maj>/<min>, ino: X+1
1120 // SAMPLE(3): <pid>/<tid>, addr in MMAP2(0)
1121 // SAMPLE(4): <pid>/<tid>, addr in MMAP2(1)
1122 // SAMPLE(5): <pid>/<tid>, addr in MMAP2(2)
1123 // Expected buildid for <path>: ""
1124 //
1125 // with multiple device/ino numbers. This is really a shortcoming of perf--
1126 // it can only associate a buildid with a path. If the same path exists in
1127 // multiple containers but refers to different files (device/inode), then
1128 // it's hard to know what to do. Similarly, PerfParser associates a DSO name
1129 // (path) with a single DSOInfo and device/inode info therein, although it
1130 // tracks all threads the DSO name was seen in. This test is set up such that
1131 // all MMAPs should be rejected, but in truth PerfParser only compares the
1132 // device/inode of the file against the device/inode of one of the MMAPs.
1133 // A better thing to do might be to track a
1134 // map<tuple<maj,min,ino,path>, DSOInfo>, but even so, it will be impossible
1135 // to store unambiguously in perf.data.
TEST(PerfParserTest,ReadsBuildidsInMountNamespace_DifferentDevOrIno)1136 TEST(PerfParserTest, ReadsBuildidsInMountNamespace_DifferentDevOrIno) {
1137 if (!HaveCapability(CAP_SYS_ADMIN)) return; // Skip test.
1138 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
1139 ScopedTempDir mntdir("/tmp/quipper_mnt.");
1140 RunInMountNamespaceThread thread(tmpdir.path(), mntdir.path());
1141 thread.Start();
1142 const pid_t pid = getpid();
1143 const pid_t tid = thread.tid();
1144
1145 const string tmpfile = tmpdir.path() + "file_in_namespace";
1146 const string tmpfile_in_ns = mntdir.path() + "file_in_namespace";
1147 InitializeLibelf();
1148 testing::WriteElfWithBuildid(tmpfile, ".note.gnu.build-id",
1149 "\xde\xad\xf0\x0d");
1150 // It's a trap! If "baadf00d" is seen, we read the wrong file.
1151 testing::WriteElfWithBuildid(tmpfile_in_ns, ".note.gnu.build-id",
1152 "\xba\xad\xf0\x0d");
1153 struct stat tmp_stat;
1154 struct stat tmp_in_ns_stat;
1155 ASSERT_EQ(stat(tmpfile.c_str(), &tmp_stat), 0);
1156 ASSERT_EQ(stat(tmpfile_in_ns.c_str(), &tmp_in_ns_stat), 0);
1157 // inodes are often issued sequentially, so go backwards rather than forwards.
1158 const ino_t bad_ino = tmp_stat.st_ino - 1;
1159 ASSERT_NE(bad_ino, tmp_in_ns_stat.st_ino);
1160
1161 // Create perf.data
1162 std::stringstream input;
1163
1164 // header
1165 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1166
1167 // data
1168
1169 // PERF_RECORD_HEADER_ATTR
1170 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1171 true /*sample_id_all*/)
1172 .WriteTo(&input);
1173
1174 // PERF_RECORD_MMAP2
1175 // - Wrong major number
1176 testing::ExampleMmap2Event(pid, tid, 0x1c1000, 0x1000, 0, tmpfile_in_ns,
1177 testing::SampleInfo().Tid(pid, tid))
1178 .WithDeviceInfo(major(tmp_stat.st_dev) + 1, minor(tmp_stat.st_dev),
1179 tmp_stat.st_ino)
1180 .WriteTo(&input); // 0
1181 // - Wrong minor number
1182 testing::ExampleMmap2Event(pid, tid, 0x1c2000, 0x1000, 0, tmpfile_in_ns,
1183 testing::SampleInfo().Tid(pid, tid))
1184 .WithDeviceInfo(major(tmp_stat.st_dev), minor(tmp_stat.st_dev) + 1,
1185 tmp_stat.st_ino)
1186 .WriteTo(&input); // 1
1187 // - Wrong inode number
1188 testing::ExampleMmap2Event(pid, tid, 0x1c3000, 0x1000, 0, tmpfile_in_ns,
1189 testing::SampleInfo().Tid(pid, tid))
1190 .WithDeviceInfo(major(tmp_stat.st_dev), minor(tmp_stat.st_dev),
1191 bad_ino)
1192 .WriteTo(&input); // 2
1193
1194 // PERF_RECORD_SAMPLE
1195 testing::ExamplePerfSampleEvent(
1196 testing::SampleInfo().Ip(0x00000000001c1000).Tid(pid, tid))
1197 .WriteTo(&input); // 3
1198 testing::ExamplePerfSampleEvent(
1199 testing::SampleInfo().Ip(0x00000000001c2000).Tid(pid, tid))
1200 .WriteTo(&input); // 4
1201 testing::ExamplePerfSampleEvent(
1202 testing::SampleInfo().Ip(0x00000000001c3000).Tid(pid, tid))
1203 .WriteTo(&input); // 5
1204
1205 //
1206 // Parse input.
1207 //
1208
1209 PerfReader reader;
1210 EXPECT_TRUE(reader.ReadFromString(input.str()));
1211
1212 PerfParserOptions options;
1213 options.read_missing_buildids = true;
1214 options.sample_mapping_percentage_threshold = 0;
1215 PerfParser parser(&reader, options);
1216 EXPECT_TRUE(parser.ParseRawEvents());
1217
1218 EXPECT_EQ(3, parser.stats().num_mmap_events);
1219 EXPECT_EQ(3, parser.stats().num_sample_events);
1220 EXPECT_EQ(3, parser.stats().num_sample_events_mapped);
1221
1222 const std::vector<ParsedEvent> &events = parser.parsed_events();
1223 ASSERT_EQ(6, events.size());
1224
1225 // Buildid should not be found for any of the samples.
1226 for (int i : {3, 4, 5}) {
1227 EXPECT_EQ(tmpfile_in_ns, events[i].dso_and_offset.dso_name());
1228 EXPECT_EQ("", events[i].dso_and_offset.build_id());
1229 }
1230
1231 thread.Join();
1232 }
1233
TEST(PerfParserTest,OverwriteBuildidIfAlreadyKnown)1234 TEST(PerfParserTest, OverwriteBuildidIfAlreadyKnown) {
1235 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
1236 const string known_file = tmpdir.path() + "buildid_already_known";
1237 const string known_file_to_overwrite =
1238 tmpdir.path() + "buildid_already_known_overwrite";
1239 const string unknown_file = tmpdir.path() + "buildid_not_known";
1240 InitializeLibelf();
1241 testing::WriteElfWithBuildid(known_file_to_overwrite, ".note.gnu.build-id",
1242 "\xf0\x01\x57\xea");
1243 testing::WriteElfWithBuildid(unknown_file, ".note.gnu.build-id",
1244 "\xc0\x01\xd0\x0d");
1245
1246 std::stringstream input;
1247
1248 // header
1249 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1250
1251 // data
1252
1253 // PERF_RECORD_HEADER_ATTR
1254 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1255 true /*sample_id_all*/)
1256 .WriteTo(&input);
1257
1258 // PERF_RECORD_MMAP
1259 testing::ExampleMmapEvent(1001, 0x1c1000, 0x1000, 0, known_file,
1260 testing::SampleInfo().Tid(1001))
1261 .WriteTo(&input); // 0
1262 // becomes: 0x0000, 0x1000, 0
1263 testing::ExampleMmapEvent(1001, 0x1c2000, 0x2000, 0, known_file_to_overwrite,
1264 testing::SampleInfo().Tid(1001))
1265 .WriteTo(&input); // 1
1266 // becomes: 0x1000, 0x2000, 0
1267 testing::ExampleMmapEvent(1001, 0x1c4000, 0x2000, 0x2000, unknown_file,
1268 testing::SampleInfo().Tid(1001))
1269 .WriteTo(&input); // 2
1270 // becomes: 0x3000, 0x2000, 0x2000
1271
1272 // PERF_RECORD_HEADER_BUILDID // N/A
1273 {
1274 string build_id_filename(known_file);
1275 build_id_filename.resize(Align<u64>(known_file.size())); // null-pad
1276 const size_t event_size =
1277 sizeof(struct build_id_event) + build_id_filename.size();
1278 const struct build_id_event event = {
1279 .header =
1280 {
1281 .type = PERF_RECORD_HEADER_BUILD_ID,
1282 .misc = 0,
1283 .size = static_cast<u16>(event_size),
1284 },
1285 .pid = -1,
1286 .build_id = {0xde, 0xad, 0xbe, 0xef},
1287 };
1288 input.write(reinterpret_cast<const char *>(&event), sizeof(event));
1289 input.write(build_id_filename.data(), build_id_filename.size());
1290 }
1291
1292 // PERF_RECORD_HEADER_BUILDID // N/A
1293 {
1294 string build_id_filename(known_file_to_overwrite);
1295 // null-pad
1296 build_id_filename.resize(Align<u64>(known_file_to_overwrite.size()));
1297 const size_t event_size =
1298 sizeof(struct build_id_event) + build_id_filename.size();
1299 const struct build_id_event event = {
1300 .header =
1301 {
1302 .type = PERF_RECORD_HEADER_BUILD_ID,
1303 .misc = 0,
1304 .size = static_cast<u16>(event_size),
1305 },
1306 .pid = -1,
1307 .build_id = {0xca, 0xfe, 0xba, 0xbe},
1308 };
1309 input.write(reinterpret_cast<const char *>(&event), sizeof(event));
1310 input.write(build_id_filename.data(), build_id_filename.size());
1311 }
1312
1313 // PERF_RECORD_SAMPLE
1314 testing::ExamplePerfSampleEvent(
1315 testing::SampleInfo().Ip(0x00000000001c100a).Tid(1001))
1316 .WriteTo(&input); // 3
1317 testing::ExamplePerfSampleEvent(
1318 testing::SampleInfo().Ip(0x00000000001c300b).Tid(1001))
1319 .WriteTo(&input); // 4
1320 testing::ExamplePerfSampleEvent(
1321 testing::SampleInfo().Ip(0x00000000001c400c).Tid(1001))
1322 .WriteTo(&input); // 5
1323
1324 //
1325 // Parse input.
1326 //
1327
1328 PerfReader reader;
1329 EXPECT_TRUE(reader.ReadFromString(input.str()));
1330
1331 PerfParserOptions options;
1332 options.read_missing_buildids = true;
1333 options.sample_mapping_percentage_threshold = 0;
1334 PerfParser parser(&reader, options);
1335 EXPECT_TRUE(parser.ParseRawEvents());
1336
1337 EXPECT_EQ(3, parser.stats().num_mmap_events);
1338 EXPECT_EQ(3, parser.stats().num_sample_events);
1339 EXPECT_EQ(3, parser.stats().num_sample_events_mapped);
1340
1341 const std::vector<ParsedEvent> &events = parser.parsed_events();
1342 ASSERT_EQ(6, events.size());
1343
1344 EXPECT_EQ(known_file, events[3].dso_and_offset.dso_name());
1345 EXPECT_EQ("deadbeef00000000000000000000000000000000",
1346 events[3].dso_and_offset.build_id());
1347 EXPECT_EQ(known_file_to_overwrite, events[4].dso_and_offset.dso_name());
1348 EXPECT_EQ("f00157ea", events[4].dso_and_offset.build_id());
1349 EXPECT_EQ(unknown_file, events[5].dso_and_offset.dso_name());
1350 EXPECT_EQ("c001d00d", events[5].dso_and_offset.build_id());
1351 }
1352
TEST(PerfParserTest,OnlyReadsBuildidIfSampled)1353 TEST(PerfParserTest, OnlyReadsBuildidIfSampled) {
1354 ScopedTempDir tmpdir("/tmp/quipper_tmp.");
1355 const string unknown_file = tmpdir.path() + "buildid_not_known";
1356 InitializeLibelf();
1357 testing::WriteElfWithBuildid(unknown_file, ".note.gnu.build-id",
1358 "\xc0\x01\xd0\x0d");
1359
1360 std::stringstream input;
1361
1362 // header
1363 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1364
1365 // data
1366
1367 // PERF_RECORD_HEADER_ATTR
1368 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1369 true /*sample_id_all*/)
1370 .WriteTo(&input);
1371
1372 // PERF_RECORD_MMAP
1373 testing::ExampleMmapEvent(1001, 0x1c1000, 0x1000, 0, unknown_file,
1374 testing::SampleInfo().Tid(1001))
1375 .WriteTo(&input); // 0
1376 // becomes: 0x0000, 0x1000, 0
1377
1378 // PERF_RECORD_SAMPLE
1379 // - Sample outside mmap
1380 testing::ExamplePerfSampleEvent(
1381 testing::SampleInfo().Ip(0x00000000001c300a).Tid(1001))
1382 .WriteTo(&input); // 1
1383
1384 //
1385 // Parse input.
1386 //
1387
1388 PerfReader reader;
1389 EXPECT_TRUE(reader.ReadFromString(input.str()));
1390
1391 PerfParserOptions options;
1392 options.read_missing_buildids = true;
1393 options.sample_mapping_percentage_threshold = 0;
1394 PerfParser parser(&reader, options);
1395 EXPECT_TRUE(parser.ParseRawEvents());
1396
1397 EXPECT_EQ(1, parser.stats().num_mmap_events);
1398 EXPECT_EQ(1, parser.stats().num_sample_events);
1399 EXPECT_EQ(0, parser.stats().num_sample_events_mapped);
1400
1401 const std::vector<ParsedEvent> &events = parser.parsed_events();
1402 ASSERT_EQ(2, events.size());
1403
1404 EXPECT_EQ("", events[1].dso_and_offset.dso_name());
1405 EXPECT_EQ("", events[1].dso_and_offset.build_id());
1406
1407 std::map<string, string> filenames_to_build_ids;
1408 reader.GetFilenamesToBuildIDs(&filenames_to_build_ids);
1409 auto it = filenames_to_build_ids.find(unknown_file);
1410 EXPECT_EQ(filenames_to_build_ids.end(), it) << it->first << " " << it->second;
1411 }
1412
TEST(PerfParserTest,HandlesFinishedRoundEventsAndSortsByTime)1413 TEST(PerfParserTest, HandlesFinishedRoundEventsAndSortsByTime) {
1414 // For now at least, we are ignoring PERF_RECORD_FINISHED_ROUND events.
1415
1416 std::stringstream input;
1417
1418 // header
1419 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1420
1421 // data
1422
1423 // PERF_RECORD_HEADER_ATTR
1424 testing::ExamplePerfEventAttrEvent_Hardware(
1425 PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME,
1426 true /*sample_id_all*/)
1427 .WriteTo(&input);
1428
1429 // PERF_RECORD_MMAP
1430 testing::ExampleMmapEvent(1001, 0x1c1000, 0x1000, 0, "/usr/lib/foo.so",
1431 testing::SampleInfo().Tid(1001).Time(12300010))
1432 .WriteTo(&input);
1433 // becomes: 0x0000, 0x1000, 0
1434
1435 // PERF_RECORD_SAMPLE
1436 testing::ExamplePerfSampleEvent(
1437 testing::SampleInfo().Ip(0x00000000001c1000).Tid(1001).Time(12300020))
1438 .WriteTo(&input); // 1
1439 testing::ExamplePerfSampleEvent(
1440 testing::SampleInfo().Ip(0x00000000001c1000).Tid(1001).Time(12300030))
1441 .WriteTo(&input); // 2
1442 // PERF_RECORD_FINISHED_ROUND
1443 testing::FinishedRoundEvent().WriteTo(&input); // N/A
1444
1445 // PERF_RECORD_SAMPLE
1446 testing::ExamplePerfSampleEvent(
1447 testing::SampleInfo().Ip(0x00000000001c1000).Tid(1001).Time(12300050))
1448 .WriteTo(&input); // 3
1449 testing::ExamplePerfSampleEvent(
1450 testing::SampleInfo().Ip(0x00000000001c1000).Tid(1001).Time(12300040))
1451 .WriteTo(&input); // 4
1452 // PERF_RECORD_FINISHED_ROUND
1453 testing::FinishedRoundEvent().WriteTo(&input); // N/A
1454
1455 //
1456 // Parse input.
1457 //
1458
1459 PerfReader reader;
1460 EXPECT_TRUE(reader.ReadFromString(input.str()));
1461
1462 PerfParserOptions options;
1463 options.sample_mapping_percentage_threshold = 0;
1464 options.sort_events_by_time = true;
1465 PerfParser parser(&reader, options);
1466 EXPECT_TRUE(parser.ParseRawEvents());
1467
1468 EXPECT_EQ(1, parser.stats().num_mmap_events);
1469 EXPECT_EQ(4, parser.stats().num_sample_events);
1470 EXPECT_EQ(4, parser.stats().num_sample_events_mapped);
1471
1472 const std::vector<ParsedEvent> &events = parser.parsed_events();
1473 ASSERT_EQ(5, events.size());
1474
1475 EXPECT_EQ(PERF_RECORD_MMAP, events[0].event_ptr->header().type());
1476 EXPECT_EQ(PERF_RECORD_SAMPLE, events[1].event_ptr->header().type());
1477 EXPECT_EQ(12300020, events[1].event_ptr->sample_event().sample_time_ns());
1478 EXPECT_EQ(PERF_RECORD_SAMPLE, events[2].event_ptr->header().type());
1479 EXPECT_EQ(12300030, events[2].event_ptr->sample_event().sample_time_ns());
1480 EXPECT_EQ(PERF_RECORD_SAMPLE, events[3].event_ptr->header().type());
1481 EXPECT_EQ(12300040, events[3].event_ptr->sample_event().sample_time_ns());
1482 EXPECT_EQ(PERF_RECORD_SAMPLE, events[4].event_ptr->header().type());
1483 EXPECT_EQ(12300050, events[4].event_ptr->sample_event().sample_time_ns());
1484 }
1485
TEST(PerfParserTest,MmapCoversEntireAddressSpace)1486 TEST(PerfParserTest, MmapCoversEntireAddressSpace) {
1487 std::stringstream input;
1488
1489 // header
1490 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1491
1492 // PERF_RECORD_HEADER_ATTR
1493 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1494 true /*sample_id_all*/)
1495 .WriteTo(&input);
1496
1497 // PERF_RECORD_MMAP, a kernel mapping that covers the whole space.
1498 const uint32_t kKernelMmapPid = UINT32_MAX;
1499 testing::ExampleMmapEvent(kKernelMmapPid, 0, UINT64_MAX, 0,
1500 "[kernel.kallsyms]_text",
1501 testing::SampleInfo().Tid(kKernelMmapPid, 0))
1502 .WriteTo(&input);
1503
1504 // PERF_RECORD_MMAP, a shared object library.
1505 testing::ExampleMmapEvent(1234, 0x7f008e000000, 0x2000000, 0,
1506 "/usr/lib/libfoo.so",
1507 testing::SampleInfo().Tid(1234, 1234))
1508 .WriteTo(&input);
1509
1510 // PERF_RECORD_SAMPLE, within library.
1511 testing::ExamplePerfSampleEvent(
1512 testing::SampleInfo().Ip(0x7f008e123456).Tid(1234, 1235))
1513 .WriteTo(&input);
1514 // PERF_RECORD_SAMPLE, within kernel.
1515 testing::ExamplePerfSampleEvent(
1516 testing::SampleInfo().Ip(0x8000819e).Tid(1234, 1235))
1517 .WriteTo(&input);
1518 // PERF_RECORD_SAMPLE, within library.
1519 testing::ExamplePerfSampleEvent(
1520 testing::SampleInfo().Ip(0x7f008fdeadbe).Tid(1234, 1235))
1521 .WriteTo(&input);
1522 // PERF_RECORD_SAMPLE, within kernel.
1523 testing::ExamplePerfSampleEvent(
1524 testing::SampleInfo().Ip(0xffffffff8100cafe).Tid(1234, 1235))
1525 .WriteTo(&input);
1526
1527 //
1528 // Parse input.
1529 //
1530
1531 PerfReader reader;
1532 EXPECT_TRUE(reader.ReadFromString(input.str()));
1533
1534 PerfParserOptions options;
1535 options.do_remap = true;
1536 PerfParser parser(&reader, options);
1537 EXPECT_TRUE(parser.ParseRawEvents());
1538
1539 EXPECT_EQ(2, parser.stats().num_mmap_events);
1540 EXPECT_EQ(4, parser.stats().num_sample_events);
1541 EXPECT_EQ(4, parser.stats().num_sample_events_mapped);
1542
1543 const std::vector<ParsedEvent> &events = parser.parsed_events();
1544 ASSERT_EQ(6, events.size());
1545
1546 EXPECT_EQ(PERF_RECORD_MMAP, events[0].event_ptr->header().type());
1547 EXPECT_EQ("[kernel.kallsyms]_text",
1548 events[0].event_ptr->mmap_event().filename());
1549 EXPECT_EQ(PERF_RECORD_MMAP, events[1].event_ptr->header().type());
1550 EXPECT_EQ("/usr/lib/libfoo.so", events[1].event_ptr->mmap_event().filename());
1551
1552 // Sample from library.
1553 EXPECT_EQ(PERF_RECORD_SAMPLE, events[2].event_ptr->header().type());
1554 EXPECT_EQ("/usr/lib/libfoo.so", events[2].dso_and_offset.dso_name());
1555 EXPECT_EQ(0x123456, events[2].dso_and_offset.offset());
1556
1557 // Sample from kernel.
1558 EXPECT_EQ(PERF_RECORD_SAMPLE, events[3].event_ptr->header().type());
1559 EXPECT_EQ("[kernel.kallsyms]_text", events[3].dso_and_offset.dso_name());
1560 EXPECT_EQ(0x8000819e, events[3].dso_and_offset.offset());
1561
1562 // Sample from library.
1563 EXPECT_EQ(PERF_RECORD_SAMPLE, events[4].event_ptr->header().type());
1564 EXPECT_EQ("/usr/lib/libfoo.so", events[4].dso_and_offset.dso_name());
1565 EXPECT_EQ(0x1deadbe, events[4].dso_and_offset.offset());
1566
1567 // Sample from kernel.
1568 EXPECT_EQ(PERF_RECORD_SAMPLE, events[5].event_ptr->header().type());
1569 EXPECT_EQ("[kernel.kallsyms]_text", events[5].dso_and_offset.dso_name());
1570 EXPECT_EQ(0xffffffff8100cafe, events[5].dso_and_offset.offset());
1571 }
1572
TEST(PerfParserTest,HugePagesMappings)1573 TEST(PerfParserTest, HugePagesMappings) {
1574 std::stringstream input;
1575
1576 // header
1577 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1578
1579 // PERF_RECORD_HEADER_ATTR
1580 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1581 true /*sample_id_all*/)
1582 .WriteTo(&input);
1583
1584 // PERF_RECORD_MMAP, a normal mapping.
1585 testing::ExampleMmapEvent(1234, 0x40000000, 0x18000, 0, "/usr/lib/libfoo.so",
1586 testing::SampleInfo().Tid(1234, 1234))
1587 .WriteTo(&input);
1588
1589 // PERF_RECORD_SAMPLE, within library.
1590 testing::ExamplePerfSampleEvent(
1591 testing::SampleInfo().Ip(0x40000100).Tid(1234, 1235))
1592 .WriteTo(&input);
1593
1594 // Split Chrome mapping #1, with a huge pages section in the middle.
1595 testing::ExampleMmapEvent(1234, 0x40018000, 0x1e8000, 0,
1596 "/opt/google/chrome/chrome",
1597 testing::SampleInfo().Tid(1234, 1234))
1598 .WriteTo(&input);
1599 testing::ExampleMmapEvent(1234, 0x40200000, 0x1c00000, 0, "//anon",
1600 testing::SampleInfo().Tid(1234, 1234))
1601 .WriteTo(&input);
1602 testing::ExampleMmapEvent(1234, 0x41e00000, 0x4000000, 0x1de8000,
1603 "/opt/google/chrome/chrome",
1604 testing::SampleInfo().Tid(1234, 1234))
1605 .WriteTo(&input);
1606
1607 // Split Chrome mapping #2, starting with a huge pages section (no preceding
1608 // normal mapping).
1609 testing::ExampleMmapEvent(2345, 0x45e00000, 0x1e00000, 0, "//anon",
1610 testing::SampleInfo().Tid(2345, 2346))
1611 .WriteTo(&input);
1612 testing::ExampleMmapEvent(2345, 0x47c00000, 0x4000000, 0x1e00000,
1613 "/opt/google/chrome/chrome",
1614 testing::SampleInfo().Tid(2345, 2346))
1615 .WriteTo(&input);
1616
1617 // PERF_RECORD_SAMPLE, within Chrome #1 (before huge pages mapping).
1618 testing::ExamplePerfSampleEvent(
1619 testing::SampleInfo().Ip(0x40018300).Tid(1234, 1235))
1620 .WriteTo(&input);
1621 // PERF_RECORD_SAMPLE, within Chrome #1 (within huge pages mapping).
1622 testing::ExamplePerfSampleEvent(
1623 testing::SampleInfo().Ip(0x40020400).Tid(1234, 1235))
1624 .WriteTo(&input);
1625 // PERF_RECORD_SAMPLE, within Chrome #1 (after huge pages mapping).
1626 testing::ExamplePerfSampleEvent(
1627 testing::SampleInfo().Ip(0x41e20500).Tid(1234, 1235))
1628 .WriteTo(&input);
1629
1630 // PERF_RECORD_SAMPLE, within library.
1631 testing::ExamplePerfSampleEvent(
1632 testing::SampleInfo().Ip(0x40000700).Tid(1234, 1235))
1633 .WriteTo(&input);
1634
1635 // PERF_RECORD_SAMPLE, within Chrome #2 (within huge pages mapping).
1636 testing::ExamplePerfSampleEvent(
1637 testing::SampleInfo().Ip(0x45e01300).Tid(2345, 2346))
1638 .WriteTo(&input);
1639 // PERF_RECORD_SAMPLE, within Chrome #2 (after huge pages mapping).
1640 testing::ExamplePerfSampleEvent(
1641 testing::SampleInfo().Ip(0x45e02f00).Tid(2345, 2346))
1642 .WriteTo(&input);
1643
1644 //
1645 // Parse input.
1646 //
1647
1648 PerfReader reader;
1649 EXPECT_TRUE(reader.ReadFromString(input.str()));
1650
1651 PerfParserOptions options;
1652 options.deduce_huge_page_mappings = true;
1653 options.combine_mappings = true;
1654 PerfParser parser(&reader, options);
1655 EXPECT_TRUE(parser.ParseRawEvents());
1656
1657 EXPECT_EQ(3, parser.stats().num_mmap_events);
1658 EXPECT_EQ(7, parser.stats().num_sample_events);
1659 EXPECT_EQ(7, parser.stats().num_sample_events_mapped);
1660
1661 const std::vector<ParsedEvent> &events = parser.parsed_events();
1662 ASSERT_EQ(10, events.size());
1663
1664 EXPECT_EQ(PERF_RECORD_MMAP, events[0].event_ptr->header().type());
1665 EXPECT_EQ("/usr/lib/libfoo.so", events[0].event_ptr->mmap_event().filename());
1666 EXPECT_EQ(0x40000000, events[0].event_ptr->mmap_event().start());
1667 EXPECT_EQ(0x18000, events[0].event_ptr->mmap_event().len());
1668 EXPECT_EQ(0x0, events[0].event_ptr->mmap_event().pgoff());
1669
1670 // Sample from library.
1671 EXPECT_EQ(PERF_RECORD_SAMPLE, events[1].event_ptr->header().type());
1672 EXPECT_EQ("/usr/lib/libfoo.so", events[1].dso_and_offset.dso_name());
1673 EXPECT_EQ(0x100, events[1].dso_and_offset.offset());
1674
1675 // The split Chrome mappings should have been combined.
1676 EXPECT_EQ(PERF_RECORD_MMAP, events[2].event_ptr->header().type());
1677 EXPECT_EQ("/opt/google/chrome/chrome",
1678 events[2].event_ptr->mmap_event().filename());
1679 EXPECT_EQ(0x40018000, events[2].event_ptr->mmap_event().start());
1680 EXPECT_EQ(0x5de8000, events[2].event_ptr->mmap_event().len());
1681 EXPECT_EQ(0x0, events[2].event_ptr->mmap_event().pgoff());
1682
1683 EXPECT_EQ(PERF_RECORD_MMAP, events[3].event_ptr->header().type());
1684 EXPECT_EQ("/opt/google/chrome/chrome",
1685 events[3].event_ptr->mmap_event().filename());
1686 EXPECT_EQ(0x45e00000, events[3].event_ptr->mmap_event().start());
1687 EXPECT_EQ(0x5e00000, events[3].event_ptr->mmap_event().len());
1688 EXPECT_EQ(0x0, events[3].event_ptr->mmap_event().pgoff());
1689
1690 // Sample from Chrome (before huge pages mapping).
1691 EXPECT_EQ(PERF_RECORD_SAMPLE, events[4].event_ptr->header().type());
1692 EXPECT_EQ("/opt/google/chrome/chrome", events[4].dso_and_offset.dso_name());
1693 EXPECT_EQ(0x300, events[4].dso_and_offset.offset());
1694
1695 // Sample from Chrome (within huge pages mapping).
1696 EXPECT_EQ(PERF_RECORD_SAMPLE, events[5].event_ptr->header().type());
1697 EXPECT_EQ("/opt/google/chrome/chrome", events[5].dso_and_offset.dso_name());
1698 EXPECT_EQ(0x8400, events[5].dso_and_offset.offset());
1699
1700 // Sample from Chrome (after huge pages mapping).
1701 EXPECT_EQ(PERF_RECORD_SAMPLE, events[6].event_ptr->header().type());
1702 EXPECT_EQ("/opt/google/chrome/chrome", events[6].dso_and_offset.dso_name());
1703 EXPECT_EQ(0x1e08500, events[6].dso_and_offset.offset());
1704
1705 // Sample from library.
1706 EXPECT_EQ(PERF_RECORD_SAMPLE, events[7].event_ptr->header().type());
1707 EXPECT_EQ("/usr/lib/libfoo.so", events[7].dso_and_offset.dso_name());
1708 EXPECT_EQ(0x700, events[7].dso_and_offset.offset());
1709
1710 // Sample from Chrome #2 (within huge pages mapping).
1711 EXPECT_EQ(PERF_RECORD_SAMPLE, events[8].event_ptr->header().type());
1712 EXPECT_EQ("/opt/google/chrome/chrome", events[8].dso_and_offset.dso_name());
1713 EXPECT_EQ(0x1300, events[8].dso_and_offset.offset());
1714
1715 // Sample from Chrome #2 (after huge pages mapping).
1716 EXPECT_EQ(PERF_RECORD_SAMPLE, events[9].event_ptr->header().type());
1717 EXPECT_EQ("/opt/google/chrome/chrome", events[9].dso_and_offset.dso_name());
1718 EXPECT_EQ(0x2f00, events[9].dso_and_offset.offset());
1719 }
1720
TEST(PerfParserTest,Regression62446346)1721 TEST(PerfParserTest, Regression62446346) {
1722 std::stringstream input;
1723
1724 // header
1725 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1726
1727 // PERF_RECORD_HEADER_ATTR
1728 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1729 true /*sample_id_all*/)
1730 .WriteTo(&input);
1731
1732 // Perf infers the filename is "file", but at offset 0 for
1733 // hugepage-backed, anonymous mappings.
1734 //
1735 // vaddr start - vaddr end vaddr-size elf-offset
1736 // [0x55a685bfb000-55a685dfb000) (0x200000) @ 0]: file
1737 // [0x55a685dfb000-55a687c00000) (0x1e05000) @ 0x200000]: file
1738 // [0x55a687c00000-55a6a5200000) (0x1d600000) @ 0]: file
1739 // [0x55a6a5200000-55a6a6400000) (0x1200000) @ 0x1f605000]: file
1740 // [0x55a6a6400000-55a6a6600000) (0x200000) @ 0]: file
1741 // [0x55a6a6600000-55a6a8800000) (0x2200000) @ 0x20a05000]: file
1742 // [0x55a6a8800000-55a6a8a00000) (0x200000) @ 0]: file
1743 // [0x55a6a8a00000-55a6a90ca000) (0x6ca000) @ 0x22e05000]: file
1744 // [0x55a6a90ca000-55a6a90cb000) (0x1000) @ 0x234cf000]: file
1745 testing::ExampleMmapEvent(1234, 0x55a685bfb000, 0x200000, 0, "file",
1746 testing::SampleInfo().Tid(1234, 1234))
1747 .WriteTo(&input);
1748 testing::ExampleMmapEvent(1234, 0x55a685dfb000, 0x1e05000, 0x200000, "file",
1749 testing::SampleInfo().Tid(1234, 1234))
1750 .WriteTo(&input);
1751 testing::ExampleMmapEvent(1234, 0x55a687c00000, 0x1d600000, 0, "file",
1752 testing::SampleInfo().Tid(1234, 1234))
1753 .WriteTo(&input);
1754 testing::ExampleMmapEvent(1234, 0x55a6a5200000, 0x1200000, 0x1f605000, "file",
1755 testing::SampleInfo().Tid(1234, 1234))
1756 .WriteTo(&input);
1757 testing::ExampleMmapEvent(1234, 0x55a6a6400000, 0x200000, 0, "file",
1758 testing::SampleInfo().Tid(1234, 1234))
1759 .WriteTo(&input);
1760 testing::ExampleMmapEvent(1234, 0x55a6a6600000, 0x2200000, 0x20a05000, "file",
1761 testing::SampleInfo().Tid(1234, 1234))
1762 .WriteTo(&input);
1763 testing::ExampleMmapEvent(1234, 0x55a6a8800000, 0x200000, 0, "file",
1764 testing::SampleInfo().Tid(1234, 1234))
1765 .WriteTo(&input);
1766 testing::ExampleMmapEvent(1234, 0x55a6a8a00000, 0x6ca000, 0x22e05000, "file",
1767 testing::SampleInfo().Tid(1234, 1234))
1768 .WriteTo(&input);
1769 testing::ExampleMmapEvent(1234, 0x55a6a90ca000, 0x1000, 0x234cf000, "file",
1770 testing::SampleInfo().Tid(1234, 1234))
1771 .WriteTo(&input);
1772
1773 //
1774 // Parse input.
1775 //
1776
1777 PerfReader reader;
1778 EXPECT_TRUE(reader.ReadFromString(input.str()));
1779
1780 PerfParserOptions options;
1781 options.deduce_huge_page_mappings = true;
1782 options.combine_mappings = true;
1783 PerfParser parser(&reader, options);
1784 EXPECT_TRUE(parser.ParseRawEvents());
1785
1786 EXPECT_EQ(1, parser.stats().num_mmap_events);
1787 EXPECT_EQ(0, parser.stats().num_sample_events);
1788 EXPECT_EQ(0, parser.stats().num_sample_events_mapped);
1789
1790 const std::vector<ParsedEvent> &events = parser.parsed_events();
1791
1792 {
1793 PerfDataProto expected;
1794 {
1795 auto *ev = expected.add_events();
1796 ev->mutable_header()->set_type(PERF_RECORD_MMAP);
1797 ev->mutable_mmap_event()->set_filename("file");
1798 ev->mutable_mmap_event()->set_start(0x55a685bfb000);
1799 ev->mutable_mmap_event()->set_len(0x234d0000);
1800 ev->mutable_mmap_event()->set_pgoff(0x0);
1801 }
1802
1803 PerfDataProto actual;
1804 for (const auto &ev : events) {
1805 if (ev.event_ptr == nullptr) {
1806 continue;
1807 }
1808
1809 *actual.add_events() = *ev.event_ptr;
1810 }
1811
1812 EXPECT_TRUE(PartiallyEqualsProto(actual, expected));
1813 }
1814 ASSERT_EQ(1, events.size());
1815
1816 // Verify the header().size() entry is large enough to deserialize/serialize.
1817 for (const auto &ev : events) {
1818 malloced_unique_ptr<event_t> e(
1819 CallocMemoryForEvent(ev.event_ptr->header().size()));
1820
1821 PerfSerializer serializer;
1822 ASSERT_TRUE(
1823 serializer.DeserializeMMapEvent(ev.event_ptr->mmap_event(), e.get()));
1824
1825 PerfDataProto_MMapEvent roundtrip;
1826 ASSERT_TRUE(serializer.SerializeMMapEvent(*e, &roundtrip));
1827 // sample_info does not roundtrip through an event_t.
1828 EXPECT_TRUE(PartiallyEqualsProto(ev.event_ptr->mmap_event(), roundtrip));
1829 }
1830 }
1831
TEST(PerfParserTest,Regression62446346_Perf3_12_0_11)1832 TEST(PerfParserTest, Regression62446346_Perf3_12_0_11) {
1833 std::stringstream input;
1834
1835 // header
1836 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1837
1838 // PERF_RECORD_HEADER_ATTR
1839 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1840 true /*sample_id_all*/)
1841 .WriteTo(&input);
1842
1843 // Perf infers the filename is "file", but at offset 0 for
1844 // hugepage-backed, anonymous mappings.
1845 //
1846 // vaddr start - vaddr end vaddr-size elf-offset
1847 // [0x55bd43879000-0x55bd45000000) (0x 1787000) @ 0x 0]: file
1848 // [0x55bd45000000-0x55bd58c00000) (0x13c00000) @ 0x 0]: file
1849 // [0x55bd58c00000-0x55bd59800000) (0x c00000) @ 0x15387000]: file
1850 // [0x55bd59800000-0x55bd59a00000) (0x 200000) @ 0x 0]: file
1851 // [0x55bd59a00000-0x55bd5b600000) (0x 1c00000) @ 0x16187000]: file
1852 // [0x55bd5b600000-0x55bd5b800000) (0x 200000) @ 0x 0]: file
1853 // [0x55bd5b800000-0x55bd5bcb5000) (0x 4b5000) @ 0x17f87000]: file
1854
1855 testing::ExampleMmapEvent(1234, 0x55bd43879000, 0x1787000, 0, "file",
1856 testing::SampleInfo().Tid(1234, 1234))
1857 .WriteTo(&input);
1858 testing::ExampleMmapEvent(1234, 0x55bd45000000, 0x13c00000, 0, "file",
1859 testing::SampleInfo().Tid(1234, 1234))
1860 .WriteTo(&input);
1861 testing::ExampleMmapEvent(1234, 0x55bd58c00000, 0xc00000, 0x15387000, "file",
1862 testing::SampleInfo().Tid(1234, 1234))
1863 .WriteTo(&input);
1864 testing::ExampleMmapEvent(1234, 0x55bd59800000, 0x200000, 0, "file",
1865 testing::SampleInfo().Tid(1234, 1234))
1866 .WriteTo(&input);
1867 testing::ExampleMmapEvent(1234, 0x55bd59a00000, 0x1c00000, 0x16187000, "file",
1868 testing::SampleInfo().Tid(1234, 1234))
1869 .WriteTo(&input);
1870 testing::ExampleMmapEvent(1234, 0x55bd5b600000, 0x200000, 0, "file",
1871 testing::SampleInfo().Tid(1234, 1234))
1872 .WriteTo(&input);
1873 testing::ExampleMmapEvent(1234, 0x55bd5b800000, 0x4b5000, 0x17f87000, "file",
1874 testing::SampleInfo().Tid(1234, 1234))
1875 .WriteTo(&input);
1876
1877 //
1878 // Parse input.
1879 //
1880
1881 PerfReader reader;
1882 EXPECT_TRUE(reader.ReadFromString(input.str()));
1883
1884 PerfParserOptions options;
1885 options.deduce_huge_page_mappings = true;
1886 options.combine_mappings = true;
1887 PerfParser parser(&reader, options);
1888 EXPECT_TRUE(parser.ParseRawEvents());
1889
1890 EXPECT_EQ(1, parser.stats().num_mmap_events);
1891 EXPECT_EQ(0, parser.stats().num_sample_events);
1892 EXPECT_EQ(0, parser.stats().num_sample_events_mapped);
1893
1894 PerfDataProto expected;
1895 {
1896 auto *ev = expected.add_events();
1897 ev->mutable_header()->set_type(PERF_RECORD_MMAP);
1898 ev->mutable_mmap_event()->set_filename("file");
1899 ev->mutable_mmap_event()->set_start(0x55bd43879000);
1900 ev->mutable_mmap_event()->set_len(0x55bd5bcb5000 - 0x55bd43879000);
1901 ev->mutable_mmap_event()->set_pgoff(0x0);
1902 }
1903
1904 PerfDataProto actual;
1905 CopyActualEvents(parser.parsed_events(), &actual);
1906 EXPECT_TRUE(PartiallyEqualsProto(actual, expected));
1907 ASSERT_EQ(1, parser.parsed_events().size());
1908 }
1909
TEST(PerfParserTest,Regression62446346_Perf3_12_0_14)1910 TEST(PerfParserTest, Regression62446346_Perf3_12_0_14) {
1911 std::stringstream input;
1912
1913 // header
1914 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1915
1916 // PERF_RECORD_HEADER_ATTR
1917 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1918 true /*sample_id_all*/)
1919 .WriteTo(&input);
1920
1921 // Perf infers the filename is "file", but at offset 0 for
1922 // hugepage-backed, anonymous mappings.
1923 //
1924 // vaddr start - vaddr end vaddr-size elf-offset
1925 // [0x55bd43879000-0x55bd45000000) (0x 1787000) @ 0x 0]: file
1926 // [0x55bd45000000-0x55bd58c00000) (0x13c00000) @ 0x 1787000]: file
1927 // [0x55bd58c00000-0x55bd59800000) (0x c00000) @ 0x15387000]: file
1928 // [0x55bd59800000-0x55bd59a00000) (0x 200000) @ 0x15f87000]: file
1929 // [0x55bd59a00000-0x55bd5b600000) (0x 1c00000) @ 0x16187000]: file
1930 // [0x55bd5b600000-0x55bd5b800000) (0x 200000) @ 0x17d87000]: file
1931 // [0x55bd5b800000-0x55bd5bcb5000) (0x 4b5000) @ 0x17f87000]: file
1932
1933 testing::ExampleMmapEvent(1234, 0x55bd43879000, 0x1787000, 0, "file",
1934 testing::SampleInfo().Tid(1234, 1234))
1935 .WriteTo(&input);
1936 testing::ExampleMmapEvent(1234, 0x55bd45000000, 0x13c00000, 0x1787000, "file",
1937 testing::SampleInfo().Tid(1234, 1234))
1938 .WriteTo(&input);
1939 testing::ExampleMmapEvent(1234, 0x55bd58c00000, 0xc00000, 0x15387000, "file",
1940 testing::SampleInfo().Tid(1234, 1234))
1941 .WriteTo(&input);
1942 testing::ExampleMmapEvent(1234, 0x55bd59800000, 0x200000, 0x15f87000, "file",
1943 testing::SampleInfo().Tid(1234, 1234))
1944 .WriteTo(&input);
1945 testing::ExampleMmapEvent(1234, 0x55bd59a00000, 0x1c00000, 0x16187000, "file",
1946 testing::SampleInfo().Tid(1234, 1234))
1947 .WriteTo(&input);
1948 testing::ExampleMmapEvent(1234, 0x55bd5b600000, 0x200000, 0x17d87000, "file",
1949 testing::SampleInfo().Tid(1234, 1234))
1950 .WriteTo(&input);
1951 testing::ExampleMmapEvent(1234, 0x55bd5b800000, 0x4b5000, 0x17f87000, "file",
1952 testing::SampleInfo().Tid(1234, 1234))
1953 .WriteTo(&input);
1954
1955 //
1956 // Parse input.
1957 //
1958
1959 PerfReader reader;
1960 EXPECT_TRUE(reader.ReadFromString(input.str()));
1961
1962 PerfParserOptions options;
1963 options.deduce_huge_page_mappings = true;
1964 options.combine_mappings = true;
1965 PerfParser parser(&reader, options);
1966 EXPECT_TRUE(parser.ParseRawEvents());
1967
1968 EXPECT_EQ(1, parser.stats().num_mmap_events);
1969 EXPECT_EQ(0, parser.stats().num_sample_events);
1970 EXPECT_EQ(0, parser.stats().num_sample_events_mapped);
1971
1972 PerfDataProto expected;
1973 {
1974 auto *ev = expected.add_events();
1975 ev->mutable_header()->set_type(PERF_RECORD_MMAP);
1976 ev->mutable_mmap_event()->set_filename("file");
1977 ev->mutable_mmap_event()->set_start(0x55bd43879000);
1978 ev->mutable_mmap_event()->set_len(0x55bd5bcb5000 - 0x55bd43879000);
1979 ev->mutable_mmap_event()->set_pgoff(0x0);
1980 }
1981
1982 PerfDataProto actual;
1983 CopyActualEvents(parser.parsed_events(), &actual);
1984 EXPECT_TRUE(PartiallyEqualsProto(actual, expected));
1985 EXPECT_EQ(1, parser.parsed_events().size());
1986 }
1987
TEST(PerfParserTest,DiscontiguousMappings)1988 TEST(PerfParserTest, DiscontiguousMappings) {
1989 std::stringstream input;
1990
1991 // header
1992 testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
1993
1994 // PERF_RECORD_HEADER_ATTR
1995 testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
1996 true /*sample_id_all*/)
1997 .WriteTo(&input);
1998
1999 // vaddr start - vaddr end vaddr-size elf-offset
2000 // [0x7f489000-0x80200000) (0xd77000) @ 0]: file
2001 // [0x80200000-0x80400000) (0x200000) @ 0]: file
2002 // [0x80400000-0x80474000) (0x47000) @ 0x1a00000]: file
2003 testing::ExampleMmapEvent(1234, 0x7f489000, 0xd77000, 0, "file",
2004 testing::SampleInfo().Tid(1234, 1234))
2005 .WriteTo(&input);
2006 testing::ExampleMmapEvent(1234, 0x80200000, 0x200000, 0, "file",
2007 testing::SampleInfo().Tid(1234, 1234))
2008 .WriteTo(&input);
2009 testing::ExampleMmapEvent(1234, 0x80400000, 0x47000, 0x1a00000, "file",
2010 testing::SampleInfo().Tid(1234, 1234))
2011 .WriteTo(&input);
2012
2013 //
2014 // Parse input.
2015 //
2016
2017 PerfReader reader;
2018 EXPECT_TRUE(reader.ReadFromString(input.str()));
2019
2020 PerfParserOptions options;
2021 options.deduce_huge_page_mappings = true;
2022 options.combine_mappings = true;
2023 PerfParser parser(&reader, options);
2024 EXPECT_TRUE(parser.ParseRawEvents());
2025
2026 EXPECT_EQ(3, parser.stats().num_mmap_events);
2027 EXPECT_EQ(0, parser.stats().num_sample_events);
2028 EXPECT_EQ(0, parser.stats().num_sample_events_mapped);
2029
2030 // The first two mappings should not combine, since we cannot know if the
2031 // middle one follows the first, or preceeds the last in the binary.
2032 PerfDataProto expected;
2033 {
2034 auto *ev = expected.add_events();
2035 ev->mutable_header()->set_type(PERF_RECORD_MMAP);
2036 ev->mutable_mmap_event()->set_filename("file");
2037 ev->mutable_mmap_event()->set_start(0x7f489000);
2038 ev->mutable_mmap_event()->set_len(0xd77000);
2039 ev->mutable_mmap_event()->set_pgoff(0x0);
2040 }
2041 {
2042 auto *ev = expected.add_events();
2043 ev->mutable_header()->set_type(PERF_RECORD_MMAP);
2044 ev->mutable_mmap_event()->set_filename("file");
2045 ev->mutable_mmap_event()->set_start(0x80200000);
2046 ev->mutable_mmap_event()->set_len(0x200000);
2047 ev->mutable_mmap_event()->set_pgoff(0x0);
2048 }
2049 {
2050 auto *ev = expected.add_events();
2051 ev->mutable_header()->set_type(PERF_RECORD_MMAP);
2052 ev->mutable_mmap_event()->set_filename("file");
2053 ev->mutable_mmap_event()->set_start(0x80400000);
2054 ev->mutable_mmap_event()->set_len(0x47000);
2055 ev->mutable_mmap_event()->set_pgoff(0x1a00000);
2056 }
2057
2058 PerfDataProto actual;
2059 CopyActualEvents(parser.parsed_events(), &actual);
2060
2061 EXPECT_TRUE(PartiallyEqualsProto(actual, expected));
2062 EXPECT_EQ(3, parser.parsed_events().size());
2063 }
2064 } // namespace quipper
2065