// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <byteswap.h>

#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include "base/logging.h"

#include "compat/string.h"
#include "compat/test.h"
#include "file_utils.h"
#include "perf_reader.h"
#include "perf_test_files.h"
#include "scoped_temp_path.h"
#include "test_perf_data.h"
#include "test_utils.h"

namespace quipper {

using PerfEvent = PerfDataProto_PerfEvent;
using SampleEvent = PerfDataProto_SampleEvent;
using SampleInfo = PerfDataProto_SampleInfo;

TEST(PerfReaderTest, PipedData_IncompleteEventHeader) {
  std::stringstream input;

  // pipe header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP,
                                              true /*sample_id_all*/)
      .WithConfig(123)
      .WriteTo(&input);

  // PERF_RECORD_HEADER_EVENT_TYPE
  const struct event_type_event event_type = {
      .header =
          {
              .type = PERF_RECORD_HEADER_EVENT_TYPE,
              .misc = 0,
              .size = sizeof(struct event_type_event),
          },
      .event_type = {
          /*event_id*/ 123,
          /*name*/ "cycles",
      },
  };
  input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type));

  // Incomplete data at the end:
  input << string(sizeof(struct perf_event_header) - 1, '\0');

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  ASSERT_EQ(1, pr.attrs().size());
  EXPECT_EQ(123, pr.attrs().Get(0).attr().config());
  ASSERT_EQ(1, pr.event_types().size());
  EXPECT_EQ("cycles", pr.event_types().Get(0).name());

  // Make sure metadata mask was set to indicate EVENT_TYPE was upgraded
  // to EVENT_DESC.
  EXPECT_EQ((1 << HEADER_EVENT_DESC), pr.metadata_mask());
}

TEST(PerfReaderTest, PipedData_IncompleteEventData) {
  std::stringstream input;

  // pipe header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP,
                                              true /*sample_id_all*/)
      .WithConfig(456)
      .WriteTo(&input);

  // PERF_RECORD_HEADER_EVENT_TYPE
  const struct event_type_event event_type = {
      .header =
          {
              .type = PERF_RECORD_HEADER_EVENT_TYPE,
              .misc = 0,
              .size = sizeof(struct event_type_event),
          },
      .event_type = {
          /*event_id*/ 456,
          /*name*/ "instructions",
      },
  };
  input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type));

  // Incomplete data at the end:
  // Header:
  const struct perf_event_header incomplete_event_header = {
      .type = PERF_RECORD_SAMPLE,
      .misc = 0,
      .size = sizeof(perf_event_header) + 10,
  };
  input.write(reinterpret_cast<const char*>(&incomplete_event_header),
              sizeof(incomplete_event_header));
  // Incomplete event:
  input << string(3, '\0');

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  ASSERT_EQ(1, pr.attrs().size());
  EXPECT_EQ(456, pr.attrs().Get(0).attr().config());
  ASSERT_EQ(1, pr.event_types().size());
  EXPECT_EQ("instructions", pr.event_types().Get(0).name());

  // Make sure metadata mask was set to indicate EVENT_TYPE was upgraded
  // to EVENT_DESC.
  EXPECT_EQ((1 << HEADER_EVENT_DESC), pr.metadata_mask());
}

TEST(PerfReaderTest, CorruptedFiles) {
  for (const char* test_file :
       perf_test_files::GetCorruptedPerfPipedDataFiles()) {
    string input_perf_data = GetTestInputFilePath(test_file);
    LOG(INFO) << "Testing " << input_perf_data;
    ASSERT_TRUE(FileExists(input_perf_data)) << "Test file does not exist!";
    PerfReader pr;
    ASSERT_FALSE(pr.ReadFile(input_perf_data));
  }
}

TEST(PerfReaderTest, ReadsAndWritesPipedModeAuxEvents) {
  std::stringstream input;

  // header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP,
                                              false /*sample_id_all*/)
      .WriteTo(&input);

  // PERF_RECORD_AUX
  struct aux_event written_aux_event = {
      .header =
          {
              .type = PERF_RECORD_AUX,
              .misc = 0,
              .size = sizeof(struct aux_event),
          },
      .aux_offset = 0x2000,
      .aux_size = 16,
      .flags = PERF_AUX_FLAG_TRUNCATED | PERF_AUX_FLAG_PARTIAL,
  };
  input.write(reinterpret_cast<const char*>(&written_aux_event),
              sizeof(struct aux_event));

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(1, pr->events().size());

    const PerfEvent& event = pr->events().Get(0);
    EXPECT_EQ(PERF_RECORD_AUX, event.header().type());
    EXPECT_EQ(0x2000, event.aux_event().aux_offset());
    EXPECT_EQ(16, event.aux_event().aux_size());
    EXPECT_TRUE(event.aux_event().is_truncated());
    EXPECT_FALSE(event.aux_event().is_overwrite());
    EXPECT_TRUE(event.aux_event().is_partial());
  }
}

TEST(PerfReaderTest, ReadsAndWritesPipedModeAuxTraceEvents) {
  std::stringstream input;

  // header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP,
                                              false /*sample_id_all*/)
      .WriteTo(&input);

  // PERF_RECORD_AUXTRACE
  testing::ExampleAuxtraceEvent(9, 0x2000, 7, 3, 0x68d, 4, 0, "/dev/zero")
      .WriteTo(&input);

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(1, pr->events().size());

    const PerfEvent& event = pr->events().Get(0);
    EXPECT_EQ(PERF_RECORD_AUXTRACE, event.header().type());
    EXPECT_EQ(9, event.auxtrace_event().size());
    EXPECT_EQ(0x2000, event.auxtrace_event().offset());
    EXPECT_EQ(7, event.auxtrace_event().reference());
    EXPECT_EQ(3, event.auxtrace_event().idx());
    EXPECT_EQ(0x68d, event.auxtrace_event().tid());
    EXPECT_EQ(4, event.auxtrace_event().cpu());
    EXPECT_EQ("/dev/zero", event.auxtrace_event().trace_data());
  }
}

TEST(PerfReaderTest, ReadsAndWritesAuxTraceEvents) {
  std::stringstream input;

  // PERF_RECORD_AUXTRACE
  testing::ExampleAuxtraceEvent auxtrace_event(9, 0x2000, 7, 3, 0x68d, 4, 0,
                                               "/dev/zero");
  const size_t data_size =
      auxtrace_event.GetSize() + auxtrace_event.GetTraceSize();

  // header
  testing::ExamplePerfDataFileHeader file_header((0));
  file_header.WithAttrCount(1).WithDataSize(data_size);
  file_header.WriteTo(&input);

  // attrs
  ASSERT_EQ(file_header.header().attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP, false /*sample_id_all*/)
      .WriteTo(&input);

  // data
  ASSERT_EQ(file_header.header().data.offset, static_cast<u64>(input.tellp()));
  auxtrace_event.WriteTo(&input);

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(1, pr->events().size());

    const PerfEvent& event = pr->events().Get(0);
    EXPECT_EQ(PERF_RECORD_AUXTRACE, event.header().type());
    EXPECT_EQ(9, event.auxtrace_event().size());
    EXPECT_EQ(0x2000, event.auxtrace_event().offset());
    EXPECT_EQ(7, event.auxtrace_event().reference());
    EXPECT_EQ(3, event.auxtrace_event().idx());
    EXPECT_EQ(0x68d, event.auxtrace_event().tid());
    EXPECT_EQ(4, event.auxtrace_event().cpu());
    EXPECT_EQ("/dev/zero", event.auxtrace_event().trace_data());
  }
}

