1// Copyright (c) 2012 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 <inttypes.h>
6#include <sys/time.h>
7
8#include <map>
9#include <sstream>
10#include <string>
11
12#include "base/logging.h"
13#include "base/macros.h"
14
15#include "compat/string.h"
16#include "compat/test.h"
17#include "file_utils.h"
18#include "perf_data_structures.h"
19#include "perf_data_utils.h"
20#include "perf_protobuf_io.h"
21#include "perf_reader.h"
22#include "perf_serializer.h"
23#include "perf_test_files.h"
24#include "scoped_temp_path.h"
25#include "test_perf_data.h"
26#include "test_utils.h"
27
28namespace {
29
30// Returns a string representation of an unsigned integer |value|.
31string UintToString(uint64_t value) {
32  std::stringstream ss;
33  ss << value;
34  return ss.str();
35}
36
37}  // namespace
38
39namespace quipper {
40
41using PerfEvent = PerfDataProto_PerfEvent;
42using SampleInfo = PerfDataProto_SampleInfo;
43
44namespace {
45
46// Set up some parameterized fixtures for test cases that should run
47// against multiple files.
48class SerializePerfDataFiles : public ::testing::TestWithParam<const char*> {};
49class SerializeAllPerfDataFiles : public ::testing::TestWithParam<const char*> {
50};
51class SerializePerfDataProtoFiles
52    : public ::testing::TestWithParam<const char*> {};
53
54// Gets the timestamp from an event field in PerfDataProto.
55const uint64_t GetSampleTimestampFromEventProto(
56    const PerfDataProto_PerfEvent& event) {
57  // Get SampleInfo from the correct type-specific event field for the event.
58  if (event.has_mmap_event()) {
59    return event.mmap_event().sample_info().sample_time_ns();
60  } else if (event.has_sample_event()) {
61    return event.sample_event().sample_time_ns();
62  } else if (event.has_comm_event()) {
63    return event.comm_event().sample_info().sample_time_ns();
64  } else if (event.has_fork_event()) {
65    return event.fork_event().sample_info().sample_time_ns();
66  } else if (event.has_exit_event()) {
67    return event.exit_event().sample_info().sample_time_ns();
68  } else if (event.has_lost_event()) {
69    return event.lost_event().sample_info().sample_time_ns();
70  } else if (event.has_throttle_event()) {
71    return event.throttle_event().sample_info().sample_time_ns();
72  } else if (event.has_read_event()) {
73    return event.read_event().sample_info().sample_time_ns();
74  } else if (event.has_aux_event()) {
75    return event.aux_event().sample_info().sample_time_ns();
76  }
77  return 0;
78}
79
80// Verifies that |proto|'s events are in chronological order. No event should
81// have an earlier timestamp than a preceding event.
82void CheckChronologicalOrderOfSerializedEvents(const PerfDataProto& proto) {
83  uint64_t prev_time_ns = 0;
84  for (int i = 0; i < proto.events_size(); ++i) {
85    // Compare each timestamp against the previous event's timestamp.
86    uint64_t time_ns = GetSampleTimestampFromEventProto(proto.events(i));
87    if (i > 0) {
88      EXPECT_GE(time_ns, prev_time_ns);
89    }
90    prev_time_ns = time_ns;
91  }
92}
93
94void SerializeAndDeserialize(const string& input, const string& output,
95                             bool do_remap, bool discard_unused_events) {
96  PerfDataProto perf_data_proto;
97  PerfParserOptions options;
98  options.do_remap = do_remap;
99  options.deduce_huge_page_mappings = false;
100  options.combine_mappings = false;
101  options.discard_unused_events = discard_unused_events;
102  options.sample_mapping_percentage_threshold = 100.0f;
103
104  ASSERT_TRUE(SerializeFromFileWithOptions(input, options, &perf_data_proto));
105
106  PerfReader reader;
107  ASSERT_TRUE(reader.Deserialize(perf_data_proto));
108
109  PerfParser parser(&reader, options);
110  ASSERT_TRUE(parser.ParseRawEvents());
111
112  // Check perf event stats.
113  const PerfDataProto_PerfEventStats& in_stats = perf_data_proto.stats();
114  PerfEventStats out_stats;
115  PerfSerializer::DeserializeParserStats(perf_data_proto, &out_stats);
116
117  EXPECT_EQ(in_stats.num_sample_events(), out_stats.num_sample_events);
118  EXPECT_EQ(in_stats.num_mmap_events(), out_stats.num_mmap_events);
119  EXPECT_EQ(in_stats.num_fork_events(), out_stats.num_fork_events);
120  EXPECT_EQ(in_stats.num_exit_events(), out_stats.num_exit_events);
121  EXPECT_EQ(in_stats.num_sample_events_mapped(),
122            out_stats.num_sample_events_mapped);
123  EXPECT_EQ(do_remap, in_stats.did_remap());
124  EXPECT_EQ(do_remap, out_stats.did_remap);
125
126  ASSERT_TRUE(reader.WriteFile(output));
127}
128
129void SerializeToFileAndBack(const string& input, const string& output) {
130  struct timeval pre_serialize_time;
131  gettimeofday(&pre_serialize_time, NULL);
132
133  // Serialize with and without sorting by chronological order.
134  PerfDataProto input_perf_data_proto;
135
136  // Serialize with and without sorting by chronological order.
137  // PerfSerializer is stateless w/r to Serialize or Deserialize calls so we can
138  // use just one.
139  PerfParserOptions options;
140  options.sort_events_by_time = true;
141  options.deduce_huge_page_mappings = false;
142  options.combine_mappings = false;
143  EXPECT_TRUE(
144      SerializeFromFileWithOptions(input, options, &input_perf_data_proto));
145  CheckChronologicalOrderOfSerializedEvents(input_perf_data_proto);
146
147  input_perf_data_proto.Clear();
148  options.sort_events_by_time = false;
149  EXPECT_TRUE(
150      SerializeFromFileWithOptions(input, options, &input_perf_data_proto));
151
152  // Make sure the timestamp_sec was properly recorded.
153  EXPECT_TRUE(input_perf_data_proto.has_timestamp_sec());
154  // Check it against the current time.
155  struct timeval post_serialize_time;
156  gettimeofday(&post_serialize_time, NULL);
157  EXPECT_GE(input_perf_data_proto.timestamp_sec(), pre_serialize_time.tv_sec);
158  EXPECT_LE(input_perf_data_proto.timestamp_sec(), post_serialize_time.tv_sec);
159
160  // Now store the protobuf into a file.
161  ScopedTempFile input_file;
162  EXPECT_FALSE(input_file.path().empty());
163  string input_filename = input_file.path();
164  ScopedTempFile output_file;
165  EXPECT_FALSE(output_file.path().empty());
166  string output_filename = output_file.path();
167
168  EXPECT_TRUE(WriteProtobufToFile(input_perf_data_proto, input_filename));
169
170  PerfDataProto output_perf_data_proto;
171  EXPECT_TRUE(ReadProtobufFromFile(&output_perf_data_proto, input_filename));
172
173  EXPECT_TRUE(DeserializeToFile(output_perf_data_proto, output));
174
175  EXPECT_TRUE(WriteProtobufToFile(output_perf_data_proto, output_filename));
176
177  EXPECT_NE(GetFileSize(input_filename), 0);
178  ASSERT_TRUE(CompareFileContents(input_filename, output_filename));
179
180  remove(input_filename.c_str());
181  remove(output_filename.c_str());
182}
183
184}  // namespace
185
186TEST_P(SerializePerfDataFiles, Test1Cycle) {
187  ScopedTempDir output_dir;
188  ASSERT_FALSE(output_dir.path().empty());
189  string output_path = output_dir.path();
190
191  // Read perf data using the PerfReader class.
192  // Dump it to a protobuf.
193  // Read the protobuf, and reconstruct the perf data.
194    PerfReader input_perf_reader, output_perf_reader, output_perf_reader1,
195        output_perf_reader2;
196    PerfDataProto perf_data_proto, perf_data_proto1;
197
198    const string test_file = GetParam();
199    const string input_perf_data = GetTestInputFilePath(test_file);
200    const string output_perf_data = output_path + test_file + ".serialized.out";
201    const string output_perf_data1 =
202        output_path + test_file + ".serialized.1.out";
203
204    LOG(INFO) << "Testing " << input_perf_data;
205    ASSERT_TRUE(input_perf_reader.ReadFile(input_perf_data));
206
207    // Discard unused events for a pseudorandom selection of half the test data
208    // files. The selection is based on the Md5sum prefix of the file contents,
209    // so that the files can be moved around in the |kPerfDataFiles| list or
210    // renamed.
211    std::vector<char> test_file_data;
212    ASSERT_TRUE(FileToBuffer(input_perf_data, &test_file_data));
213    bool discard = (Md5Prefix(test_file_data) % 2 == 0);
214
215    SerializeAndDeserialize(input_perf_data, output_perf_data, false, discard);
216    output_perf_reader.ReadFile(output_perf_data);
217    SerializeAndDeserialize(output_perf_data, output_perf_data1, false,
218                            discard);
219    output_perf_reader1.ReadFile(output_perf_data1);
220
221    ASSERT_TRUE(CompareFileContents(output_perf_data, output_perf_data1));
222
223    string output_perf_data2 = output_path + test_file + ".io.out";
224    SerializeToFileAndBack(input_perf_data, output_perf_data2);
225    output_perf_reader2.ReadFile(output_perf_data2);
226
227    // Make sure the # of events do not increase.  They can decrease because
228    // some unused non-sample events may be discarded.
229    if (discard) {
230      ASSERT_LE(output_perf_reader.events().size(),
231                input_perf_reader.events().size());
232    } else {
233      ASSERT_EQ(output_perf_reader.events().size(),
234                input_perf_reader.events().size());
235    }
236    ASSERT_EQ(output_perf_reader1.events().size(),
237              output_perf_reader.events().size());
238    ASSERT_EQ(output_perf_reader2.events().size(),
239              input_perf_reader.events().size());
240
241    EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data));
242    EXPECT_TRUE(ComparePerfBuildIDLists(input_perf_data, output_perf_data));
243    EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data2));
244    EXPECT_TRUE(ComparePerfBuildIDLists(output_perf_data, output_perf_data2));
245}
246
247TEST_P(SerializeAllPerfDataFiles, TestRemap) {
248  ScopedTempDir output_dir;
249  ASSERT_FALSE(output_dir.path().empty());
250  const string output_path = output_dir.path();
251
252  // Read perf data using the PerfReader class with address remapping.
253  // Dump it to a protobuf.
254  // Read the protobuf, and reconstruct the perf data.
255  const string test_file = GetParam();
256  const string input_perf_data = GetTestInputFilePath(test_file);
257  LOG(INFO) << "Testing " << input_perf_data;
258  const string output_perf_data = output_path + test_file + ".ser.remap.out";
259  SerializeAndDeserialize(input_perf_data, output_perf_data, true, true);
260}
261
262TEST_P(SerializePerfDataFiles, TestCommMd5s) {
263  ScopedTempDir output_dir;
264  ASSERT_FALSE(output_dir.path().empty());
265  string output_path = output_dir.path();
266
267  // Replace command strings with their Md5sums.  Test size adjustment for
268  // command strings.
269  const string test_file = GetParam();
270  const string input_perf_data = GetTestInputFilePath(test_file);
271  LOG(INFO) << "Testing COMM Md5sum for " << input_perf_data;
272
273  PerfDataProto perf_data_proto;
274  EXPECT_TRUE(SerializeFromFile(input_perf_data, &perf_data_proto));
275
276  // Need to get file attrs to construct a SampleInfoReader within
277  // |serializer|.
278  ASSERT_GT(perf_data_proto.file_attrs().size(), 0U);
279  ASSERT_TRUE(perf_data_proto.file_attrs(0).has_attr());
280  PerfSerializer serializer;
281  PerfFileAttr attr;
282  const auto& proto_attr = perf_data_proto.file_attrs(0);
283  ASSERT_TRUE(serializer.DeserializePerfFileAttr(proto_attr, &attr));
284  serializer.CreateSampleInfoReader(attr, false /* read_cross_endian */);
285
286  for (int j = 0; j < perf_data_proto.events_size(); ++j) {
287    PerfDataProto_PerfEvent& event = *perf_data_proto.mutable_events(j);
288    if (event.header().type() != PERF_RECORD_COMM) continue;
289    CHECK(event.has_comm_event());
290
291    string comm_md5_string = UintToString(event.comm_event().comm_md5_prefix());
292    // Make sure it fits in the comm string array, accounting for the null
293    // terminator.
294    struct comm_event dummy;
295    if (comm_md5_string.size() > arraysize(dummy.comm) - 1)
296      comm_md5_string.resize(arraysize(dummy.comm) - 1);
297    int64_t string_len_diff =
298        GetUint64AlignedStringLength(comm_md5_string) -
299        GetUint64AlignedStringLength(event.comm_event().comm());
300    event.mutable_comm_event()->set_comm(comm_md5_string);
301
302    // Update with the new size.
303    event.mutable_header()->set_size(event.header().size() + string_len_diff);
304    }
305
306    const string output_perf_data = output_path + test_file + ".ser.comm.out";
307    EXPECT_TRUE(DeserializeToFile(perf_data_proto, output_perf_data));
308    EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data));
309}
310
311TEST_P(SerializePerfDataFiles, TestMmapMd5s) {
312  ScopedTempDir output_dir;
313  ASSERT_FALSE(output_dir.path().empty());
314  string output_path = output_dir.path();
315
316  // Replace MMAP filename strings with their Md5sums.  Test size adjustment for
317  // MMAP filename strings.
318  const string test_file = GetParam();
319  const string input_perf_data = GetTestInputFilePath(test_file);
320  LOG(INFO) << "Testing MMAP Md5sum for " << input_perf_data;
321
322  PerfDataProto perf_data_proto;
323  EXPECT_TRUE(SerializeFromFile(input_perf_data, &perf_data_proto));
324
325  // Need to get file attrs to construct a SampleInfoReader within
326  // |serializer|.
327  ASSERT_GT(perf_data_proto.file_attrs().size(), 0U);
328  ASSERT_TRUE(perf_data_proto.file_attrs(0).has_attr());
329  PerfSerializer serializer;
330  PerfFileAttr attr;
331  const auto& proto_attr = perf_data_proto.file_attrs(0);
332  ASSERT_TRUE(serializer.DeserializePerfFileAttr(proto_attr, &attr));
333  serializer.CreateSampleInfoReader(attr, false /* read_cross_endian */);
334
335  for (int j = 0; j < perf_data_proto.events_size(); ++j) {
336    PerfDataProto_PerfEvent& event = *perf_data_proto.mutable_events(j);
337    if (event.header().type() != PERF_RECORD_MMAP) continue;
338    ASSERT_TRUE(event.has_mmap_event());
339
340    string filename_md5_string =
341        UintToString(event.mmap_event().filename_md5_prefix());
342    struct mmap_event dummy;
343    // Make sure the Md5 prefix string can fit in the filename buffer,
344    // including the null terminator
345    if (filename_md5_string.size() > arraysize(dummy.filename) - 1)
346      filename_md5_string.resize(arraysize(dummy.filename) - 1);
347
348    int64_t string_len_diff =
349        GetUint64AlignedStringLength(filename_md5_string) -
350        GetUint64AlignedStringLength(event.mmap_event().filename());
351    event.mutable_mmap_event()->set_filename(filename_md5_string);
352
353    // Update with the new size.
354    event.mutable_header()->set_size(event.header().size() + string_len_diff);
355    }
356
357    const string output_perf_data = output_path + test_file + ".ser.mmap.out";
358    // Make sure the data can be deserialized after replacing the filenames with
359    // Md5sum prefixes.  No need to check the output.
360    EXPECT_TRUE(DeserializeToFile(perf_data_proto, output_perf_data));
361}
362
363TEST_P(SerializePerfDataProtoFiles, TestProtoFiles) {
364  const string test_file = GetParam();
365  string perf_data_proto_file = GetTestInputFilePath(test_file);
366  LOG(INFO) << "Testing " << perf_data_proto_file;
367  std::vector<char> data;
368  ASSERT_TRUE(FileToBuffer(perf_data_proto_file, &data));
369  string text(data.begin(), data.end());
370
371  PerfDataProto perf_data_proto;
372  ASSERT_TRUE(TextFormat::ParseFromString(text, &perf_data_proto));
373
374  // Test deserializing.
375  PerfReader deserializer;
376  EXPECT_TRUE(deserializer.Deserialize(perf_data_proto));
377}
378
379TEST_P(SerializePerfDataFiles, TestBuildIDs) {
380  const string test_file = GetParam();
381  string perf_data_file = GetTestInputFilePath(test_file);
382  LOG(INFO) << "Testing " << perf_data_file;
383
384  // Serialize into a protobuf.
385  PerfDataProto perf_data_proto;
386  EXPECT_TRUE(SerializeFromFile(perf_data_file, &perf_data_proto));
387
388  // Test a file with build ID filenames removed.
389  for (int i = 0; i < perf_data_proto.build_ids_size(); ++i) {
390    perf_data_proto.mutable_build_ids(i)->clear_filename();
391  }
392  PerfReader deserializer;
393  EXPECT_TRUE(deserializer.Deserialize(perf_data_proto));
394}
395
396TEST(PerfSerializerTest, SerializesAndDeserializesTraceMetadata) {
397  std::stringstream input;
398
399  const size_t data_size =
400      testing::ExamplePerfSampleEvent_Tracepoint::kEventSize;
401
402  // header
403  testing::ExamplePerfDataFileHeader file_header(1 << HEADER_TRACING_DATA);
404  file_header.WithAttrCount(1).WithDataSize(data_size);
405  file_header.WriteTo(&input);
406  const perf_file_header& header = file_header.header();
407  // attrs
408  testing::ExamplePerfFileAttr_Tracepoint(73).WriteTo(&input);
409  // data
410  ASSERT_EQ(static_cast<u64>(input.tellp()), header.data.offset);
411  testing::ExamplePerfSampleEvent_Tracepoint().WriteTo(&input);
412  ASSERT_EQ(input.tellp(), file_header.data_end());
413  // metadata
414  const unsigned int metadata_count = 1;
415  // HEADER_TRACING_DATA
416  testing::ExampleTracingMetadata tracing_metadata(
417      file_header.data_end() + metadata_count * sizeof(perf_file_section));
418  tracing_metadata.index_entry().WriteTo(&input);
419  tracing_metadata.data().WriteTo(&input);
420
421  // Parse and Serialize
422
423  PerfReader reader;
424  ASSERT_TRUE(reader.ReadFromString(input.str()));
425
426  PerfDataProto perf_data_proto;
427  ASSERT_TRUE(reader.Serialize(&perf_data_proto));
428
429  const string& tracing_metadata_str = tracing_metadata.data().value();
430  const auto& tracing_data = perf_data_proto.tracing_data();
431  EXPECT_EQ(tracing_metadata_str, tracing_data.tracing_data());
432  EXPECT_EQ(Md5Prefix(tracing_metadata_str),
433            tracing_data.tracing_data_md5_prefix());
434
435  // Deserialize
436
437  PerfReader deserializer;
438  EXPECT_TRUE(deserializer.Deserialize(perf_data_proto));
439  EXPECT_EQ(tracing_metadata_str, deserializer.tracing_data());
440}
441
442TEST(PerfSerializerTest, SerializesAndDeserializesMmapEvents) {
443  std::stringstream input;
444
445  // header
446  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
447
448  // data
449
450  // PERF_RECORD_HEADER_ATTR
451  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
452                                              true /*sample_id_all*/)
453      .WriteTo(&input);
454
455  // PERF_RECORD_MMAP
456  testing::ExampleMmapEvent(1001, 0x1c1000, 0x1000, 0, "/usr/lib/foo.so",
457                            testing::SampleInfo().Tid(1001))
458      .WriteTo(&input);
459
460  // PERF_RECORD_MMAP2
461  testing::ExampleMmap2Event(1002, 0x2c1000, 0x2000, 0x3000, "/usr/lib/bar.so",
462                             testing::SampleInfo().Tid(1002))
463      .WriteTo(&input);
464
465  // Parse and Serialize
466
467  PerfReader reader;
468  ASSERT_TRUE(reader.ReadFromString(input.str()));
469
470  PerfDataProto perf_data_proto;
471  ASSERT_TRUE(reader.Serialize(&perf_data_proto));
472
473  EXPECT_EQ(2, perf_data_proto.events().size());
474
475  {
476    const PerfDataProto::PerfEvent& event = perf_data_proto.events(0);
477    EXPECT_EQ(PERF_RECORD_MMAP, event.header().type());
478    EXPECT_TRUE(event.has_mmap_event());
479    const PerfDataProto::MMapEvent& mmap = event.mmap_event();
480    EXPECT_EQ(1001, mmap.pid());
481    EXPECT_EQ(1001, mmap.tid());
482    EXPECT_EQ(0x1c1000, mmap.start());
483    EXPECT_EQ(0x1000, mmap.len());
484    EXPECT_EQ(0, mmap.pgoff());
485    EXPECT_EQ("/usr/lib/foo.so", mmap.filename());
486  }
487
488  {
489    const PerfDataProto::PerfEvent& event = perf_data_proto.events(1);
490    EXPECT_EQ(PERF_RECORD_MMAP2, event.header().type());
491    EXPECT_TRUE(event.has_mmap_event());
492    const PerfDataProto::MMapEvent& mmap = event.mmap_event();
493    EXPECT_EQ(1002, mmap.pid());
494    EXPECT_EQ(1002, mmap.tid());
495    EXPECT_EQ(0x2c1000, mmap.start());
496    EXPECT_EQ(0x2000, mmap.len());
497    EXPECT_EQ(0x3000, mmap.pgoff());
498    EXPECT_EQ("/usr/lib/bar.so", mmap.filename());
499    // These values are hard-coded in ExampleMmap2Event:
500    EXPECT_EQ(6, mmap.maj());
501    EXPECT_EQ(7, mmap.min());
502    EXPECT_EQ(8, mmap.ino());
503    EXPECT_EQ(9, mmap.ino_generation());
504    EXPECT_EQ(1 | 2, mmap.prot());
505    EXPECT_EQ(2, mmap.flags());
506  }
507}
508
509TEST(PerfSerializerTest, SerializesAndDeserializesAuxtraceEvents) {
510  std::stringstream input;
511
512  // header
513  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
514
515  // data
516
517  // PERF_RECORD_HEADER_ATTR
518  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP,
519                                              true /*sample_id_all*/)
520      .WriteTo(&input);
521
522  // PERF_RECORD_MMAP
523  testing::ExampleAuxtraceEvent(9, 0x2000, 7, 3, 0x68d, 4, 0, "/dev/zero")
524      .WriteTo(&input);
525
526  // Parse and Serialize
527
528  PerfReader reader;
529  ASSERT_TRUE(reader.ReadFromString(input.str()));
530
531  PerfDataProto perf_data_proto;
532  ASSERT_TRUE(reader.Serialize(&perf_data_proto));
533
534  EXPECT_EQ(1, perf_data_proto.events().size());
535
536  {
537    const PerfDataProto::PerfEvent& event = perf_data_proto.events(0);
538    EXPECT_EQ(PERF_RECORD_AUXTRACE, event.header().type());
539    EXPECT_TRUE(event.has_auxtrace_event());
540    const PerfDataProto::AuxtraceEvent& auxtrace_event = event.auxtrace_event();
541    EXPECT_EQ(9, auxtrace_event.size());
542    EXPECT_EQ(0x2000, auxtrace_event.offset());
543    EXPECT_EQ(7, auxtrace_event.reference());
544    EXPECT_EQ(3, auxtrace_event.idx());
545    EXPECT_EQ(0x68d, auxtrace_event.tid());
546    EXPECT_EQ("/dev/zero", auxtrace_event.trace_data());
547  }
548}
549
550// Regression test for http://crbug.com/501004.
551TEST(PerfSerializerTest, SerializesAndDeserializesBuildIDs) {
552  std::stringstream input;
553
554  // header
555  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
556
557  // no data
558
559  // PERF_RECORD_HEADER_ATTR
560  testing::ExamplePerfEventAttrEvent_Hardware(
561      PERF_SAMPLE_TID | PERF_SAMPLE_TIME, true /*sample_id_all*/)
562      .WriteTo(&input);
563
564  PerfReader reader;
565  ASSERT_TRUE(reader.ReadFromString(input.str()));
566
567  std::map<string, string> build_id_map;
568  build_id_map["file1"] = "0123456789abcdef0123456789abcdef01234567";
569  build_id_map["file2"] = "0123456789abcdef0123456789abcdef01230000";
570  build_id_map["file3"] = "0123456789abcdef0123456789abcdef00000000";
571  build_id_map["file4"] = "0123456789abcdef0123456789abcdef0000";
572  build_id_map["file5"] = "0123456789abcdef0123456789abcdef";
573  build_id_map["file6"] = "0123456789abcdef0123456789ab0000";
574  build_id_map["file7"] = "0123456789abcdef012345670000";
575  build_id_map["file8"] = "0123456789abcdef01234567";
576  build_id_map["file9"] = "00000000";
577  reader.InjectBuildIDs(build_id_map);
578
579  PerfDataProto perf_data_proto;
580  ASSERT_TRUE(reader.Serialize(&perf_data_proto));
581
582  // Verify that the build ID info was properly injected.
583  EXPECT_EQ(9, perf_data_proto.build_ids_size());
584  for (int i = 0; i < perf_data_proto.build_ids_size(); ++i) {
585    EXPECT_TRUE(perf_data_proto.build_ids(i).has_filename());
586    EXPECT_TRUE(perf_data_proto.build_ids(i).has_build_id_hash());
587  }
588
589  // Verify that the serialized build IDs have had their trailing zeroes
590  // trimmed.
591  EXPECT_EQ("file1", perf_data_proto.build_ids(0).filename());
592  EXPECT_EQ("0123456789abcdef0123456789abcdef01234567",
593            RawDataToHexString(perf_data_proto.build_ids(0).build_id_hash()));
594
595  EXPECT_EQ("file2", perf_data_proto.build_ids(1).filename());
596  EXPECT_EQ("0123456789abcdef0123456789abcdef01230000",
597            RawDataToHexString(perf_data_proto.build_ids(1).build_id_hash()));
598
599  EXPECT_EQ("file3", perf_data_proto.build_ids(2).filename());
600  EXPECT_EQ("0123456789abcdef0123456789abcdef",
601            RawDataToHexString(perf_data_proto.build_ids(2).build_id_hash()));
602
603  EXPECT_EQ("file4", perf_data_proto.build_ids(3).filename());
604  EXPECT_EQ("0123456789abcdef0123456789abcdef",
605            RawDataToHexString(perf_data_proto.build_ids(3).build_id_hash()));
606
607  EXPECT_EQ("file5", perf_data_proto.build_ids(4).filename());
608  EXPECT_EQ("0123456789abcdef0123456789abcdef",
609            RawDataToHexString(perf_data_proto.build_ids(4).build_id_hash()));
610
611  EXPECT_EQ("file6", perf_data_proto.build_ids(5).filename());
612  EXPECT_EQ("0123456789abcdef0123456789ab0000",
613            RawDataToHexString(perf_data_proto.build_ids(5).build_id_hash()));
614
615  EXPECT_EQ("file7", perf_data_proto.build_ids(6).filename());
616  EXPECT_EQ("0123456789abcdef01234567",
617            RawDataToHexString(perf_data_proto.build_ids(6).build_id_hash()));
618
619  EXPECT_EQ("file8", perf_data_proto.build_ids(7).filename());
620  EXPECT_EQ("0123456789abcdef01234567",
621            RawDataToHexString(perf_data_proto.build_ids(7).build_id_hash()));
622
623  EXPECT_EQ("file9", perf_data_proto.build_ids(8).filename());
624  EXPECT_EQ("",
625            RawDataToHexString(perf_data_proto.build_ids(8).build_id_hash()));
626
627  // Check deserialization.
628  PerfReader out_reader;
629  EXPECT_TRUE(out_reader.Deserialize(perf_data_proto));
630  const auto& build_ids = out_reader.build_ids();
631  ASSERT_EQ(9, build_ids.size());
632
633  std::vector<malloced_unique_ptr<build_id_event>> raw_build_ids(
634      build_ids.size());
635
636  // Convert the build IDs back to raw build ID events.
637  PerfSerializer serializer;
638  for (int i = 0; i < build_ids.size(); ++i) {
639    ASSERT_TRUE(serializer.DeserializeBuildIDEvent(build_ids.Get(i),
640                                                   &raw_build_ids[i]));
641  }
642
643  // All trimmed build IDs should be padded to the full 20 byte length.
644  EXPECT_EQ(string("file1"), raw_build_ids[0]->filename);
645  EXPECT_EQ("0123456789abcdef0123456789abcdef01234567",
646            RawDataToHexString(raw_build_ids[0]->build_id, kBuildIDArraySize));
647
648  EXPECT_EQ(string("file2"), raw_build_ids[1]->filename);
649  EXPECT_EQ("0123456789abcdef0123456789abcdef01230000",
650            RawDataToHexString(raw_build_ids[1]->build_id, kBuildIDArraySize));
651
652  EXPECT_EQ(string("file3"), raw_build_ids[2]->filename);
653  EXPECT_EQ("0123456789abcdef0123456789abcdef00000000",
654            RawDataToHexString(raw_build_ids[2]->build_id, kBuildIDArraySize));
655
656  EXPECT_EQ(string("file4"), raw_build_ids[3]->filename);
657  EXPECT_EQ("0123456789abcdef0123456789abcdef00000000",
658            RawDataToHexString(raw_build_ids[3]->build_id, kBuildIDArraySize));
659
660  EXPECT_EQ(string("file5"), raw_build_ids[4]->filename);
661  EXPECT_EQ("0123456789abcdef0123456789abcdef00000000",
662            RawDataToHexString(raw_build_ids[4]->build_id, kBuildIDArraySize));
663
664  EXPECT_EQ(string("file6"), raw_build_ids[5]->filename);
665  EXPECT_EQ("0123456789abcdef0123456789ab000000000000",
666            RawDataToHexString(raw_build_ids[5]->build_id, kBuildIDArraySize));
667
668  EXPECT_EQ(string("file7"), raw_build_ids[6]->filename);
669  EXPECT_EQ("0123456789abcdef012345670000000000000000",
670            RawDataToHexString(raw_build_ids[6]->build_id, kBuildIDArraySize));
671
672  EXPECT_EQ(string("file8"), raw_build_ids[7]->filename);
673  EXPECT_EQ("0123456789abcdef012345670000000000000000",
674            RawDataToHexString(raw_build_ids[7]->build_id, kBuildIDArraySize));
675
676  EXPECT_EQ(string("file9"), raw_build_ids[8]->filename);
677  EXPECT_EQ("0000000000000000000000000000000000000000",
678            RawDataToHexString(raw_build_ids[8]->build_id, kBuildIDArraySize));
679}
680
681// Regression test for http://crbug.com/500746.
682TEST(PerfSerializerTest, SerializesAndDeserializesForkAndExitEvents) {
683  std::stringstream input;
684
685  // header
686  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
687
688  // data
689
690  // PERF_RECORD_HEADER_ATTR
691  testing::ExamplePerfEventAttrEvent_Hardware(
692      PERF_SAMPLE_TID | PERF_SAMPLE_TIME, true /*sample_id_all*/)
693      .WriteTo(&input);
694
695  // PERF_RECORD_FORK
696  testing::ExampleForkEvent(
697      1010, 1020, 1030, 1040, 355ULL * 1000000000,
698      testing::SampleInfo().Tid(2010, 2020).Time(356ULL * 1000000000))
699      .WriteTo(&input);
700
701  // PERF_RECORD_EXIT
702  testing::ExampleExitEvent(
703      3010, 3020, 3030, 3040, 432ULL * 1000000000,
704      testing::SampleInfo().Tid(4010, 4020).Time(433ULL * 1000000000))
705      .WriteTo(&input);
706
707  // Parse and serialize.
708  PerfReader reader;
709  ASSERT_TRUE(reader.ReadFromString(input.str()));
710
711  PerfDataProto perf_data_proto;
712  ASSERT_TRUE(reader.Serialize(&perf_data_proto));
713
714  ASSERT_EQ(2, perf_data_proto.events_size());
715
716  {
717    const PerfDataProto_PerfEvent& event = perf_data_proto.events(0);
718    EXPECT_EQ(PERF_RECORD_FORK, event.header().type());
719    EXPECT_TRUE(event.has_fork_event());
720    EXPECT_FALSE(event.has_exit_event());
721
722    EXPECT_EQ(1010, event.fork_event().pid());
723    EXPECT_EQ(1020, event.fork_event().ppid());
724    EXPECT_EQ(1030, event.fork_event().tid());
725    EXPECT_EQ(1040, event.fork_event().ptid());
726    EXPECT_EQ(355ULL * 1000000000, event.fork_event().fork_time_ns());
727
728    EXPECT_EQ(2010, event.fork_event().sample_info().pid());
729    EXPECT_EQ(2020, event.fork_event().sample_info().tid());
730    EXPECT_EQ(356ULL * 1000000000,
731              event.fork_event().sample_info().sample_time_ns());
732  }
733
734  {
735    const PerfDataProto_PerfEvent& event = perf_data_proto.events(1);
736    EXPECT_EQ(PERF_RECORD_EXIT, event.header().type());
737    EXPECT_FALSE(event.has_fork_event());
738    EXPECT_TRUE(event.has_exit_event());
739
740    EXPECT_EQ(3010, event.exit_event().pid());
741    EXPECT_EQ(3020, event.exit_event().ppid());
742    EXPECT_EQ(3030, event.exit_event().tid());
743    EXPECT_EQ(3040, event.exit_event().ptid());
744    EXPECT_EQ(432ULL * 1000000000, event.exit_event().fork_time_ns());
745
746    EXPECT_EQ(4010, event.exit_event().sample_info().pid());
747    EXPECT_EQ(4020, event.exit_event().sample_info().tid());
748    EXPECT_EQ(433ULL * 1000000000,
749              event.exit_event().sample_info().sample_time_ns());
750  }
751
752  // Deserialize and verify events.
753  PerfReader out_reader;
754  ASSERT_TRUE(out_reader.Deserialize(perf_data_proto));
755
756  EXPECT_EQ(2, out_reader.events().size());
757
758  {
759    const PerfEvent& event = out_reader.events().Get(0);
760    EXPECT_EQ(PERF_RECORD_FORK, event.header().type());
761
762    EXPECT_EQ(1010, event.fork_event().pid());
763    EXPECT_EQ(1020, event.fork_event().ppid());
764    EXPECT_EQ(1030, event.fork_event().tid());
765    EXPECT_EQ(1040, event.fork_event().ptid());
766    EXPECT_EQ(355ULL * 1000000000, event.fork_event().fork_time_ns());
767
768    const SampleInfo& sample_info = event.fork_event().sample_info();
769    EXPECT_EQ(2010, sample_info.pid());
770    EXPECT_EQ(2020, sample_info.tid());
771    EXPECT_EQ(356ULL * 1000000000, sample_info.sample_time_ns());
772  }
773
774  {
775    const PerfEvent& event = out_reader.events().Get(1);
776    EXPECT_EQ(PERF_RECORD_EXIT, event.header().type());
777
778    EXPECT_EQ(3010, event.exit_event().pid());
779    EXPECT_EQ(3020, event.exit_event().ppid());
780    EXPECT_EQ(3030, event.exit_event().tid());
781    EXPECT_EQ(3040, event.exit_event().ptid());
782    EXPECT_EQ(432ULL * 1000000000, event.exit_event().fork_time_ns());
783
784    const SampleInfo& sample_info = event.exit_event().sample_info();
785    EXPECT_EQ(4010, sample_info.pid());
786    EXPECT_EQ(4020, sample_info.tid());
787    EXPECT_EQ(433ULL * 1000000000, sample_info.sample_time_ns());
788  }
789}
790
791// Regression test for http://crbug.com/500746.
792TEST(PerfSerializerTest, DeserializeLegacyExitEvents) {
793  std::stringstream input;
794
795  // header
796  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
797
798  // data
799
800  // PERF_RECORD_HEADER_ATTR
801  testing::ExamplePerfEventAttrEvent_Hardware(
802      PERF_SAMPLE_TID | PERF_SAMPLE_TIME, true /*sample_id_all*/)
803      .WriteTo(&input);
804
805  // PERF_RECORD_EXIT
806  testing::ExampleExitEvent(
807      3010, 3020, 3030, 3040, 432ULL * 1000000000,
808      testing::SampleInfo().Tid(4010, 4020).Time(433ULL * 1000000000))
809      .WriteTo(&input);
810
811  // Parse and serialize.
812  PerfReader reader;
813  ASSERT_TRUE(reader.ReadFromString(input.str()));
814
815  PerfDataProto proto;
816  ASSERT_TRUE(reader.Serialize(&proto));
817
818  ASSERT_EQ(1, proto.events_size());
819  ASSERT_TRUE(proto.events(0).has_exit_event());
820  ASSERT_FALSE(proto.events(0).has_fork_event());
821
822  // Modify the protobuf to store the exit event in the |fork_event| field
823  // instead.
824  PerfDataProto_ForkEvent ex;
825  ex.CopyFrom(proto.events(0).exit_event());
826  proto.mutable_events(0)->clear_exit_event();
827  proto.mutable_events(0)->mutable_fork_event()->CopyFrom(ex);
828
829  PerfReader out_reader;
830  ASSERT_TRUE(out_reader.Deserialize(proto));
831
832  EXPECT_EQ(1U, out_reader.events().size());
833
834  const PerfEvent& event = out_reader.events().Get(0);
835  EXPECT_EQ(PERF_RECORD_EXIT, event.header().type());
836  EXPECT_EQ(3010, event.fork_event().pid());
837  EXPECT_EQ(3020, event.fork_event().ppid());
838  EXPECT_EQ(3030, event.fork_event().tid());
839  EXPECT_EQ(3040, event.fork_event().ptid());
840  EXPECT_EQ(432ULL * 1000000000, event.fork_event().fork_time_ns());
841
842  const SampleInfo& sample_info = event.fork_event().sample_info();
843  EXPECT_EQ(4010, sample_info.pid());
844  EXPECT_EQ(4020, sample_info.tid());
845  EXPECT_EQ(433ULL * 1000000000, sample_info.sample_time_ns());
846}
847
848namespace {
849std::vector<const char*> AllPerfData() {
850  const auto& files = perf_test_files::GetPerfDataFiles();
851  const auto& piped = perf_test_files::GetPerfPipedDataFiles();
852
853  std::vector<const char*> ret(std::begin(files), std::end(files));
854  ret.insert(std::end(ret), std::begin(piped), std::end(piped));
855  return ret;
856}
857}  // namespace
858
859INSTANTIATE_TEST_CASE_P(
860    PerfSerializerTest, SerializePerfDataFiles,
861    ::testing::ValuesIn(perf_test_files::GetPerfDataFiles()));
862INSTANTIATE_TEST_CASE_P(PerfSerializerTest, SerializeAllPerfDataFiles,
863                        ::testing::ValuesIn(AllPerfData()));
864INSTANTIATE_TEST_CASE_P(
865    PerfSerializerTest, SerializePerfDataProtoFiles,
866    ::testing::ValuesIn(perf_test_files::GetPerfDataProtoFiles()));
867}  // namespace quipper
868