TEST(PerfReaderTest, ReadsAndWritesTraceMetadata) {
  std::stringstream input;

  const size_t data_size =
      testing::ExamplePerfSampleEvent_Tracepoint::kEventSize;

  // header
  testing::ExamplePerfDataFileHeader file_header(1 << HEADER_TRACING_DATA);
  file_header.WithAttrCount(1).WithDataSize(data_size);
  file_header.WriteTo(&input);
  const perf_file_header& header = file_header.header();

  // attrs
  CHECK_EQ(header.attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Tracepoint(73).WriteTo(&input);

  // data
  ASSERT_EQ(header.data.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfSampleEvent_Tracepoint().WriteTo(&input);
  ASSERT_EQ(file_header.data_end(), input.tellp());

  // metadata

  const unsigned int metadata_count = 1;

  // HEADER_TRACING_DATA
  testing::ExampleTracingMetadata tracing_metadata(
      file_header.data_end() + metadata_count * sizeof(perf_file_section));

  // write metadata index entries
  tracing_metadata.index_entry().WriteTo(&input);
  // write metadata
  tracing_metadata.data().WriteTo(&input);

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  EXPECT_EQ(tracing_metadata.data().value(), pr.tracing_data());

  // Write it out and read it in again, it should still be good:
  std::vector<char> output_perf_data;
  EXPECT_TRUE(pr.WriteToVector(&output_perf_data));
  EXPECT_TRUE(pr.ReadFromVector(output_perf_data));
  EXPECT_EQ(tracing_metadata.data().value(), pr.tracing_data());
}

TEST(PerfReaderTest, ReadsTracingMetadataEvent) {
  std::stringstream input;

  // pipe header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  const char raw_data[] = "\x17\x08\x44tracing0.5BLAHBLAHBLAH....";
  const string trace_metadata(raw_data, sizeof(raw_data) - 1);

  const tracing_data_event trace_event = {
      .header =
          {
              .type = PERF_RECORD_HEADER_TRACING_DATA,
              .misc = 0,
              .size = sizeof(tracing_data_event),
          },
      .size = static_cast<u32>(trace_metadata.size()),
  };

  input.write(reinterpret_cast<const char*>(&trace_event), sizeof(trace_event));
  input.write(trace_metadata.data(), trace_metadata.size());

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));
  EXPECT_EQ(trace_metadata, pr.tracing_data());

  // Write it out and read it in again, tracing_data() should still be correct.
  // NB: It does not get written as an event, but in a metadata section.
  std::vector<char> output_perf_data;
  EXPECT_TRUE(pr.WriteToVector(&output_perf_data));
  EXPECT_TRUE(pr.ReadFromVector(output_perf_data));
  EXPECT_EQ(trace_metadata, pr.tracing_data());
}

// Regression test for http://crbug.com/484393
TEST(PerfReaderTest, BranchStackMetadataIndexHasZeroSize) {
  std::stringstream input;

  const size_t data_size =
      testing::ExamplePerfSampleEvent_BranchStack::kEventSize;

  // header
  testing::ExamplePerfDataFileHeader file_header(1 << HEADER_BRANCH_STACK);
  file_header.WithAttrCount(1).WithDataSize(data_size);
  file_header.WriteTo(&input);
  const perf_file_header& header = file_header.header();

  // attrs
  CHECK_EQ(header.attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_BRANCH_STACK,
                                        false /*sample_id_all*/)
      .WriteTo(&input);

  // data
  ASSERT_EQ(header.data.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfSampleEvent_BranchStack().WriteTo(&input);
  ASSERT_EQ(file_header.data_end(), input.tellp());

  // metadata

  // HEADER_BRANCH_STACK
  const perf_file_section branch_stack_index = {
      .offset = file_header.data_end_offset(),
      .size = 0,
  };
  input.write(reinterpret_cast<const char*>(&branch_stack_index),
              sizeof(branch_stack_index));

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Write it out again.
  // Initialize the buffer to a non-zero sentinel value so that the bytes
  // we are checking were written with zero must have been written.
  const size_t max_predicted_written_size = 1024;
  std::vector<char> output_perf_data(max_predicted_written_size, '\xaa');
  EXPECT_TRUE(pr.WriteToVector(&output_perf_data));
  EXPECT_LE(output_perf_data.size(), max_predicted_written_size)
      << "Bad prediction for written size";

  // Specifically check that the metadata index has zero in the size.
  const auto* output_header =
      reinterpret_cast<struct perf_file_header*>(output_perf_data.data());
  EXPECT_EQ(1 << HEADER_BRANCH_STACK, output_header->adds_features[0])
      << "Expected just a HEADER_BRANCH_STACK feature";
  const size_t metadata_offset =
      output_header->data.offset + output_header->data.size;
  const auto* output_feature_index =
      reinterpret_cast<struct perf_file_section*>(output_perf_data.data() +
                                                  metadata_offset);
  EXPECT_EQ(0, output_feature_index[0].size)
      << "Regression: Expected zero size for the HEADER_BRANCH_STACK feature "
      << "metadata index";
}

// Regression test for http://crbug.com/427767
TEST(PerfReaderTest, CorrectlyReadsPerfEventAttrSize) {
  std::stringstream input;

  // pipe header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  struct old_perf_event_attr {
    __u32 type;
    __u32 size;
    __u64 config;
    // clang-format off
    // union {
    __u64 sample_period;
    //      __u64 sample_freq;
    // };
    // clang-format on
    __u64 sample_type;
    __u64 read_format;
    // Skip the rest of the fields from perf_event_attr to simulate an
    // older, smaller version of the struct.
  };

  struct old_attr_event {
    struct perf_event_header header;
    struct old_perf_event_attr attr;
    u64 id[];
  };

  const old_attr_event attr = {
      .header =
          {
              .type = PERF_RECORD_HEADER_ATTR,
              .misc = 0,
              // A count of 8 ids is carefully selected to make the event exceed
              // 96 bytes (sizeof(perf_event_attr)) so that the test fails
              // instead of crashes with the old code.
              .size = sizeof(old_attr_event) + 8 * sizeof(u64),
          },
      .attr =
          {
              .type = 0,
              .size = sizeof(old_perf_event_attr),
              .config = 0,
              .sample_period = 10000001,
              .sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID |
                             PERF_SAMPLE_TIME | PERF_SAMPLE_ID |
                             PERF_SAMPLE_CPU,
              .read_format = PERF_FORMAT_ID,
          },
  };

  input.write(reinterpret_cast<const char*>(&attr), sizeof(attr));
  for (u64 id : {301, 302, 303, 304, 305, 306, 307, 308})
    input.write(reinterpret_cast<const char*>(&id), sizeof(id));

  // Add some sample events so that there's something to over-read.
  const sample_event sample = {
      .header = {
          .type = PERF_RECORD_SAMPLE,
          .misc = 0,
          .size = sizeof(perf_event_header) + 5 * sizeof(u64),
      }};
  // We don't care about the contents of the SAMPLE events, except for the ID,
  // which is needed to determine the attr for reading sample info.
  const u64 sample_event_array[] = {
      0,    // IP
      0,    // TID
      0,    // TIME
      308,  // ID
      0,    // CPU
  };

  for (int i = 0; i < 20; i++) {
    input.write(reinterpret_cast<const char*>(&sample), sizeof(sample));
    input.write(reinterpret_cast<const char*>(sample_event_array),
                sizeof(sample_event_array));
  }

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));
  ASSERT_EQ(pr.attrs().size(), 1);
  const auto& actual_attr = pr.attrs().Get(0);
  ASSERT_EQ(8, actual_attr.ids().size());
  EXPECT_EQ(301, actual_attr.ids(0));
  EXPECT_EQ(302, actual_attr.ids(1));
  EXPECT_EQ(303, actual_attr.ids(2));
  EXPECT_EQ(304, actual_attr.ids(3));
}

// Tests all sample info fields. When support for new sample_info fields are
// added to quipper, update this test accordingly.
TEST(PerfReaderTest, ReadsAndWritesSampleEvent) {
  using testing::PunU32U64;

  std::stringstream input;

  // header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  const u64 sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
                          PERF_SAMPLE_ADDR | PERF_SAMPLE_READ |
                          PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_CPU |
                          PERF_SAMPLE_ID | PERF_SAMPLE_PERIOD |
                          PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_BRANCH_STACK;

  const size_t num_sample_event_bits = 10;
  // not tested:
  // PERF_SAMPLE_RAW |
  testing::ExamplePerfEventAttrEvent_Hardware(sample_type,
                                              true /*sample_id_all*/)
      .WithId(401)
      .WithReadFormat(PERF_FORMAT_TOTAL_TIME_ENABLED |
                      PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID)
      .WriteTo(&input);

  // PERF_RECORD_SAMPLE
  const size_t call_chain_size = 6;
  const size_t branch_stack_size = 5;
  const sample_event written_sample_event = {
      .header = {
          .type = PERF_RECORD_SAMPLE,
          .misc = PERF_RECORD_MISC_KERNEL,
          .size =
              sizeof(struct sample_event) +
              num_sample_event_bits * sizeof(u64) +
              4 * sizeof(
                      u64) +  // Non-grouped read info, see
                              // perf_event_read_format in kernel/perf_event.h.
              call_chain_size * sizeof(u64) +
              branch_stack_size * sizeof(struct branch_entry),
      }};
  const u64 sample_event_array[] = {
      0xffffffff01234567,                    // IP
      PunU32U64{.v32 = {0x68d, 0x68e}}.v64,  // TID (u32 pid, tid)
      1415837014 * 1000000000ULL,            // TIME
      0x00007f999c38d15a,                    // ADDR
      401,                                   // ID
      1,                                     // STREAM_ID
      8,                                     // CPU
      10001,                                 // PERIOD

      // READ
      0x103c5d0,       // value
      0x7f6e8c45a920,  // time_enabled
      0x7ffed1a5e950,  // time_running
      402,             // id

      // clang-format off
      // CALLCHAIN
      6,         // nr
      0x1c1000,  // ips
      0x1c2000,
      0x1c3000,
      0x2c0000,
      0x2c1000,
      0x3c1000,

      // BRANCH_STACK
      5,  // nr
      0x00007f4a313bb8cc, 0x00007f4a313bdb40, 0x02,  // entries
      0x00007f4a30ce4de2, 0x00007f4a313bb8b3, 0x02,  // predicted = 0x2
      0x00007f4a313bb8b0, 0x00007f4a30ce4de0, 0x01,  // mispredict = 0x1
      0x00007f4a30ff45c1, 0x00007f4a313bb8a0, 0x02,
      0x00007f4a30ff49f2, 0x00007f4a30ff45bb, 0x02,
      // clang-format on
  };
  ASSERT_EQ(written_sample_event.header.size,
            sizeof(written_sample_event.header) + sizeof(sample_event_array));
  input.write(reinterpret_cast<const char*>(&written_sample_event),
              sizeof(written_sample_event));
  input.write(reinterpret_cast<const char*>(sample_event_array),
              sizeof(sample_event_array));

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(1, pr->events().size());

    const PerfEvent& event = pr->events().Get(0);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    const SampleEvent& sample = event.sample_event();
    EXPECT_EQ(0xffffffff01234567, sample.ip());
    EXPECT_EQ(0x68d, sample.pid());
    EXPECT_EQ(0x68e, sample.tid());
    EXPECT_EQ(1415837014 * 1000000000ULL, sample.sample_time_ns());
    EXPECT_EQ(0x00007f999c38d15a, sample.addr());
    EXPECT_EQ(401, sample.id());
    EXPECT_EQ(1, sample.stream_id());
    EXPECT_EQ(8, sample.cpu());
    EXPECT_EQ(10001, sample.period());

    // Read info
    EXPECT_TRUE(sample.has_read_info());
    EXPECT_EQ(0x7f6e8c45a920, sample.read_info().time_enabled());
    EXPECT_EQ(0x7ffed1a5e950, sample.read_info().time_running());
    ASSERT_EQ(1, sample.read_info().read_value_size());
    EXPECT_EQ(0x103c5d0, sample.read_info().read_value(0).value());
    EXPECT_EQ(402, sample.read_info().read_value(0).id());

    // Callchain.
    ASSERT_EQ(6, sample.callchain_size());
    EXPECT_EQ(0x1c1000, sample.callchain(0));
    EXPECT_EQ(0x1c2000, sample.callchain(1));
    EXPECT_EQ(0x1c3000, sample.callchain(2));
    EXPECT_EQ(0x2c0000, sample.callchain(3));
    EXPECT_EQ(0x2c1000, sample.callchain(4));
    EXPECT_EQ(0x3c1000, sample.callchain(5));

    // Branch stack.
    ASSERT_EQ(5, sample.branch_stack_size());
    EXPECT_EQ(0x00007f4a313bb8cc, sample.branch_stack(0).from_ip());
    EXPECT_EQ(0x00007f4a313bdb40, sample.branch_stack(0).to_ip());
    EXPECT_FALSE(sample.branch_stack(0).mispredicted());
    EXPECT_EQ(0x00007f4a30ce4de2, sample.branch_stack(1).from_ip());
    EXPECT_EQ(0x00007f4a313bb8b3, sample.branch_stack(1).to_ip());
    EXPECT_FALSE(sample.branch_stack(1).mispredicted());
    EXPECT_EQ(0x00007f4a313bb8b0, sample.branch_stack(2).from_ip());
    EXPECT_EQ(0x00007f4a30ce4de0, sample.branch_stack(2).to_ip());
    EXPECT_TRUE(sample.branch_stack(2).mispredicted());
    EXPECT_EQ(0x00007f4a30ff45c1, sample.branch_stack(3).from_ip());
    EXPECT_EQ(0x00007f4a313bb8a0, sample.branch_stack(3).to_ip());
    EXPECT_FALSE(sample.branch_stack(3).mispredicted());
    EXPECT_EQ(0x00007f4a30ff49f2, sample.branch_stack(4).from_ip());
    EXPECT_EQ(0x00007f4a30ff45bb, sample.branch_stack(4).to_ip());
    EXPECT_FALSE(sample.branch_stack(4).mispredicted());
  }
}

TEST(PerfReaderTest, ReadsAndWritesSampleEventMissingTime) {
  using testing::PunU32U64;

  std::stringstream input;

  // header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  const u64 sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ | PERF_SAMPLE_ID;
  const size_t num_sample_event_bits = 2;
  testing::ExamplePerfEventAttrEvent_Hardware(sample_type,
                                              true /*sample_id_all*/)
      .WithId(401)
      .WithReadFormat(PERF_FORMAT_ID)
      .WriteTo(&input);
  const sample_event written_sample_event = {
      .header = {
          .type = PERF_RECORD_SAMPLE,
          .misc = 0,
          .size = sizeof(struct sample_event) +
                  num_sample_event_bits * sizeof(u64) +
                  2 * sizeof(u64),  // Non-grouped read info (without times!).
      }};
  const u64 sample_event_array[] = {
      0xffffffff01234567,  // IP
      401,                 // ID
      // READ
      0x103c5d0,  // value
      402,        // id
  };
  ASSERT_EQ(written_sample_event.header.size,
            sizeof(written_sample_event.header) + sizeof(sample_event_array));
  input.write(reinterpret_cast<const char*>(&written_sample_event),
              sizeof(written_sample_event));
  input.write(reinterpret_cast<const char*>(sample_event_array),
              sizeof(sample_event_array));

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(1, pr->events().size());

    const PerfEvent& event = pr->events().Get(0);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    const SampleEvent& sample = event.sample_event();
    EXPECT_EQ(0xffffffff01234567, sample.ip());
    EXPECT_EQ(401, sample.id());

    // Read info
    EXPECT_TRUE(sample.has_read_info());
    EXPECT_FALSE(sample.read_info().has_time_enabled());
    EXPECT_FALSE(sample.read_info().has_time_running());
    ASSERT_EQ(1, sample.read_info().read_value_size());
    EXPECT_EQ(0x103c5d0, sample.read_info().read_value(0).value());
    EXPECT_EQ(402, sample.read_info().read_value(0).id());
  }
}

TEST(PerfReaderTest, ReadsAndWritesSampleAndSampleIdAll) {
  using testing::PunU32U64;

  std::stringstream input;

  // header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  // clang-format off
  const u64 sample_type =      // * == in sample_id_all
      PERF_SAMPLE_IP |
      PERF_SAMPLE_TID |        // *
      PERF_SAMPLE_TIME |       // *
      PERF_SAMPLE_ADDR |
      PERF_SAMPLE_ID |         // *
      PERF_SAMPLE_STREAM_ID |  // *
      PERF_SAMPLE_CPU |        // *
      PERF_SAMPLE_PERIOD;
  // clang-format on
  const size_t num_sample_event_bits = 8;
  const size_t num_sample_id_bits = 5;
  // not tested:
  // PERF_SAMPLE_READ |
  // PERF_SAMPLE_RAW |
  // PERF_SAMPLE_CALLCHAIN |
  // PERF_SAMPLE_BRANCH_STACK |
  testing::ExamplePerfEventAttrEvent_Hardware(sample_type,
                                              true /*sample_id_all*/)
      .WithId(401)
      .WriteTo(&input);

  // PERF_RECORD_SAMPLE
  const sample_event written_sample_event = {
      .header = {
          .type = PERF_RECORD_SAMPLE,
          .misc = PERF_RECORD_MISC_KERNEL,
          .size =
              sizeof(struct sample_event) + num_sample_event_bits * sizeof(u64),
      }};
  const u64 sample_event_array[] = {
      0xffffffff01234567,                    // IP
      PunU32U64{.v32 = {0x68d, 0x68e}}.v64,  // TID (u32 pid, tid)
      1415837014 * 1000000000ULL,            // TIME
      0x00007f999c38d15a,                    // ADDR
      401,                                   // ID
      1,                                     // STREAM_ID
      8,                                     // CPU
      10001,                                 // PERIOD
  };
  ASSERT_EQ(written_sample_event.header.size,
            sizeof(written_sample_event.header) + sizeof(sample_event_array));
  input.write(reinterpret_cast<const char*>(&written_sample_event),
              sizeof(written_sample_event));
  input.write(reinterpret_cast<const char*>(sample_event_array),
              sizeof(sample_event_array));

  // PERF_RECORD_MMAP
  ASSERT_EQ(40, offsetof(struct mmap_event, filename));
  // clang-format off
  const size_t mmap_event_size =
      offsetof(struct mmap_event, filename) + 10 +
      6 /* ==16, nearest 64-bit boundary for filename */ +
      num_sample_id_bits * sizeof(u64);
  // clang-format on
  struct mmap_event written_mmap_event = {
      .header =
          {
              .type = PERF_RECORD_MMAP,
              .misc = 0,
              .size = mmap_event_size,
          },
      .pid = 0x68d,
      .tid = 0x68d,
      .start = 0x1d000,
      .len = 0x1000,
      .pgoff = 0,
      // .filename = ..., // written separately
  };
  const char mmap_filename[10 + 6] = "/dev/zero";
  const u64 mmap_sample_id[] = {
      PunU32U64{.v32 = {0x68d, 0x68e}}.v64,  // TID (u32 pid, tid)
      1415911367 * 1000000000ULL,            // TIME
      401,                                   // ID
      2,                                     // STREAM_ID
      9,                                     // CPU
  };
  const size_t pre_mmap_offset = input.tellp();
  input.write(reinterpret_cast<const char*>(&written_mmap_event),
              offsetof(struct mmap_event, filename));
  input.write(mmap_filename, 10 + 6);
  input.write(reinterpret_cast<const char*>(mmap_sample_id),
              sizeof(mmap_sample_id));
  const size_t written_mmap_size =
      static_cast<size_t>(input.tellp()) - pre_mmap_offset;
  ASSERT_EQ(written_mmap_event.header.size,
            static_cast<u64>(written_mmap_size));

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(2, pr->events().size());

    {
      const PerfEvent& event = pr->events().Get(0);
      EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

      const SampleEvent& sample = event.sample_event();
      EXPECT_EQ(0xffffffff01234567, sample.ip());
      EXPECT_EQ(0x68d, sample.pid());
      EXPECT_EQ(0x68e, sample.tid());
      EXPECT_EQ(1415837014 * 1000000000ULL, sample.sample_time_ns());
      EXPECT_EQ(0x00007f999c38d15a, sample.addr());
      EXPECT_EQ(401, sample.id());
      EXPECT_EQ(1, sample.stream_id());
      EXPECT_EQ(8, sample.cpu());
      EXPECT_EQ(10001, sample.period());
    }

    {
      const PerfEvent& event = pr->events().Get(1);
      EXPECT_EQ(PERF_RECORD_MMAP, event.header().type());

      const SampleInfo& sample = event.mmap_event().sample_info();
      EXPECT_EQ(0x68d, sample.pid());
      EXPECT_EQ(0x68e, sample.tid());
      EXPECT_EQ(1415911367 * 1000000000ULL, sample.sample_time_ns());
      EXPECT_EQ(401, sample.id());
      EXPECT_EQ(2, sample.stream_id());
      EXPECT_EQ(9, sample.cpu());
    }
  }
}

// Test that PERF_SAMPLE_IDENTIFIER is parsed correctly. This field
// is in a different place in PERF_RECORD_SAMPLE events compared to the
// struct sample_id placed at the end of all other events.
TEST(PerfReaderTest, ReadsAndWritesPerfSampleIdentifier) {
  std::stringstream input;

  // header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(
      PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP | PERF_SAMPLE_TID,
      true /*sample_id_all*/)
      .WithIds({0xdeadbeef, 0xf00dbaad})
      .WriteTo(&input);

  // PERF_RECORD_SAMPLE
  const sample_event written_sample_event = {
      .header = {
          .type = PERF_RECORD_SAMPLE,
          .misc = PERF_RECORD_MISC_KERNEL,
          .size = sizeof(struct sample_event) + 3 * sizeof(u64),
      }};
  const u64 sample_event_array[] = {
      0x00000000deadbeef,  // IDENTIFIER
      0x00007f999c38d15a,  // IP
      0x0000068d0000068d,  // TID (u32 pid, tid)
  };
  ASSERT_EQ(written_sample_event.header.size,
            sizeof(written_sample_event.header) + sizeof(sample_event_array));
  input.write(reinterpret_cast<const char*>(&written_sample_event),
              sizeof(written_sample_event));
  input.write(reinterpret_cast<const char*>(sample_event_array),
              sizeof(sample_event_array));

  // PERF_RECORD_MMAP
  ASSERT_EQ(40, offsetof(struct mmap_event, filename));
  // clang-format off
  const size_t mmap_event_size =
      offsetof(struct mmap_event, filename) + 10 +
      6 /* ==16, nearest 64-bit boundary for filename */ + 2 * sizeof(u64);
  // clang-format on
  struct mmap_event written_mmap_event = {
      .header =
          {
              .type = PERF_RECORD_MMAP,
              .misc = 0,
              .size = mmap_event_size,
          },
      .pid = 0x68d,
      .tid = 0x68d,
      .start = 0x1d000,
      .len = 0x1000,
      .pgoff = 0,
      // .filename = ..., // written separately
  };
  const char mmap_filename[10 + 6] = "/dev/zero";
  const u64 mmap_sample_id[] = {
      // NB: PERF_SAMPLE_IP is not part of sample_id
      0x0000068d0000068d,  // TID (u32 pid, tid)
      0x00000000f00dbaad,  // IDENTIFIER
  };
  const size_t pre_mmap_offset = input.tellp();
  input.write(reinterpret_cast<const char*>(&written_mmap_event),
              offsetof(struct mmap_event, filename));
  input.write(mmap_filename, 10 + 6);
  input.write(reinterpret_cast<const char*>(mmap_sample_id),
              sizeof(mmap_sample_id));
  const size_t written_mmap_size =
      static_cast<size_t>(input.tellp()) - pre_mmap_offset;
  ASSERT_EQ(written_mmap_event.header.size,
            static_cast<u64>(written_mmap_size));

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(2, pr->events().size());

    const PerfEvent& ip_event = pr->events().Get(0);
    EXPECT_EQ(PERF_RECORD_SAMPLE, ip_event.header().type());
    EXPECT_EQ(0xdeadbeefULL, ip_event.sample_event().id());

    const PerfEvent& mmap_event = pr->events().Get(1);
    EXPECT_EQ(PERF_RECORD_MMAP, mmap_event.header().type());
    EXPECT_EQ(0xf00dbaadULL, mmap_event.mmap_event().sample_info().id());
  }
}

TEST(PerfReaderTest, ReadsAndWritesMmap2Events) {
  std::stringstream input;

  // header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP,
                                              false /*sample_id_all*/)
      .WriteTo(&input);

  // PERF_RECORD_MMAP2
  ASSERT_EQ(72, offsetof(struct mmap2_event, filename));
  // clang-format off
  const size_t mmap_event_size =
      offsetof(struct mmap2_event, filename) + 10 +
      6; /* ==16, nearest 64-bit boundary for filename */
  // clang-format on
  struct mmap2_event written_mmap_event = {
      .header =
          {
              .type = PERF_RECORD_MMAP2,
              .misc = 0,
              .size = mmap_event_size,
          },
      .pid = 0x68d,
      .tid = 0x68d,
      .start = 0x1d000,
      .len = 0x1000,
      .pgoff = 0x2000,
      .maj = 6,
      .min = 7,
      .ino = 8,
      .ino_generation = 9,
      .prot = 1 | 2,  // == PROT_READ | PROT_WRITE
      .flags = 2,     // == MAP_PRIVATE
                      // .filename = ..., // written separately
  };
  const char mmap_filename[10 + 6] = "/dev/zero";
  const size_t pre_mmap_offset = input.tellp();
  input.write(reinterpret_cast<const char*>(&written_mmap_event),
              offsetof(struct mmap2_event, filename));
  input.write(mmap_filename, 10 + 6);
  const size_t written_mmap_size =
      static_cast<size_t>(input.tellp()) - pre_mmap_offset;
  ASSERT_EQ(written_mmap_event.header.size,
            static_cast<u64>(written_mmap_size));

  //
  // Parse input.
  //

  PerfReader pr1;
  ASSERT_TRUE(pr1.ReadFromString(input.str()));
  // Write it out and read it in again, the two should have the same data.
  std::vector<char> output_perf_data;
  ASSERT_TRUE(pr1.WriteToVector(&output_perf_data));
  PerfReader pr2;
  ASSERT_TRUE(pr2.ReadFromVector(output_perf_data));

  // Test both versions:
  for (PerfReader* pr : {&pr1, &pr2}) {
    // PERF_RECORD_HEADER_ATTR is added to attr(), not events().
    EXPECT_EQ(1, pr->events().size());

    const PerfEvent& event = pr->events().Get(0);
    EXPECT_EQ(PERF_RECORD_MMAP2, event.header().type());
    EXPECT_EQ(0x68d, event.mmap_event().pid());
    EXPECT_EQ(0x68d, event.mmap_event().tid());
    EXPECT_EQ(0x1d000, event.mmap_event().start());
    EXPECT_EQ(0x1000, event.mmap_event().len());
    EXPECT_EQ(0x2000, event.mmap_event().pgoff());
    EXPECT_EQ(6, event.mmap_event().maj());
    EXPECT_EQ(7, event.mmap_event().min());
    EXPECT_EQ(8, event.mmap_event().ino());
    EXPECT_EQ(9, event.mmap_event().ino_generation());
    EXPECT_EQ(1 | 2, event.mmap_event().prot());
    EXPECT_EQ(2, event.mmap_event().flags());
  }
}

// Regression test for http://crbug.com/493533
TEST(PerfReaderTest, ReadsAllAvailableMetadataTypes) {
  std::stringstream input;

  const uint32_t features = (1 << HEADER_HOSTNAME) | (1 << HEADER_OSRELEASE) |
                            (1 << HEADER_VERSION) | (1 << HEADER_ARCH) |
                            (1 << HEADER_LAST_FEATURE);

  // header
  testing::ExamplePerfDataFileHeader file_header(features);
  file_header.WriteTo(&input);

  // metadata

  size_t metadata_offset =
      file_header.data_end() + 5 * sizeof(perf_file_section);

  // HEADER_HOSTNAME
  testing::ExampleStringMetadata hostname_metadata("hostname", metadata_offset);
  metadata_offset += hostname_metadata.size();

  // HEADER_OSRELEASE
  testing::ExampleStringMetadata osrelease_metadata("osrelease",
                                                    metadata_offset);
  metadata_offset += osrelease_metadata.size();

  // HEADER_VERSION
  testing::ExampleStringMetadata version_metadata("version", metadata_offset);
  metadata_offset += version_metadata.size();

  // HEADER_ARCH
  testing::ExampleStringMetadata arch_metadata("arch", metadata_offset);
  metadata_offset += arch_metadata.size();

  // HEADER_LAST_FEATURE -- this is just a dummy metadata that will be skipped
  // over. In practice, there will not actually be a metadata entry of type
  // HEADER_LAST_FEATURE. But use because it will never become a supported
  // metadata type.
  testing::ExampleStringMetadata last_feature("*unsupported*", metadata_offset);
  metadata_offset += last_feature.size();

  hostname_metadata.index_entry().WriteTo(&input);
  osrelease_metadata.index_entry().WriteTo(&input);
  version_metadata.index_entry().WriteTo(&input);
  arch_metadata.index_entry().WriteTo(&input);
  last_feature.index_entry().WriteTo(&input);

  hostname_metadata.WriteTo(&input);
  osrelease_metadata.WriteTo(&input);
  version_metadata.WriteTo(&input);
  arch_metadata.WriteTo(&input);
  last_feature.WriteTo(&input);

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // The dummy metadata should not have prevented the reading of the other
  // metadata.
  const auto& string_metadata = pr.string_metadata();
  EXPECT_EQ("hostname", string_metadata.hostname().value());
  EXPECT_EQ("osrelease", string_metadata.kernel_version().value());
  EXPECT_EQ("version", string_metadata.perf_version().value());
  EXPECT_EQ("arch", string_metadata.architecture().value());
}

TEST(PerfReaderTest, AttrsWithDifferentSampleTypes) {
  std::stringstream input;

  // PERF_RECORD_SAMPLE
  testing::ExamplePerfSampleEvent sample_event_1(
      testing::SampleInfo().Id(51).Ip(0x00000000002c100a).Tid(1002));
  // PERF_RECORD_SAMPLE
  testing::ExamplePerfSampleEvent sample_event_2(testing::SampleInfo()
                                                     .Id(61)
                                                     .Ip(0x00000000002c100b)
                                                     .Tid(1002)
                                                     .Time(1000006));
  // PERF_RECORD_SAMPLE
  testing::ExamplePerfSampleEvent sample_event_3(
      testing::SampleInfo().Id(52).Ip(0x00000000002c100c).Tid(1002));

  const size_t data_size = sample_event_1.GetSize() + sample_event_2.GetSize() +
                           sample_event_3.GetSize();
  const uint32_t features = 0;

  // header
  testing::ExamplePerfDataFileHeader file_header(features);
  file_header.WithAttrIdsCount(3).WithAttrCount(2).WithDataSize(data_size);
  file_header.WriteTo(&input);

  // attr ids
  testing::AttrIdsSection attr_ids(input.tellp());
  const auto id_section_1 = attr_ids.AddIds({51, 52});
  const auto id_section_2 = attr_ids.AddIds({61});
  attr_ids.WriteTo(&input);

  // attrs
  ASSERT_EQ(file_header.header().attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(
      PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP | PERF_SAMPLE_TID,
      true /*sample_id_all*/)
      .WithIds(id_section_1)
      .WriteTo(&input);
  testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IDENTIFIER |
                                            PERF_SAMPLE_IP | PERF_SAMPLE_TID |
                                            PERF_SAMPLE_TIME,
                                        true /*sample_id_all*/)
      .WithIds(id_section_2)
      .WriteTo(&input);

  // data

  ASSERT_EQ(file_header.header().data.offset, static_cast<u64>(input.tellp()));
  sample_event_1.WriteTo(&input);
  sample_event_2.WriteTo(&input);
  sample_event_3.WriteTo(&input);
  ASSERT_EQ(file_header.header().data.offset + data_size,
            static_cast<u64>(input.tellp()));

  // no metadata

  //
  // Parse input.
  //
  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr ids were read correctly.
  ASSERT_EQ(2, pr.attrs().size());
  ASSERT_EQ(2, pr.attrs().Get(0).ids().size());
  EXPECT_EQ(51, pr.attrs().Get(0).ids(0));
  EXPECT_EQ(52, pr.attrs().Get(0).ids(1));
  ASSERT_EQ(1, pr.attrs().Get(1).ids().size());
  EXPECT_EQ(61, pr.attrs().Get(1).ids(0));

  // Verify events were read properly.
  ASSERT_EQ(3, pr.events().size());
  {
    const PerfEvent& event = pr.events().Get(0);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    const SampleEvent& sample = event.sample_event();
    EXPECT_EQ(51, sample.id());
    EXPECT_EQ(0x00000000002c100a, sample.ip());
    EXPECT_EQ(1002, sample.tid());
    // This event doesn't have a timestamp.
    EXPECT_FALSE(sample.has_sample_time_ns());
  }
  {
    const PerfEvent& event = pr.events().Get(1);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    const SampleEvent& sample = event.sample_event();
    EXPECT_EQ(61, sample.id());
    EXPECT_EQ(0x00000000002c100b, sample.ip());
    EXPECT_EQ(1002, sample.tid());
    EXPECT_EQ(1000006, sample.sample_time_ns());
  }
  {
    const PerfEvent& event = pr.events().Get(2);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    const SampleEvent& sample = event.sample_event();
    EXPECT_EQ(52, sample.id());
    EXPECT_EQ(0x00000000002c100c, sample.ip());
    EXPECT_EQ(1002, sample.tid());
    // This event doesn't have a timestamp.
    EXPECT_FALSE(sample.has_sample_time_ns());
  }
}

// Neither PERF_SAMPLE_ID nor PERF_SAMPLE_IDENTIFIER are set. We should
// fall back to using the first attr when looking for the sample_format.
TEST(PerfReaderTest, NoSampleIdField) {
  std::stringstream input;

  // PERF_RECORD_SAMPLE
  testing::ExamplePerfSampleEvent sample_event(
      testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002));

  const size_t data_size = sample_event.GetSize();
  const uint32_t features = 0;

  // header
  testing::ExamplePerfDataFileHeader file_header(features);
  file_header.WithAttrCount(1).WithDataSize(data_size);
  file_header.WriteTo(&input);

  // attrs
  ASSERT_EQ(file_header.header().attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                        false /*sample_id_all*/)
      .WithConfig(456)
      .WriteTo(&input);

  // data

  ASSERT_EQ(file_header.header().data.offset, static_cast<u64>(input.tellp()));
  sample_event.WriteTo(&input);
  ASSERT_EQ(file_header.header().data.offset + data_size,
            static_cast<u64>(input.tellp()));

  // no metadata

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  ASSERT_EQ(1, pr.attrs().size());
  EXPECT_EQ(456, pr.attrs().Get(0).attr().config());

  // Verify subsequent sample event was read properly.
  ASSERT_EQ(1, pr.events().size());
  const PerfEvent& event = pr.events().Get(0);
  EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());
  EXPECT_EQ(data_size, event.header().size());

  EXPECT_EQ(0x00000000002c100a, event.sample_event().ip());
  EXPECT_EQ(1002, event.sample_event().tid());
}

// When sample_id_all == false, non-sample events should not look for sample_id.
TEST(PerfReaderTest, SampleIdFalseMeansDontReadASampleId) {
  std::stringstream input;

  // PERF_RECORD_SAMPLE
  testing::ExamplePerfSampleEvent sample_event(
      testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002).Id(48));

  // PERF_RECORD_MMAP
  testing::ExampleMmapEvent mmap_event(
      1001, 0x1c1000, 0x1000, 0, "/usr/lib/foo.so",
      // Write a sample_info even though we shouldn't (sample_id_all==false)
      // Use a bogus ID: if we look at the sample_id and look for an attr, it
      // should return an error.
      testing::SampleInfo().Tid(1001).Id(666));

  const size_t data_size = sample_event.GetSize() + mmap_event.GetSize();
  const uint32_t features = 0;

  // header
  testing::ExamplePerfDataFileHeader file_header(features);
  file_header.WithAttrIdsCount(1).WithAttrCount(1).WithDataSize(data_size);
  file_header.WriteTo(&input);

  // attr IDs
  testing::AttrIdsSection attr_ids(input.tellp());
  const auto id_section = attr_ids.AddId(48);
  attr_ids.WriteTo(&input);

  // attrs
  ASSERT_EQ(file_header.header().attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(
      PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_ID,
      false /*sample_id_all*/)
      .WithIds(id_section)
      .WriteTo(&input);

  // data

  ASSERT_EQ(file_header.header().data.offset, static_cast<u64>(input.tellp()));
  sample_event.WriteTo(&input);
  mmap_event.WriteTo(&input);
  ASSERT_EQ(file_header.header().data.offset + data_size,
            static_cast<u64>(input.tellp()));

  // no metadata

  //
  // Parse input.
  //

  PerfReader pr;
  EXPECT_TRUE(pr.ReadFromString(input.str()));

  // Verify events were read properly.
  ASSERT_EQ(2, pr.events().size());
  {
    const PerfEvent& event = pr.events().Get(0);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    EXPECT_EQ(0x00000000002c100a, event.sample_event().ip());
    EXPECT_EQ(1002, event.sample_event().tid());
    EXPECT_EQ(48, event.sample_event().id());
  }

  {
    const PerfEvent& event = pr.events().Get(1);
    EXPECT_EQ(PERF_RECORD_MMAP, event.header().type());

    // This event is malformed: it has junk at the end. Therefore, sample_info
    // should not have TID or ID fields.
    EXPECT_FALSE(event.mmap_event().sample_info().has_tid());
    EXPECT_FALSE(event.mmap_event().sample_info().has_id());
  }
}

// Regression test for http://crbug.com/496441
TEST(PerfReaderTest, LargePerfEventAttr) {
  std::stringstream input;

  const size_t attr_size = sizeof(perf_event_attr) + sizeof(u64);
  testing::ExamplePerfSampleEvent sample_event(
      testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002));
  const size_t data_size = sample_event.GetSize();

  // header
  testing::ExamplePerfDataFileHeader file_header(0);
  file_header.WithCustomPerfEventAttrSize(attr_size)
      .WithAttrCount(1)
      .WithDataSize(data_size);
  file_header.WriteTo(&input);

  // attrs
  ASSERT_EQ(file_header.header().attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                        false /*sample_id_all*/)
      .WithAttrSize(attr_size)
      .WithConfig(456)
      .WriteTo(&input);

  // data

  ASSERT_EQ(file_header.header().data.offset, static_cast<u64>(input.tellp()));
  sample_event.WriteTo(&input);
  ASSERT_EQ(file_header.header().data.offset + data_size,
            static_cast<u64>(input.tellp()));

  // no metadata

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  EXPECT_EQ(1, pr.attrs().size());
  EXPECT_EQ(456, pr.attrs().Get(0).attr().config());

  // Verify subsequent sample event was read properly.
  ASSERT_EQ(1, pr.events().size());

  const PerfEvent& event = pr.events().Get(0);
  EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());
  EXPECT_EQ(data_size, event.header().size());

  const SampleEvent& sample_info = event.sample_event();
  EXPECT_EQ(0x00000000002c100a, sample_info.ip());
  EXPECT_EQ(1002, sample_info.tid());
}

// Regression test for http://crbug.com/496441
TEST(PerfReaderTest, LargePerfEventAttrPiped) {
  std::stringstream input;

  // pipe header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                              true /*sample_id_all*/)
      .WithAttrSize(sizeof(perf_event_attr) + sizeof(u64))
      .WithConfig(123)
      .WriteTo(&input);

  // PERF_RECORD_HEADER_EVENT_TYPE
  const struct event_type_event event_type = {
      .header =
          {
              .type = PERF_RECORD_HEADER_EVENT_TYPE,
              .misc = 0,
              .size = sizeof(struct event_type_event),
          },
      .event_type = {
          /*event_id*/ 123,
          /*name*/ "cycles",
      },
  };
  input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type));

  testing::ExamplePerfSampleEvent sample_event(
      testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002));
  sample_event.WriteTo(&input);

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  EXPECT_EQ(1, pr.attrs().size());
  EXPECT_EQ(123, pr.attrs().Get(0).attr().config());
  ASSERT_EQ(1, pr.event_types().size());
  EXPECT_EQ("cycles", pr.event_types().Get(0).name());

  // Verify subsequent sample event was read properly.
  ASSERT_EQ(1, pr.events().size());

  const PerfEvent& event = pr.events().Get(0);
  EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());
  EXPECT_EQ(sample_event.GetSize(), event.header().size());

  const SampleEvent& sample_info = event.sample_event();
  EXPECT_EQ(0x00000000002c100a, sample_info.ip());
  EXPECT_EQ(1002, sample_info.tid());
}

// Regression test for http://crbug.com/496441
TEST(PerfReaderTest, SmallPerfEventAttr) {
  std::stringstream input;

  const size_t attr_size = sizeof(perf_event_attr) - sizeof(u64);
  testing::ExamplePerfSampleEvent sample_event(
      testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002));
  const size_t data_size = sample_event.GetSize();

  // header
  testing::ExamplePerfDataFileHeader file_header(0);
  file_header.WithCustomPerfEventAttrSize(attr_size)
      .WithAttrCount(1)
      .WithDataSize(data_size);
  file_header.WriteTo(&input);

  // attrs
  CHECK_EQ(file_header.header().attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                        false /*sample_id_all*/)
      .WithAttrSize(attr_size)
      .WithConfig(456)
      .WriteTo(&input);

  // data

  ASSERT_EQ(file_header.header().data.offset, static_cast<u64>(input.tellp()));
  sample_event.WriteTo(&input);
  ASSERT_EQ(file_header.header().data.offset + data_size,
            static_cast<u64>(input.tellp()));

  // no metadata

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  EXPECT_EQ(1, pr.attrs().size());
  EXPECT_EQ(456, pr.attrs().Get(0).attr().config());

  // Verify subsequent sample event was read properly.
  ASSERT_EQ(1, pr.events().size());

  const PerfEvent& event = pr.events().Get(0);
  EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());
  EXPECT_EQ(data_size, event.header().size());

  const SampleEvent& sample_info = event.sample_event();
  EXPECT_EQ(0x00000000002c100a, sample_info.ip());
  EXPECT_EQ(1002, sample_info.tid());
}

// Regression test for http://crbug.com/496441
TEST(PerfReaderTest, SmallPerfEventAttrPiped) {
  std::stringstream input;

  // pipe header
  testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);

  // data

  // PERF_RECORD_HEADER_ATTR
  testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                              true /*sample_id_all*/)
      .WithAttrSize(sizeof(perf_event_attr) - sizeof(u64))
      .WithConfig(123)
      .WriteTo(&input);

  // PERF_RECORD_HEADER_EVENT_TYPE
  const struct event_type_event event_type = {
      .header =
          {
              .type = PERF_RECORD_HEADER_EVENT_TYPE,
              .misc = 0,
              .size = sizeof(struct event_type_event),
          },
      .event_type = {
          /*event_id*/ 123,
          /*name*/ "cycles",
      },
  };
  input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type));

  testing::ExamplePerfSampleEvent sample_event(
      testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002));
  sample_event.WriteTo(&input);

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  EXPECT_EQ(1, pr.attrs().size());
  EXPECT_EQ(123, pr.attrs().Get(0).attr().config());
  ASSERT_EQ(1, pr.event_types().size());
  EXPECT_EQ("cycles", pr.event_types().Get(0).name());

  // Verify subsequent sample event was read properly.
  ASSERT_EQ(1, pr.events().size());

  const PerfEvent& event = pr.events().Get(0);
  EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());
  EXPECT_EQ(sample_event.GetSize(), event.header().size());

  const SampleEvent& sample_info = event.sample_event();
  EXPECT_EQ(0x00000000002c100a, sample_info.ip());
  EXPECT_EQ(1002, sample_info.tid());
}

TEST(PerfReaderTest, CrossEndianAttrs) {
  for (bool is_cross_endian : {true, false}) {
    LOG(INFO) << "Testing with cross endianness = " << is_cross_endian;

    std::stringstream input;

    // header
    const uint32_t features = 0;
    testing::ExamplePerfDataFileHeader file_header(features);
    file_header.WithAttrCount(3)
        .WithCrossEndianness(is_cross_endian)
        .WriteTo(&input);

    // attrs
    CHECK_EQ(file_header.header().attrs.offset,
             static_cast<u64>(input.tellp()));
    // Provide two attrs with different sample_id_all values to test the
    // correctness of byte swapping of the bit fields.
    testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                          true /*sample_id_all*/)
        .WithConfig(123)
        .WithCrossEndianness(is_cross_endian)
        .WriteTo(&input);
    testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                          true /*sample_id_all*/)
        .WithConfig(456)
        .WithCrossEndianness(is_cross_endian)
        .WriteTo(&input);
    testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                          false /*sample_id_all*/)
        .WithConfig(456)
        .WithCrossEndianness(is_cross_endian)
        .WriteTo(&input);

    // No data.
    // No metadata.

    // Read data.

    PerfReader pr;
    ASSERT_TRUE(pr.ReadFromString(input.str()));

    // Make sure the attr was recorded properly.
    EXPECT_EQ(3, pr.attrs().size());

    const auto& attr0 = pr.attrs().Get(0).attr();
    EXPECT_EQ(123, attr0.config());
    EXPECT_EQ(1, attr0.sample_period());
    EXPECT_EQ(PERF_SAMPLE_IP | PERF_SAMPLE_TID, attr0.sample_type());
    EXPECT_TRUE(attr0.sample_id_all());
    EXPECT_EQ(2, attr0.precise_ip());

    const auto& attr1 = pr.attrs().Get(1).attr();
    EXPECT_EQ(456, attr1.config());
    EXPECT_EQ(1, attr1.sample_period());
    EXPECT_EQ(PERF_SAMPLE_IP | PERF_SAMPLE_TID, attr1.sample_type());
    EXPECT_TRUE(attr1.sample_id_all());
    EXPECT_EQ(2, attr1.precise_ip());

    const auto& attr2 = pr.attrs().Get(2).attr();
    EXPECT_EQ(456, attr2.config());
    EXPECT_EQ(1, attr2.sample_period());
    EXPECT_EQ(PERF_SAMPLE_IP | PERF_SAMPLE_TID, attr2.sample_type());
    EXPECT_FALSE(attr2.sample_id_all());
    EXPECT_EQ(2, attr2.precise_ip());
  }
}

TEST(PerfReaderTest, CrossEndianNormalPerfData) {
  // data
  // Do this before header to compute the total data size.
  std::stringstream input_data;
  testing::ExampleMmapEvent(
      1234, 0x0000000000810000, 0x10000, 0x2000, "/usr/lib/foo.so",
      testing::SampleInfo().Tid(bswap_32(1234), bswap_32(1235)))
      .WithCrossEndianness(true)
      .WriteTo(&input_data);
  testing::ExampleForkEvent(
      1236, 1234, 1237, 1235, 30ULL * 1000000000,
      testing::SampleInfo().Tid(bswap_32(1236), bswap_32(1237)))
      .WithCrossEndianness(true)
      .WriteTo(&input_data);
  testing::ExamplePerfSampleEvent(testing::SampleInfo()
                                      .Ip(bswap_64(0x0000000000810100))
                                      .Tid(bswap_32(1234), bswap_32(1235)))
      .WithCrossEndianness(true)
      .WriteTo(&input_data);
  testing::ExamplePerfSampleEvent(testing::SampleInfo()
                                      .Ip(bswap_64(0x000000000081ff00))
                                      .Tid(bswap_32(1236), bswap_32(1237)))
      .WithCrossEndianness(true)
      .WriteTo(&input_data);

  std::stringstream input;

  // header
  const size_t data_size = input_data.str().size();
  const uint32_t features = (1 << HEADER_HOSTNAME) | (1 << HEADER_OSRELEASE);
  testing::ExamplePerfDataFileHeader file_header(features);
  file_header.WithAttrCount(1)
      .WithDataSize(data_size)
      .WithCrossEndianness(true)
      .WriteTo(&input);

  // attrs
  CHECK_EQ(file_header.header().attrs.offset, static_cast<u64>(input.tellp()));
  testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
                                        true /*sample_id_all*/)
      .WithConfig(456)
      .WithCrossEndianness(true)
      .WriteTo(&input);

  // Write data.

  u64 data_offset = file_header.header().data.offset;
  ASSERT_EQ(data_offset, static_cast<u64>(input.tellp()));
  input << input_data.str();
  ASSERT_EQ(data_offset + data_size, static_cast<u64>(input.tellp()));

  // metadata
  size_t metadata_offset =
      file_header.data_end() + 2 * sizeof(perf_file_section);

  // HEADER_HOSTNAME
  testing::ExampleStringMetadata hostname_metadata("hostname", metadata_offset);
  hostname_metadata.WithCrossEndianness(true);
  metadata_offset += hostname_metadata.size();

  // HEADER_OSRELEASE
  testing::ExampleStringMetadata osrelease_metadata("osrelease",
                                                    metadata_offset);
  osrelease_metadata.WithCrossEndianness(true);
  metadata_offset += osrelease_metadata.size();

  hostname_metadata.index_entry().WriteTo(&input);
  osrelease_metadata.index_entry().WriteTo(&input);

  hostname_metadata.WriteTo(&input);
  osrelease_metadata.WriteTo(&input);

  //
  // Parse input.
  //

  PerfReader pr;
  ASSERT_TRUE(pr.ReadFromString(input.str()));

  // Make sure the attr was recorded properly.
  EXPECT_EQ(1, pr.attrs().size());
  EXPECT_EQ(456, pr.attrs().Get(0).attr().config());
  EXPECT_TRUE(pr.attrs().Get(0).attr().sample_id_all());

  // Verify perf events.
  ASSERT_EQ(4, pr.events().size());

  {
    const PerfEvent& event = pr.events().Get(0);
    EXPECT_EQ(PERF_RECORD_MMAP, event.header().type());
    EXPECT_EQ(1234, event.mmap_event().pid());
    EXPECT_EQ(1234, event.mmap_event().tid());
    EXPECT_EQ(string("/usr/lib/foo.so"), event.mmap_event().filename());
    EXPECT_EQ(0x0000000000810000, event.mmap_event().start());
    EXPECT_EQ(0x10000, event.mmap_event().len());
    EXPECT_EQ(0x2000, event.mmap_event().pgoff());
  }

  {
    const PerfEvent& event = pr.events().Get(1);
    EXPECT_EQ(PERF_RECORD_FORK, event.header().type());
    EXPECT_EQ(1236, event.fork_event().pid());
    EXPECT_EQ(1234, event.fork_event().ppid());
    EXPECT_EQ(1237, event.fork_event().tid());
    EXPECT_EQ(1235, event.fork_event().ptid());
    EXPECT_EQ(30ULL * 1000000000, event.fork_event().fork_time_ns());
  }

  {
    const PerfEvent& event = pr.events().Get(2);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    const SampleEvent& sample_info = event.sample_event();
    EXPECT_EQ(0x0000000000810100, sample_info.ip());
    EXPECT_EQ(1234, sample_info.pid());
    EXPECT_EQ(1235, sample_info.tid());
  }

  {
    const PerfEvent& event = pr.events().Get(3);
    EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type());

    const SampleEvent& sample_info = event.sample_event();
    EXPECT_EQ(0x000000000081ff00, sample_info.ip());
    EXPECT_EQ(1236, sample_info.pid());
    EXPECT_EQ(1237, sample_info.tid());
  }
}

TEST(PerfReaderTest, MetadataMaskInitialized) {
  // The metadata mask is actually an array of uint64's. The accessors/mutator
  // in PerfReader depend on it being initialized.
  PerfReader reader;
  ASSERT_EQ(1U, reader.proto().metadata_mask().size());
  EXPECT_EQ(0U, reader.metadata_mask());
}

}  // namespace quipper