// 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 "perf_reader.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#include <algorithm>
#include <vector>

#include "base/logging.h"
#include "base/macros.h"

#include "binary_data_utils.h"
#include "buffer_reader.h"
#include "buffer_writer.h"
#include "compat/string.h"
#include "file_reader.h"
#include "file_utils.h"
#include "perf_data_structures.h"
#include "perf_data_utils.h"
#include "sample_info_reader.h"

namespace quipper {

using PerfEvent = PerfDataProto_PerfEvent;
using SampleInfo = PerfDataProto_SampleInfo;
using StringAndMd5sumPrefix =
    PerfDataProto_StringMetadata_StringAndMd5sumPrefix;

namespace {

// The type of the storage size of a string, prefixed before each string field
// in raw data.
typedef u32 string_size_type;

// The type of the number of string data, found in the command line metadata in
// the perf data file.
typedef u32 num_string_data_type;

// Types of the event desc fields that are not found in other structs.
typedef u32 event_desc_num_events;
typedef u32 event_desc_attr_size;
typedef u32 event_desc_num_unique_ids;

// The type of the number of nodes field in NUMA topology.
typedef u32 numa_topology_num_nodes_type;

// The type of the number of mappings in pmu mappings.
typedef u32 pmu_mappings_num_mappings_type;

// The type of the number of groups in group desc.
typedef u32 group_desc_num_groups_type;

// A mask that is applied to |metadata_mask()| in order to get a mask for
// only the metadata supported by quipper.
const uint32_t kSupportedMetadataMask =
    1 << HEADER_TRACING_DATA | 1 << HEADER_BUILD_ID | 1 << HEADER_HOSTNAME |
    1 << HEADER_OSRELEASE | 1 << HEADER_VERSION | 1 << HEADER_ARCH |
    1 << HEADER_NRCPUS | 1 << HEADER_CPUDESC | 1 << HEADER_CPUID |
    1 << HEADER_TOTAL_MEM | 1 << HEADER_CMDLINE | 1 << HEADER_EVENT_DESC |
    1 << HEADER_CPU_TOPOLOGY | 1 << HEADER_NUMA_TOPOLOGY |
    1 << HEADER_BRANCH_STACK | 1 << HEADER_PMU_MAPPINGS |
    1 << HEADER_GROUP_DESC;

// By default, the build ID event has PID = -1.
const uint32_t kDefaultBuildIDEventPid = static_cast<uint32_t>(-1);

// Eight bits in a byte.
size_t BytesToBits(size_t num_bytes) { return num_bytes * 8; }

u8 ReverseByte(u8 x) {
  x = (x & 0xf0) >> 4 | (x & 0x0f) << 4;  // exchange nibbles
  x = (x & 0xcc) >> 2 | (x & 0x33) << 2;  // exchange pairs
  x = (x & 0xaa) >> 1 | (x & 0x55) << 1;  // exchange neighbors
  return x;
}

// If field points to the start of a bitfield padded to len bytes, this
// performs an endian swap of the bitfield, assuming the compiler that produced
// it conforms to the same ABI (bitfield layout is not completely specified by
// the language).
void SwapBitfieldOfBits(u8* field, size_t len) {
  for (size_t i = 0; i < len; i++) {
    field[i] = ReverseByte(field[i]);
  }
}

// The code currently assumes that the compiler will not add any padding to the
// various structs.  These CHECKs make sure that this is true.
void CheckNoEventHeaderPadding() {
  perf_event_header header;
  CHECK_EQ(sizeof(header),
           sizeof(header.type) + sizeof(header.misc) + sizeof(header.size));
}

void CheckNoPerfEventAttrPadding() {
  perf_event_attr attr;
  CHECK_EQ(sizeof(attr), (reinterpret_cast<u64>(&attr.__reserved_2) -
                          reinterpret_cast<u64>(&attr)) +
                             sizeof(attr.__reserved_2));
}

void CheckNoEventTypePadding() {
  perf_trace_event_type event_type;
  CHECK_EQ(sizeof(event_type),
           sizeof(event_type.event_id) + sizeof(event_type.name));
}

void CheckNoBuildIDEventPadding() {
  build_id_event event;
  CHECK_EQ(sizeof(event), sizeof(event.header.type) +
                              sizeof(event.header.misc) +
                              sizeof(event.header.size) + sizeof(event.pid) +
                              sizeof(event.build_id));
}

// Creates a new build ID event with the given build ID string, filename, and
// misc value.
malloced_unique_ptr<build_id_event> CreateBuildIDEvent(const string& build_id,
                                                       const string& filename,
                                                       uint16_t misc) {
  size_t filename_len = GetUint64AlignedStringLength(filename);
  size_t size = sizeof(struct build_id_event) + filename_len;
  malloced_unique_ptr<build_id_event> event(CallocMemoryForBuildID(size));
  event->header.type = HEADER_BUILD_ID;
  event->header.misc = misc;
  event->header.size = size;

  event->pid = kDefaultBuildIDEventPid;
  snprintf(event->filename, filename_len, "%s", filename.c_str());
  memset(event->filename + filename.size(), 0, filename_len - filename.size());

  HexStringToRawData(build_id, event->build_id, sizeof(event->build_id));
  return event;
}

// Given a string, returns the total size required to store the string in perf
// data, including a preceding length field and extra padding to align the
// string + null terminator to a multiple of uint64s.
size_t ExpectedStorageSizeOf(const string& str) {
  return sizeof(string_size_type) + GetUint64AlignedStringLength(str);
}

// Reads a perf_event_header from |data| and performs byte swapping if
// necessary. Returns true on success, or false if there was an error.
bool ReadPerfEventHeader(DataReader* data, struct perf_event_header* header) {
  if (!data->ReadData(sizeof(struct perf_event_header), header)) {
    LOG(ERROR) << "Error reading perf event header.";
    return false;
  }
  if (data->is_cross_endian()) {
    ByteSwap(&header->type);
    ByteSwap(&header->size);
    ByteSwap(&header->misc);
  }
  return true;
}

// Reads a perf_file_section from |data| and performs byte swapping if
// necessary. Returns true on success, or false if there was an error.
bool ReadPerfFileSection(DataReader* data, struct perf_file_section* section) {
  if (!data->ReadData(sizeof(*section), section)) {
    LOG(ERROR) << "Error reading perf file section info.";
    return false;
  }
  if (data->is_cross_endian()) {
    ByteSwap(&section->offset);
    ByteSwap(&section->size);
  }
  return true;
}

// Returns true if |e1| has an earlier timestamp than |e2|. Used to sort an
// array of events.
static bool CompareEventTimes(const PerfEvent* e1, const PerfEvent* e2) {
  return e1->timestamp() < e2->timestamp();
}

}  // namespace

PerfReader::PerfReader()
    : proto_(Arena::CreateMessage<PerfDataProto>(&arena_)),
      is_cross_endian_(false) {
  // The metadata mask is stored in |proto_|. It should be initialized to 0
  // since it is used heavily.
  proto_->add_metadata_mask(0);
}

PerfReader::~PerfReader() {}

bool PerfReader::Serialize(PerfDataProto* perf_data_proto) const {
  perf_data_proto->CopyFrom(*proto_);

  // Add a timestamp_sec to the protobuf.
  struct timeval timestamp_sec;
  if (!gettimeofday(&timestamp_sec, NULL))
    perf_data_proto->set_timestamp_sec(timestamp_sec.tv_sec);
  return true;
}

bool PerfReader::Deserialize(const PerfDataProto& perf_data_proto) {
  proto_->CopyFrom(perf_data_proto);

  // Iterate through all attrs and create a SampleInfoReader for each of them.
  // This is necessary for writing the proto representation of perf data to raw
  // data.
  for (const auto& stored_attr : proto_->file_attrs()) {
    PerfFileAttr attr;
    serializer_.DeserializePerfFileAttr(stored_attr, &attr);
    serializer_.CreateSampleInfoReader(attr, false /* read_cross_endian */);
  }
  return true;
}

bool PerfReader::ReadFile(const string& filename) {
  FileReader reader(filename);
  if (!reader.IsOpen()) {
    LOG(ERROR) << "Unable to open file " << filename;
    return false;
  }
  return ReadFromData(&reader);
}

bool PerfReader::ReadFromVector(const std::vector<char>& data) {
  return ReadFromPointer(data.data(), data.size());
}

bool PerfReader::ReadFromString(const string& str) {
  return ReadFromPointer(str.data(), str.size());
}

bool PerfReader::ReadFromPointer(const char* data, size_t size) {
  BufferReader buffer(data, size);
  return ReadFromData(&buffer);
}

bool PerfReader::ReadFromData(DataReader* data) {
  if (data->size() == 0) {
    LOG(ERROR) << "Input data is empty";
    return false;
  }
  if (!ReadHeader(data)) return false;

  // Check if it is normal perf data.
  if (header_.size == sizeof(header_)) {
    DVLOG(1) << "Perf data is in normal format.";
    return ReadFileData(data);
  }

  // Otherwise it is piped data.
  if (piped_header_.size != sizeof(piped_header_)) {
    LOG(ERROR) << "Expecting piped data format, but header size "
               << piped_header_.size << " does not match expected size "
               << sizeof(piped_header_);
    return false;
  }

  return ReadPipedData(data);
}

bool PerfReader::WriteFile(const string& filename) {
  std::vector<char> data;
  return WriteToVector(&data) && BufferToFile(filename, data);
}

bool PerfReader::WriteToVector(std::vector<char>* data) {
  data->resize(GetSize());
  return WriteToPointerWithoutCheckingSize(&data->at(0), data->size());
}

bool PerfReader::WriteToString(string* str) {
  str->resize(GetSize());
  return WriteToPointerWithoutCheckingSize(&str->at(0), str->size());
}

bool PerfReader::WriteToPointer(char* buffer, size_t size) {
  size_t required_size = GetSize();
  if (size < required_size) {
    LOG(ERROR) << "Buffer is too small - buffer size is " << size
               << " and required size is " << required_size;
    return false;
  }
  return WriteToPointerWithoutCheckingSize(buffer, size);
}

bool PerfReader::WriteToPointerWithoutCheckingSize(char* buffer, size_t size) {
  BufferWriter data(buffer, size);
  struct perf_file_header header;
  GenerateHeader(&header);

  return WriteHeader(header, &data) && WriteAttrs(header, &data) &&
         WriteData(header, &data) && WriteMetadata(header, &data);
}

size_t PerfReader::GetSize() const {
  struct perf_file_header header;
  GenerateHeader(&header);

  size_t total_size = 0;
  total_size += header.size;
  total_size += header.attrs.size;
  total_size += header.event_types.size;
  total_size += header.data.size;
  // Add the ID info, whose size is not explicitly included in the header.
  for (const auto& attr : proto_->file_attrs()) {
    total_size +=
        attr.ids_size() * sizeof(decltype(PerfFileAttr::ids)::value_type);
  }

  // Additional info about metadata.  See WriteMetadata for explanation.
  total_size += GetNumSupportedMetadata() * sizeof(struct perf_file_section);

  // Add the sizes of the various metadata.
  total_size += tracing_data().size();
  total_size += GetBuildIDMetadataSize();
  total_size += GetStringMetadataSize();
  total_size += GetUint32MetadataSize();
  total_size += GetUint64MetadataSize();
  total_size += GetEventDescMetadataSize();
  total_size += GetCPUTopologyMetadataSize();
  total_size += GetNUMATopologyMetadataSize();
  total_size += GetPMUMappingsMetadataSize();
  total_size += GetGroupDescMetadataSize();
  return total_size;
}

void PerfReader::GenerateHeader(struct perf_file_header* header) const {
  // This is the order of the input perf file contents in normal mode:
  // 1. Header
  // 2. Attribute IDs (pointed to by attr.ids.offset)
  // 3. Attributes
  // 4. Event types
  // 5. Data
  // 6. Metadata

  // Compute offsets in the above order.
  CheckNoEventHeaderPadding();
  memset(header, 0, sizeof(*header));
  header->magic = kPerfMagic;
  header->size = sizeof(*header);
  header->attr_size = sizeof(perf_file_attr);
  header->attrs.size = header->attr_size * attrs().size();
  for (const PerfEvent& event : proto_->events()) {
    header->data.size += event.header().size();
    // Auxtrace event contain trace data at the end of the event. Add the size
    // of this trace data to the header.data size.
    if (event.header().type() == PERF_RECORD_AUXTRACE) {
      header->data.size += event.auxtrace_event().size();
    }
  }
  // Do not use the event_types section. Use EVENT_DESC metadata instead.
  header->event_types.size = 0;

  u64 current_offset = 0;
  current_offset += header->size;
  for (const auto& attr : proto_->file_attrs()) {
    current_offset +=
        sizeof(decltype(PerfFileAttr::ids)::value_type) * attr.ids_size();
  }
  header->attrs.offset = current_offset;
  current_offset += header->attrs.size;
  header->event_types.offset = current_offset;
  current_offset += header->event_types.size;

  header->data.offset = current_offset;

  // Construct the header feature bits.
  memset(&header->adds_features, 0, sizeof(header->adds_features));
  // The following code makes the assumption that all feature bits are in the
  // first word of |adds_features|.  If the perf data format changes and the
  // assumption is no longer valid, this CHECK will fail, at which point the
  // below code needs to be updated.  For now, sticking to that assumption keeps
  // the code simple.
  // This assumption is also used when reading metadata, so that code
  // will also have to be updated if this CHECK starts to fail.
  CHECK_LE(static_cast<size_t>(HEADER_LAST_FEATURE),
           BytesToBits(sizeof(header->adds_features[0])));
  header->adds_features[0] |= metadata_mask() & kSupportedMetadataMask;
}

bool PerfReader::InjectBuildIDs(
    const std::map<string, string>& filenames_to_build_ids) {
  set_metadata_mask_bit(HEADER_BUILD_ID);
  std::set<string> updated_filenames;
  // Inject new build ID's for existing build ID events.
  for (auto& build_id : *proto_->mutable_build_ids()) {
    auto find_result = filenames_to_build_ids.find(build_id.filename());
    if (find_result == filenames_to_build_ids.end()) continue;
    const string& build_id_string = find_result->second;
    const int kHexCharsPerByte = 2;
    std::vector<uint8_t> build_id_data(build_id_string.size() /
                                       kHexCharsPerByte);
    if (!HexStringToRawData(build_id_string, build_id_data.data(),
                            build_id_data.size())) {
      LOG(ERROR) << "Could not convert hex string to raw data: "
                 << build_id_string;
      return false;
    }
    build_id.set_build_id_hash(build_id_data.data(), build_id_data.size());

    updated_filenames.insert(build_id.filename());
  }

  // For files with no existing build ID events, create new build ID events.
  // This requires a lookup of all MMAP's to determine the |misc| field of each
  // build ID event.
  std::map<string, uint16_t> filename_to_misc;
  for (const PerfEvent& event : proto_->events()) {
    if (event.header().type() == PERF_RECORD_MMAP ||
        event.header().type() == PERF_RECORD_MMAP2) {
      filename_to_misc[event.mmap_event().filename()] = event.header().misc();
    }
  }

  std::map<string, string>::const_iterator it;
  for (it = filenames_to_build_ids.begin(); it != filenames_to_build_ids.end();
       ++it) {
    const string& filename = it->first;
    if (updated_filenames.find(filename) != updated_filenames.end()) continue;

    // Determine the misc field.
    uint16_t new_misc = PERF_RECORD_MISC_KERNEL;
    std::map<string, uint16_t>::const_iterator misc_iter =
        filename_to_misc.find(filename);
    if (misc_iter != filename_to_misc.end()) new_misc = misc_iter->second;

    string build_id = it->second;
    malloced_unique_ptr<build_id_event> event =
        CreateBuildIDEvent(build_id, filename, new_misc);
    if (!serializer_.SerializeBuildIDEvent(event, proto_->add_build_ids())) {
      LOG(ERROR) << "Could not serialize build ID event with ID " << build_id;
      return false;
    }
  }
  return true;
}

bool PerfReader::Localize(
    const std::map<string, string>& build_ids_to_filenames) {
  std::map<string, string> filename_map;
  for (auto& build_id : *proto_->mutable_build_ids()) {
    string build_id_string = RawDataToHexString(build_id.build_id_hash());
    auto find_result = build_ids_to_filenames.find(build_id_string);
    if (find_result == build_ids_to_filenames.end()) continue;
    const string& new_filename = find_result->second;
    filename_map[build_id.filename()] = new_filename;
  }

  return LocalizeUsingFilenames(filename_map);
}

bool PerfReader::LocalizeUsingFilenames(
    const std::map<string, string>& filename_map) {
  LocalizeMMapFilenames(filename_map);
  for (auto& build_id : *proto_->mutable_build_ids()) {
    auto find_result = filename_map.find(build_id.filename());
    if (find_result != filename_map.end())
      build_id.set_filename(find_result->second);
  }
  return true;
}

void PerfReader::GetFilenames(std::vector<string>* filenames) const {
  std::set<string> filename_set;
  GetFilenamesAsSet(&filename_set);
  filenames->clear();
  filenames->insert(filenames->begin(), filename_set.begin(),
                    filename_set.end());
}

void PerfReader::GetFilenamesAsSet(std::set<string>* filenames) const {
  filenames->clear();
  for (const PerfEvent& event : proto_->events()) {
    if (event.header().type() == PERF_RECORD_MMAP ||
        event.header().type() == PERF_RECORD_MMAP2) {
      filenames->insert(event.mmap_event().filename());
    }
  }
}

void PerfReader::GetFilenamesToBuildIDs(
    std::map<string, string>* filenames_to_build_ids) const {
  filenames_to_build_ids->clear();
  for (const auto& build_id : proto_->build_ids()) {
    string build_id_string = RawDataToHexString(build_id.build_id_hash());
    PerfizeBuildIDString(&build_id_string);
    (*filenames_to_build_ids)[build_id.filename()] = build_id_string;
  }
}

void PerfReader::MaybeSortEventsByTime() {
  // Events can not be sorted by time if PERF_SAMPLE_TIME is not set in
  // attr.sample_type for all attrs.
  for (const auto& attr : attrs()) {
    if (!(attr.attr().sample_type() & PERF_SAMPLE_TIME)) {
      return;
    }
  }

  // Sort the events based on timestamp.

  // This sorts the pointers in the proto-internal vector, which
  // requires no copying and less external space.
  std::stable_sort(proto_->mutable_events()->pointer_begin(),
                   proto_->mutable_events()->pointer_end(), CompareEventTimes);
}

bool PerfReader::ReadHeader(DataReader* data) {
  CheckNoEventHeaderPadding();
  // The header is the first thing to be read. Don't use SeekSet(0) because it
  // doesn't make sense for piped files. Instead, verify that the reader points
  // to the start of the data.
  CHECK_EQ(0U, data->Tell());
  if (!data->ReadUint64(&piped_header_.magic)) {
    LOG(ERROR) << "Error reading header magic number.";
    return false;
  }

  if (piped_header_.magic != kPerfMagic &&
      piped_header_.magic != bswap_64(kPerfMagic)) {
    // clang-format off
    LOG(ERROR) << "Read wrong magic. Expected: 0x" << std::hex << kPerfMagic
               << " or 0x" << std::hex << bswap_64(kPerfMagic)
               << " Got: 0x" << std::hex << piped_header_.magic;
    // clang-format on
    return false;
  }
  is_cross_endian_ = (piped_header_.magic != kPerfMagic);
  data->set_is_cross_endian(is_cross_endian_);

  if (!data->ReadUint64(&piped_header_.size)) {
    LOG(ERROR) << "Error reading header size.";
    return false;
  }

  CHECK_EQ(data->Tell(), sizeof(piped_header_));

  // Header can be a piped header.
  if (piped_header_.size == sizeof(piped_header_)) return true;

  // Read as a non-piped header.
  if (!data->ReadUint64(&header_.attr_size)) {
    LOG(ERROR) << "Error reading header::attr_size.";
    return false;
  }
  if (!ReadPerfFileSection(data, &header_.attrs) ||
      !ReadPerfFileSection(data, &header_.data) ||
      !ReadPerfFileSection(data, &header_.event_types)) {
    LOG(ERROR) << "Error reading header file section info.";
    return false;
  }

  const size_t features_size = sizeof(header_.adds_features);
  CHECK_EQ(data->Tell(), sizeof(header_) - features_size);

  if (!data->ReadData(features_size, header_.adds_features)) {
    LOG(ERROR) << "Error reading header::adds_features.";
    return false;
  }
  proto_->set_metadata_mask(0, header_.adds_features[0]);

  // Byte-swapping |adds_features| is tricky. It is defined as an array of
  // unsigned longs, which can vary between architectures. However, the overall
  // size of the array in bytes is fixed.
  //
  // According to perf's perf_file_header__read() function, the hostname feature
  // should always be set. Try byte-swapping as uint64s first and check the
  // hostname bit. If it's not set, then try swapping as uint32s. This is
  // similar to the algorithm used in perf.
  if (data->is_cross_endian()) {
    static_assert(sizeof(header_.adds_features[0]) == sizeof(uint32_t) ||
                      sizeof(header_.adds_features[0]) == sizeof(uint64_t),
                  "|header_.adds_features| must be defined as an array of "
                  "either 32-bit or 64-bit words.");

    uint64_t features64 = 0;
    // Some compilers will complain if we directly cast |header_.adds_features|
    // to a uint64_t*. Instead, determine the first uint64_t without using
    // pointer aliasing.
    if (sizeof(header_.adds_features[0]) == sizeof(uint64_t)) {
      features64 = bswap_64(header_.adds_features[0]);
    } else {
      // If the native |adds_features| is composed of 32-bit words, swap the
      // byte order of each word and then swap their positions to create a
      // 64-bit word.
      features64 = static_cast<uint64_t>(bswap_32(header_.adds_features[0]))
                   << 32;
      features64 |= bswap_32(header_.adds_features[1]);
    }
    if (features64 & (1 << HEADER_HOSTNAME)) {
      for (size_t i = 0; i < features_size / sizeof(uint64_t); ++i)
        ByteSwap(reinterpret_cast<uint64_t*>(header_.adds_features) + i);
    } else {
      for (size_t i = 0; i < features_size / sizeof(uint32_t); ++i)
        ByteSwap(reinterpret_cast<uint32_t*>(header_.adds_features) + i);
    }
  }

  return true;
}

bool PerfReader::ReadAttrsSection(DataReader* data) {
  size_t num_attrs = header_.attrs.size / header_.attr_size;
  if (header_.attrs.size % header_.attr_size != 0) {
    LOG(ERROR) << "Total size of attrs " << header_.attrs.size
               << " is not a multiple of attr size " << header_.attr_size;
  }
  data->SeekSet(header_.attrs.offset);
  for (size_t i = 0; i < num_attrs; i++) {
    if (!ReadAttr(data)) return false;
  }
  return true;
}

bool PerfReader::ReadAttr(DataReader* data) {
  PerfFileAttr attr;
  if (!ReadEventAttr(data, &attr.attr)) return false;

  perf_file_section ids;
  if (!ReadPerfFileSection(data, &ids)) return false;

  // The ID may be stored at a different location in the file than where we're
  // currently reading.
  size_t saved_offset = data->Tell();
  data->SeekSet(ids.offset);

  size_t num_ids = ids.size / sizeof(decltype(attr.ids)::value_type);
  if (!ReadUniqueIDs(data, num_ids, &attr.ids)) return false;
  data->SeekSet(saved_offset);
  AddPerfFileAttr(attr);

  return true;
}

bool PerfReader::ReadEventAttr(DataReader* data, perf_event_attr* attr) {
  CheckNoPerfEventAttrPadding();
  *attr = {0};

  static_assert(
      offsetof(struct perf_event_attr, size) == sizeof(perf_event_attr::type),
      "type and size should be the first to fields of perf_event_attr");

  if (!data->ReadUint32(&attr->type) || !data->ReadUint32(&attr->size)) {
    LOG(ERROR) << "Error reading event attr type and size.";
    return false;
  }

  // Now read the rest of the attr struct.
  const size_t attr_offset = sizeof(attr->type) + sizeof(attr->size);
  const size_t attr_readable_size =
      std::min(static_cast<size_t>(attr->size), sizeof(*attr));
  if (!data->ReadDataValue(attr_readable_size - attr_offset, "attribute",
                           reinterpret_cast<char*>(attr) + attr_offset)) {
    return false;
  }
  data->SeekSet(data->Tell() + attr->size - attr_readable_size);

  if (data->is_cross_endian()) {
    // Depending on attr->size, some of these might not have actually been
    // read. This is okay: they are zero.
    ByteSwap(&attr->type);
    ByteSwap(&attr->size);
    ByteSwap(&attr->config);
    ByteSwap(&attr->sample_period);
    ByteSwap(&attr->sample_type);
    ByteSwap(&attr->read_format);

    // NB: This will also reverse precise_ip : 2 as if it was two fields:
    auto* const bitfield_start = &attr->read_format + 1;
    SwapBitfieldOfBits(reinterpret_cast<u8*>(bitfield_start), sizeof(u64));
    // ... So swap it back:
    const auto tmp = attr->precise_ip;
    attr->precise_ip = (tmp & 0x2) >> 1 | (tmp & 0x1) << 1;

    ByteSwap(&attr->wakeup_events);  // union with wakeup_watermark
    ByteSwap(&attr->bp_type);
    ByteSwap(&attr->bp_addr);  // union with config1
    ByteSwap(&attr->bp_len);   // union with config2
    ByteSwap(&attr->branch_sample_type);
    ByteSwap(&attr->sample_regs_user);
    ByteSwap(&attr->sample_stack_user);
  }

  // The actual perf_event_attr data size might be different from the size of
  // the struct definition.  Check against perf_event_attr's |size| field.
  attr->size = sizeof(*attr);

  return true;
}

bool PerfReader::ReadUniqueIDs(DataReader* data, size_t num_ids,
                               std::vector<u64>* ids) {
  ids->resize(num_ids);
  for (u64& id : *ids) {
    if (!data->ReadUint64(&id)) {
      LOG(ERROR) << "Error reading unique ID.";
      return false;
    }
  }
  return true;
}

bool PerfReader::ReadEventTypesSection(DataReader* data) {
  int num_event_types =
      header_.event_types.size / sizeof(struct perf_trace_event_type);
  if (num_event_types == 0) {
    // Not available.
    return true;
  }
  CHECK_EQ(proto_->file_attrs().size(), num_event_types);
  CHECK_EQ(sizeof(perf_trace_event_type) * num_event_types,
           header_.event_types.size);
  data->SeekSet(header_.event_types.offset);
  for (int i = 0; i < num_event_types; ++i) {
    if (!ReadEventType(data, i, 0)) return false;
  }
  return true;
}

bool PerfReader::ReadEventType(DataReader* data, int attr_idx,
                               size_t event_size) {
  CheckNoEventTypePadding();
  decltype(perf_trace_event_type::event_id) event_id;

  if (!data->ReadUint64(&event_id)) {
    LOG(ERROR) << "Error reading event ID.";
    return false;
  }

  size_t event_name_len;
  if (event_size == 0) {  // Not in an event.
    event_name_len = sizeof(perf_trace_event_type::name);
  } else {
    event_name_len = event_size - sizeof(perf_event_header) - sizeof(event_id);
  }

  PerfFileAttr attr;
  if (!data->ReadString(event_name_len, &attr.name)) {
    LOG(ERROR) << "Not enough data left in data to read event name.";
    return false;
  }

  if (attr_idx >= proto_->file_attrs().size()) {
    LOG(ERROR) << "Too many event types, or attrs not read yet!";
    return false;
  }
  if (event_id != proto_->file_attrs(attr_idx).attr().config()) {
    LOG(ERROR) << "event_id for perf_trace_event_type (" << event_id << ") "
               << "does not match attr.config ("
               << proto_->file_attrs(attr_idx).attr().config() << ")";
    return false;
  }
  attr.attr.config = proto_->file_attrs(attr_idx).attr().config();

  serializer_.SerializePerfEventType(attr, proto_->add_event_types());
  return true;
}

bool PerfReader::ReadDataSection(DataReader* data) {
  u64 data_remaining_bytes = header_.data.size;
  data->SeekSet(header_.data.offset);
  while (data_remaining_bytes != 0) {
    // Read the header to determine the size of the event.
    perf_event_header header;
    if (!ReadPerfEventHeader(data, &header)) {
      LOG(ERROR) << "Error reading event header from data section.";
      return false;
    }

    // Read the rest of the event data.
    malloced_unique_ptr<event_t> event(CallocMemoryForEvent(header.size));
    event->header = header;
    if (!data->ReadDataValue(event->header.size - sizeof(event->header),
                             "rest of event", &event->header + 1)) {
      return false;
    }
    MaybeSwapEventFields(event.get(), data->is_cross_endian());

    // We must have a valid way to read sample info before reading perf events.
    CHECK(serializer_.SampleInfoReaderAvailable());

    // Serialize the event to protobuf form.
    PerfEvent* proto_event = proto_->add_events();
    if (!serializer_.SerializeEvent(event, proto_event)) return false;

    if (proto_event->header().type() == PERF_RECORD_AUXTRACE) {
      if (!ReadAuxtraceTraceData(data, proto_event)) return false;
      data_remaining_bytes -= proto_event->auxtrace_event().size();
    }
    data_remaining_bytes -= event->header.size;
  }

  DLOG(INFO) << "Number of events stored: " << proto_->events_size();
  return true;
}

bool PerfReader::ReadMetadata(DataReader* data) {
  // Metadata comes after the event data.
  data->SeekSet(header_.data.offset + header_.data.size);

  // Read the (offset, size) pairs of all the metadata elements. Note that this
  // takes into account all present metadata types, not just the ones included
  // in |kSupportedMetadataMask|. If a metadata type is not supported, it is
  // skipped over.
  std::vector<struct perf_file_section> sections(GetNumBits(metadata_mask()));
  for (struct perf_file_section& section : sections) {
    if (!ReadPerfFileSection(data, &section)) {
      LOG(ERROR) << "Error reading metadata entry info.";
      return false;
    }
  }

  auto section_iter = sections.begin();
  for (u32 type = HEADER_FIRST_FEATURE; type != HEADER_LAST_FEATURE; ++type) {
    if (!get_metadata_mask_bit(type)) continue;
    data->SeekSet(section_iter->offset);
    u64 size = section_iter->size;

    switch (type) {
      case HEADER_TRACING_DATA:
        if (!ReadTracingMetadata(data, size)) return false;
        break;
      case HEADER_BUILD_ID:
        if (!ReadBuildIDMetadata(data, size)) return false;
        break;
      case HEADER_HOSTNAME:
        if (!ReadSingleStringMetadata(
                data, size,
                proto_->mutable_string_metadata()->mutable_hostname())) {
          return false;
        }
        break;
      case HEADER_OSRELEASE:
        if (!ReadSingleStringMetadata(
                data, size,
                proto_->mutable_string_metadata()->mutable_kernel_version())) {
          return false;
        }
        break;
      case HEADER_VERSION:
        if (!ReadSingleStringMetadata(
                data, size,
                proto_->mutable_string_metadata()->mutable_perf_version())) {
          return false;
        }
        break;
      case HEADER_ARCH:
        if (!ReadSingleStringMetadata(
                data, size,
                proto_->mutable_string_metadata()->mutable_architecture())) {
          return false;
        }
        break;
      case HEADER_CPUDESC:
        if (!ReadSingleStringMetadata(
                data, size,
                proto_->mutable_string_metadata()->mutable_cpu_description())) {
          return false;
        }
        break;
      case HEADER_CPUID:
        if (!ReadSingleStringMetadata(
                data, size,
                proto_->mutable_string_metadata()->mutable_cpu_id())) {
          return false;
        }
        break;
      case HEADER_CMDLINE: {
        auto* string_metadata = proto_->mutable_string_metadata();
        if (!ReadRepeatedStringMetadata(
                data, size, string_metadata->mutable_perf_command_line_token(),
                string_metadata->mutable_perf_command_line_whole())) {
          return false;
        }
        break;
      }
      case HEADER_NRCPUS:
        if (!ReadUint32Metadata(data, type, size)) return false;
        break;
      case HEADER_TOTAL_MEM:
        if (!ReadUint64Metadata(data, type, size)) return false;
        break;
      case HEADER_EVENT_DESC:
        if (!ReadEventDescMetadata(data)) return false;
        break;
      case HEADER_CPU_TOPOLOGY:
        if (!ReadCPUTopologyMetadata(data)) return false;
        break;
      case HEADER_NUMA_TOPOLOGY:
        if (!ReadNUMATopologyMetadata(data)) return false;
        break;
      case HEADER_BRANCH_STACK:
        break;
      case HEADER_PMU_MAPPINGS:
        if (!ReadPMUMappingsMetadata(data, size)) return false;
        break;
      case HEADER_GROUP_DESC:
        if (!ReadGroupDescMetadata(data)) return false;
        break;
      default:
        LOG(INFO) << "Unsupported metadata type, skipping: " << type;
        break;
    }
    ++section_iter;
  }

  return true;
}

bool PerfReader::ReadBuildIDMetadata(DataReader* data, size_t size) {
  CheckNoBuildIDEventPadding();
  while (size > 0) {
    // Make sure there is enough data for everything but the filename.
    perf_event_header build_id_header;
    if (!ReadPerfEventHeader(data, &build_id_header)) {
      LOG(ERROR) << "Error reading build ID header.";
      return false;
    }

    if (!ReadBuildIDMetadataWithoutHeader(data, build_id_header)) return false;
    size -= build_id_header.size;
  }

  return true;
}

bool PerfReader::ReadBuildIDMetadataWithoutHeader(
    DataReader* data, const perf_event_header& header) {
  // Allocate memory for the event.
  malloced_unique_ptr<build_id_event> event(
      CallocMemoryForBuildID(header.size));
  event->header = header;

  // Make sure there is enough data for the rest of the event.
  if (!data->ReadDataValue(header.size - sizeof(header),
                           "rest of build ID event", &event->header + 1)) {
    LOG(ERROR) << "Not enough bytes to read build id event";
    return false;
  }
  if (data->is_cross_endian()) ByteSwap(&event->pid);

  // Perf tends to use more space than necessary, so fix the size.
  event->header.size =
      sizeof(*event) + GetUint64AlignedStringLength(event->filename);

  if (!serializer_.SerializeBuildIDEvent(event, proto_->add_build_ids())) {
    LOG(ERROR) << "Could not serialize build ID event with ID "
               << RawDataToHexString(event->build_id, sizeof(event->build_id));
    return false;
  }
  return true;
}

bool PerfReader::ReadSingleStringMetadata(DataReader* data,
                                          size_t max_readable_size,
                                          StringAndMd5sumPrefix* dest) const {
  // If a string metadata field is present but empty, it can have a size of 0,
  // in which case there is nothing to be read.
  string single_string;
  if (max_readable_size && !data->ReadStringWithSizeFromData(&single_string))
    return false;
  dest->set_value(single_string);
  dest->set_value_md5_prefix(Md5Prefix(single_string));
  return true;
}

bool PerfReader::ReadRepeatedStringMetadata(
    DataReader* data, size_t max_readable_size,
    RepeatedPtrField<StringAndMd5sumPrefix>* dest_array,
    StringAndMd5sumPrefix* dest_single) const {
  num_string_data_type count = 1;
  if (!data->ReadUint32(&count)) {
    LOG(ERROR) << "Error reading string count.";
    return false;
  }
  size_t size_read = sizeof(count);

  string full_string;
  while (count-- > 0 && size_read < max_readable_size) {
    StringAndMd5sumPrefix* new_entry = dest_array->Add();
    size_t offset = data->Tell();
    if (!ReadSingleStringMetadata(data, max_readable_size - size_read,
                                  new_entry)) {
      return false;
    }

    if (!full_string.empty()) full_string += " ";
    full_string += new_entry->value();

    size_read += data->Tell() - offset;
  }

  dest_single->set_value(full_string);
  dest_single->set_value_md5_prefix(Md5Prefix(full_string));
  return true;
}

bool PerfReader::ReadUint32Metadata(DataReader* data, u32 type, size_t size) {
  PerfUint32Metadata uint32_data;
  uint32_data.type = type;

  while (size > 0) {
    uint32_t item;
    if (!data->ReadUint32(&item)) {
      LOG(ERROR) << "Error reading uint32 metadata";
      return false;
    }

    uint32_data.data.push_back(item);
    size -= sizeof(item);
  }

  serializer_.SerializeSingleUint32Metadata(uint32_data,
                                            proto_->add_uint32_metadata());
  return true;
}

bool PerfReader::ReadUint64Metadata(DataReader* data, u32 type, size_t size) {
  PerfUint64Metadata uint64_data;
  uint64_data.type = type;

  while (size > 0) {
    uint64_t item;
    if (!data->ReadUint64(&item)) {
      LOG(ERROR) << "Error reading uint64 metadata";
      return false;
    }

    uint64_data.data.push_back(item);
    size -= sizeof(item);
  }

  serializer_.SerializeSingleUint64Metadata(uint64_data,
                                            proto_->add_uint64_metadata());
  return true;
}
bool PerfReader::ReadEventDescMetadata(DataReader* data) {
  // Structure:
  // u32 nr_events
  // u32 sizeof(perf_event_attr)
  // foreach event (nr_events):
  //   struct perf_event_attr
  //   u32 nr_ids
  //   event name (len & string, 64-bit padded)
  //   u64 ids[nr_ids]

  u32 nr_events;
  if (!data->ReadUint32(&nr_events)) {
    LOG(ERROR) << "Error reading event_desc nr_events.";
    return false;
  }

  u32 attr_size;
  if (!data->ReadUint32(&attr_size)) {
    LOG(ERROR) << "Error reading event_desc attr_size.";
    return false;
  }

  file_attrs_seen_.clear();
  proto_->clear_file_attrs();
  proto_->mutable_file_attrs()->Reserve(nr_events);

  for (u32 i = 0; i < nr_events; i++) {
    PerfFileAttr attr;
    if (!ReadEventAttr(data, &attr.attr)) return false;

    u32 nr_ids;
    if (!data->ReadUint32(&nr_ids)) {
      LOG(ERROR) << "Error reading event_desc nr_ids.";
      return false;
    }

    if (!data->ReadStringWithSizeFromData(&attr.name)) return false;
    std::vector<u64>& ids = attr.ids;
    ids.resize(nr_ids);
    for (u64& id : ids) {
      if (!data->ReadUint64(&id)) {
        LOG(ERROR) << "Error reading ID value for attr #" << i;
        return false;
      }
    }
    AddPerfFileAttr(attr);
    // The EVENT_DESC metadata is the newer replacement for the older event type
    // fields. In the protobuf, both types of data are stored in the
    // |event_types| field.
    serializer_.SerializePerfEventType(attr, proto_->add_event_types());
  }
  return true;
}

bool PerfReader::ReadCPUTopologyMetadata(DataReader* data) {
  num_siblings_type num_core_siblings;
  if (!data->ReadUint32(&num_core_siblings)) {
    LOG(ERROR) << "Error reading num core siblings.";
    return false;
  }

  PerfCPUTopologyMetadata cpu_topology;
  cpu_topology.core_siblings.resize(num_core_siblings);
  for (size_t i = 0; i < num_core_siblings; ++i) {
    if (!data->ReadStringWithSizeFromData(&cpu_topology.core_siblings[i]))
      return false;
  }

  num_siblings_type num_thread_siblings;
  if (!data->ReadUint32(&num_thread_siblings)) {
    LOG(ERROR) << "Error reading num core siblings.";
    return false;
  }

  cpu_topology.thread_siblings.resize(num_thread_siblings);
  for (size_t i = 0; i < num_thread_siblings; ++i) {
    if (!data->ReadStringWithSizeFromData(&cpu_topology.thread_siblings[i]))
      return false;
  }

  serializer_.SerializeCPUTopologyMetadata(cpu_topology,
                                           proto_->mutable_cpu_topology());
  return true;
}

bool PerfReader::ReadNUMATopologyMetadata(DataReader* data) {
  numa_topology_num_nodes_type num_nodes;
  if (!data->ReadUint32(&num_nodes)) {
    LOG(ERROR) << "Error reading NUMA topology num nodes.";
    return false;
  }

  for (size_t i = 0; i < num_nodes; ++i) {
    PerfNodeTopologyMetadata node;
    if (!data->ReadUint32(&node.id) || !data->ReadUint64(&node.total_memory) ||
        !data->ReadUint64(&node.free_memory) ||
        !data->ReadStringWithSizeFromData(&node.cpu_list)) {
      LOG(ERROR) << "Error reading NUMA topology info for node #" << i;
      return false;
    }
    serializer_.SerializeNodeTopologyMetadata(node,
                                              proto_->add_numa_topology());
  }
  return true;
}

bool PerfReader::ReadPMUMappingsMetadata(DataReader* data, size_t size) {
  pmu_mappings_num_mappings_type num_mappings;
  auto begin_offset = data->Tell();
  if (!data->ReadUint32(&num_mappings)) {
    LOG(ERROR) << "Error reading the number of PMU mappings.";
    return false;
  }

  // Check size of the data read in addition to the iteration based on the
  // number of PMU mappings because the number of pmu mappings is always zero
  // in piped perf.data file.
  //
  // The number of PMU mappings is initialized to zero and after all the
  // mappings are wirtten to the perf.data files, this value is set to the
  // number of PMU mappings written. This logic doesn't work in pipe mode. So,
  // the number of PMU mappings is always zero.
  // Fix to write the number of PMU mappings before writing the actual PMU
  // mappings landed upstream in 4.14. But the check for size is required as
  // long as there are machines with older version of perf.
  for (u32 i = 0; i < num_mappings || data->Tell() - begin_offset < size; ++i) {
    PerfPMUMappingsMetadata mapping;
    if (!data->ReadUint32(&mapping.type) ||
        !data->ReadStringWithSizeFromData(&mapping.name)) {
      LOG(ERROR) << "Error reading PMU mapping info for mapping #" << i;
      return false;
    }
    serializer_.SerializePMUMappingsMetadata(mapping,
                                             proto_->add_pmu_mappings());
  }
  if (data->Tell() - begin_offset != size) {
    LOG(ERROR) << "Size from the header doesn't match the read size";
    return false;
  }
  return true;
}

bool PerfReader::ReadGroupDescMetadata(DataReader* data) {
  group_desc_num_groups_type num_groups;
  if (!data->ReadUint32(&num_groups)) {
    LOG(ERROR) << "Error reading group desc num groups.";
    return false;
  }

  for (u32 i = 0; i < num_groups; ++i) {
    PerfGroupDescMetadata group;
    if (!data->ReadStringWithSizeFromData(&group.name) ||
        !data->ReadUint32(&group.leader_idx) ||
        !data->ReadUint32(&group.num_members)) {
      LOG(ERROR) << "Error reading group desc info for group #" << i;
      return false;
    }
    serializer_.SerializeGroupDescMetadata(group, proto_->add_group_desc());
  }
  return true;
}
bool PerfReader::ReadTracingMetadata(DataReader* data, size_t size) {
  std::vector<char> tracing_data(size);
  if (!data->ReadDataValue(tracing_data.size(), "tracing_data",
                           tracing_data.data())) {
    return false;
  }
  serializer_.SerializeTracingMetadata(tracing_data, proto_);
  return true;
}

bool PerfReader::ReadFileData(DataReader* data) {
  // Make sure sections are within the size of the file. This check prevents
  // more obscure messages later when attempting to read from one of these
  // sections.
  if (header_.attrs.offset + header_.attrs.size > data->size()) {
    LOG(ERROR) << "Header says attrs section ends at "
               << header_.attrs.offset + header_.attrs.size
               << " bytes, which is larger than perf data size of "
               << data->size() << " bytes.";
    return false;
  }
  if (header_.data.offset + header_.data.size > data->size()) {
    LOG(ERROR) << "Header says data section ends at "
               << header_.data.offset + header_.data.size
               << " bytes, which is larger than perf data size of "
               << data->size() << " bytes.";
    return false;
  }
  if (header_.event_types.offset + header_.event_types.size > data->size()) {
    LOG(ERROR) << "Header says event_types section ends at "
               << header_.event_types.offset + header_.event_types.size
               << " bytes, which is larger than perf data size of "
               << data->size() << " bytes.";
    return false;
  }

  if (!get_metadata_mask_bit(HEADER_EVENT_DESC)) {
    // Prefer to read attrs and event names from HEADER_EVENT_DESC metadata if
    // available. event_types section of perf.data is obsolete, but use it as
    // a fallback:
    if (!(ReadAttrsSection(data) && ReadEventTypesSection(data))) return false;
  }

  if (!(ReadMetadata(data) && ReadDataSection(data))) return false;

  // We can construct HEADER_EVENT_DESC from attrs and event types.
  // NB: Can't set this before ReadMetadata(), or it may misread the metadata.
  if (!event_types().empty()) set_metadata_mask_bit(HEADER_EVENT_DESC);

  return true;
}

bool PerfReader::ReadPipedData(DataReader* data) {
  // The piped data comes right after the file header.
  CHECK_EQ(piped_header_.size, data->Tell());
  bool result = true;
  int num_event_types = 0;

  CheckNoEventHeaderPadding();

  while (result && data->Tell() < data->size()) {
    perf_event_header header;
    if (!ReadPerfEventHeader(data, &header)) {
      LOG(ERROR) << "Error reading event header.";
      break;
    }

    if (header.size == 0) {
      // Avoid an infinite loop.
      LOG(ERROR) << "Event size is zero. Type: " << header.type;
      return false;
    }

    // Compute the size of the post-header part of the event data.
    size_t size_without_header = header.size - sizeof(header);

    bool isHeaderEventType = [&] {
      switch (header.type) {
        case PERF_RECORD_HEADER_ATTR:
        case PERF_RECORD_HEADER_EVENT_TYPE:
        case PERF_RECORD_HEADER_TRACING_DATA:
        case PERF_RECORD_HEADER_BUILD_ID:
          return true;
        default:
          return false;
      }
    }();

    if (!isHeaderEventType) {
      // Allocate space for an event struct based on the size in the header.
      // Don't blindly allocate the entire event_t because it is a
      // variable-sized type that may include data beyond what's nominally
      // declared in its definition.
      malloced_unique_ptr<event_t> event(CallocMemoryForEvent(header.size));
      event->header = header;

      // Read the rest of the event data.
      if (!data->ReadDataValue(size_without_header, "rest of piped event",
                               &event->header + 1)) {
        break;
      }
      MaybeSwapEventFields(event.get(), data->is_cross_endian());

      // Serialize the event to protobuf form.
      PerfEvent* proto_event = proto_->add_events();
      if (!serializer_.SerializeEvent(event, proto_event)) return false;

      if (proto_event->header().type() == PERF_RECORD_AUXTRACE) {
        if (!ReadAuxtraceTraceData(data, proto_event)) return false;
      }
      continue;
    }

    result = [&] {
      switch (header.type) {
        case PERF_RECORD_HEADER_ATTR:
          return ReadAttrEventBlock(data, size_without_header);
        case PERF_RECORD_HEADER_EVENT_TYPE:
          return ReadEventType(data, num_event_types++, header.size);
        case PERF_RECORD_HEADER_TRACING_DATA:
          set_metadata_mask_bit(HEADER_TRACING_DATA);
          {
            // TRACING_DATA's header.size is a lie. It is the size of only the
            // event struct. The size of the data is in the event struct, and
            // followed immediately by the tracing header data.
            decltype(tracing_data_event::size) size = 0;
            if (!data->ReadUint32(&size)) {
              LOG(ERROR) << "Error reading tracing data size.";
              return false;
            }
            return ReadTracingMetadata(data, size);
          }
        case PERF_RECORD_HEADER_BUILD_ID:
          set_metadata_mask_bit(HEADER_BUILD_ID);
          return ReadBuildIDMetadataWithoutHeader(data, header);
        default:
          // For unsupported event types, log a warning only if the type is an
          // unknown type.
          if (header.type < PERF_RECORD_USER_TYPE_START ||
              header.type >= PERF_RECORD_HEADER_MAX) {
            LOG(WARNING) << "Unknown event type: " << header.type;
          }
          // Skip over the data in this event.
          data->SeekSet(data->Tell() + size_without_header);
          return true;
      }
    }();
  }

  if (!result) return false;

  // The PERF_RECORD_HEADER_EVENT_TYPE events are obsolete, but if present
  // and PERF_RECORD_HEADER_EVENT_DESC metadata events are not, we should use
  // them. Otherwise, we should use prefer the _EVENT_DESC data.
  if (!get_metadata_mask_bit(HEADER_EVENT_DESC) &&
      num_event_types == proto_->file_attrs().size()) {
    // We can construct HEADER_EVENT_DESC:
    set_metadata_mask_bit(HEADER_EVENT_DESC);
  }

  return result;
}

bool PerfReader::ReadAuxtraceTraceData(DataReader* data,
                                       PerfEvent* proto_event) {
  std::vector<char> trace_data(proto_event->auxtrace_event().size());
  if (!data->ReadDataValue(trace_data.size(),
                           "trace date from PERF_RECORD_AUXTRACE event",
                           trace_data.data())) {
    return false;
  }
  if (data->is_cross_endian()) {
    LOG(ERROR) << "Cannot byteswap trace data from PERF_RECORD_AUXTRACE";
  }
  if (!serializer_.SerializeAuxtraceEventTraceData(
          trace_data, proto_event->mutable_auxtrace_event())) {
    return false;
  }
  return true;
}

bool PerfReader::WriteHeader(const struct perf_file_header& header,
                             DataWriter* data) const {
  CheckNoEventHeaderPadding();
  size_t size = sizeof(header);
  return data->WriteDataValue(&header, size, "file header");
}

bool PerfReader::WriteAttrs(const struct perf_file_header& header,
                            DataWriter* data) const {
  CheckNoPerfEventAttrPadding();
  const size_t id_offset = header.size;
  CHECK_EQ(id_offset, data->Tell());

  std::vector<struct perf_file_section> id_sections;
  id_sections.reserve(attrs().size());
  for (const auto& attr : proto_->file_attrs()) {
    size_t section_size =
        attr.ids_size() * sizeof(decltype(PerfFileAttr::ids)::value_type);
    id_sections.push_back(perf_file_section{data->Tell(), section_size});
    for (const uint64_t& id : attr.ids()) {
      if (!data->WriteDataValue(&id, sizeof(id), "ID info")) return false;
    }
  }

  CHECK_EQ(header.attrs.offset, data->Tell());
  for (int i = 0; i < attrs().size(); i++) {
    const struct perf_file_section& id_section = id_sections[i];
    PerfFileAttr attr;
    serializer_.DeserializePerfFileAttr(proto_->file_attrs(i), &attr);
    if (!data->WriteDataValue(&attr.attr, sizeof(attr.attr), "attribute") ||
        !data->WriteDataValue(&id_section, sizeof(id_section), "ID section")) {
      return false;
    }
  }
  return true;
}

bool PerfReader::WriteData(const struct perf_file_header& header,
                           DataWriter* data) const {
  // No need to CHECK anything if no event data is being written.
  if (proto_->events().empty()) return true;

  CHECK(serializer_.SampleInfoReaderAvailable());
  CHECK_EQ(header.data.offset, data->Tell());
  for (const PerfEvent& proto_event : proto_->events()) {
    malloced_unique_ptr<event_t> event;
    // The nominal size given by |proto_event| may not be correct, as the
    // contents may have changed since the PerfEvent was created. Use the size
    // in the event_t returned by PerfSerializer::DeserializeEvent().
    if (!serializer_.DeserializeEvent(proto_event, &event) ||
        !data->WriteDataValue(event.get(), event->header.size, "event data")) {
      return false;
    }
    // PERF_RECORD_AUXTRACE contains trace data that is written after writing
    // the actual event data.
    if (proto_event.header().type() == PERF_RECORD_AUXTRACE &&
        proto_event.auxtrace_event().size() > 0) {
      std::vector<char> trace_data;
      if (!serializer_.DeserializeAuxtraceEventTraceData(
              proto_event.auxtrace_event(), &trace_data) ||
          !data->WriteDataValue(reinterpret_cast<void*>(trace_data.data()),
                                trace_data.size(),
                                "trace data from PERF_RECORD_AUXTRACE event")) {
        return false;
      }
    }
  }
  return true;
}

bool PerfReader::WriteMetadata(const struct perf_file_header& header,
                               DataWriter* data) const {
  const size_t header_offset = header.data.offset + header.data.size;
  CHECK_EQ(header_offset, data->Tell());

  // There is one header for each feature pointing to the metadata for that
  // feature. If a feature has no metadata, the size field is zero.
  const size_t headers_size =
      GetNumSupportedMetadata() * sizeof(perf_file_section);
  const size_t metadata_offset = header_offset + headers_size;
  data->SeekSet(metadata_offset);

  // Record the new metadata offsets and sizes in this vector of info entries.
  std::vector<struct perf_file_section> metadata_sections;
  metadata_sections.reserve(GetNumSupportedMetadata());

  // For less verbose access to string metadata fields.
  const auto& string_metadata = proto_->string_metadata();

  for (u32 type = HEADER_FIRST_FEATURE; type != HEADER_LAST_FEATURE; ++type) {
    if ((header.adds_features[0] & (1 << type)) == 0) continue;

    struct perf_file_section header_entry;
    header_entry.offset = data->Tell();
    // Write actual metadata to address metadata_offset
    switch (type) {
      case HEADER_TRACING_DATA:
        if (!data->WriteDataValue(tracing_data().data(), tracing_data().size(),
                                  "tracing data")) {
          return false;
        }
        break;
      case HEADER_BUILD_ID:
        if (!WriteBuildIDMetadata(type, data)) return false;
        break;
      case HEADER_HOSTNAME:
        if (!WriteSingleStringMetadata(string_metadata.hostname(), data))
          return false;
        break;
      case HEADER_OSRELEASE:
        if (!WriteSingleStringMetadata(string_metadata.kernel_version(), data))
          return false;
        break;
      case HEADER_VERSION:
        if (!WriteSingleStringMetadata(string_metadata.perf_version(), data))
          return false;
        break;
      case HEADER_ARCH:
        if (!WriteSingleStringMetadata(string_metadata.architecture(), data))
          return false;
        break;
      case HEADER_CPUDESC:
        if (!WriteSingleStringMetadata(string_metadata.cpu_description(), data))
          return false;
        break;
      case HEADER_CPUID:
        if (!WriteSingleStringMetadata(string_metadata.cpu_id(), data))
          return false;
        break;
      case HEADER_CMDLINE:
        if (!WriteRepeatedStringMetadata(
                string_metadata.perf_command_line_token(), data)) {
          return false;
        }
        break;
      case HEADER_NRCPUS:
        if (!WriteUint32Metadata(type, data)) return false;
        break;
      case HEADER_TOTAL_MEM:
        if (!WriteUint64Metadata(type, data)) return false;
        break;
      case HEADER_EVENT_DESC:
        if (!WriteEventDescMetadata(data)) return false;
        break;
      case HEADER_CPU_TOPOLOGY:
        if (!WriteCPUTopologyMetadata(data)) return false;
        break;
      case HEADER_NUMA_TOPOLOGY:
        if (!WriteNUMATopologyMetadata(data)) return false;
        break;
      case HEADER_BRANCH_STACK:
        break;
      case HEADER_PMU_MAPPINGS:
        if (!WritePMUMappingsMetadata(data)) return false;
        break;
      case HEADER_GROUP_DESC:
        if (!WriteGroupDescMetadata(data)) return false;
        break;
      default:
        LOG(ERROR) << "Unsupported metadata type: " << type;
        return false;
    }

    // Compute the size of the metadata that was just written. This is reflected
    // in how much the data write pointer has moved.
    header_entry.size = data->Tell() - header_entry.offset;
    metadata_sections.push_back(header_entry);
  }
  // Make sure we have recorded the right number of entries.
  CHECK_EQ(GetNumSupportedMetadata(), metadata_sections.size());

  // Now write the metadata offset and size info back to the metadata headers.
  size_t old_offset = data->Tell();
  data->SeekSet(header_offset);
  if (!data->WriteDataValue(metadata_sections.data(), headers_size,
                            "metadata section info")) {
    return false;
  }
  // Make sure the write pointer now points to the end of the metadata headers
  // and hence the beginning of the actual metadata.
  CHECK_EQ(metadata_offset, data->Tell());
  data->SeekSet(old_offset);

  return true;
}

bool PerfReader::WriteBuildIDMetadata(u32 type, DataWriter* data) const {
  CheckNoBuildIDEventPadding();
  for (const auto& build_id : proto_->build_ids()) {
    malloced_unique_ptr<build_id_event> event;
    if (!serializer_.DeserializeBuildIDEvent(build_id, &event)) {
      LOG(ERROR) << "Could not deserialize build ID event with build ID "
                 << RawDataToHexString(build_id.build_id_hash());
      return false;
    }
    if (!data->WriteDataValue(event.get(), event->header.size,
                              "Build ID metadata")) {
      return false;
    }
  }
  return true;
}

bool PerfReader::WriteSingleStringMetadata(const StringAndMd5sumPrefix& src,
                                           DataWriter* data) const {
  return data->WriteStringWithSizeToData(src.value());
}

bool PerfReader::WriteRepeatedStringMetadata(
    const RepeatedPtrField<StringAndMd5sumPrefix>& src_array,
    DataWriter* data) const {
  num_string_data_type num_strings = src_array.size();
  if (!data->WriteDataValue(&num_strings, sizeof(num_strings),
                            "number of string metadata")) {
    return false;
  }
  for (const auto& src_entry : src_array) {
    if (!data->WriteStringWithSizeToData(src_entry.value())) return false;
  }
  return true;
}

bool PerfReader::WriteUint32Metadata(u32 type, DataWriter* data) const {
  for (const auto& metadata : proto_->uint32_metadata()) {
    if (metadata.type() != type) continue;
    PerfUint32Metadata local_metadata;
    serializer_.DeserializeSingleUint32Metadata(metadata, &local_metadata);
    const std::vector<uint32_t>& raw_data = local_metadata.data;
    return data->WriteDataValue(raw_data.data(),
                                raw_data.size() * sizeof(uint32_t),
                                "uint32_t metadata");
  }
  LOG(ERROR) << "Uint32 metadata of type " << type << " not present";
  return false;
}

bool PerfReader::WriteUint64Metadata(u32 type, DataWriter* data) const {
  for (const auto& metadata : proto_->uint64_metadata()) {
    if (metadata.type() != type) continue;
    PerfUint64Metadata local_metadata;
    serializer_.DeserializeSingleUint64Metadata(metadata, &local_metadata);
    const std::vector<uint64_t>& raw_data = local_metadata.data;
    return data->WriteDataValue(raw_data.data(),
                                raw_data.size() * sizeof(uint64_t),
                                "uint32_t metadata");
  }
  LOG(ERROR) << "Uint64 metadata of type " << type << " not present";
  return false;
}

bool PerfReader::WriteEventDescMetadata(DataWriter* data) const {
  CheckNoPerfEventAttrPadding();

  if (attrs().size() > event_types().size()) {
    LOG(ERROR) << "Number of attrs (" << attrs().size() << ") cannot exceed "
               << "number of event types (" << event_types().size() << ")";
    return false;
  }

  event_desc_num_events num_events = proto_->file_attrs().size();
  if (!data->WriteDataValue(&num_events, sizeof(num_events),
                            "event_desc num_events")) {
    return false;
  }
  event_desc_attr_size attr_size = sizeof(perf_event_attr);
  if (!data->WriteDataValue(&attr_size, sizeof(attr_size),
                            "event_desc attr_size")) {
    return false;
  }

  for (int i = 0; i < attrs().size(); ++i) {
    const auto& stored_attr = attrs().Get(i);
    PerfFileAttr attr;
    serializer_.DeserializePerfFileAttr(stored_attr, &attr);
    if (!serializer_.DeserializePerfEventType(proto_->event_types(i), &attr))
      return false;

    if (!data->WriteDataValue(&attr.attr, sizeof(attr.attr),
                              "event_desc attribute")) {
      return false;
    }

    event_desc_num_unique_ids num_ids = attr.ids.size();
    if (!data->WriteDataValue(&num_ids, sizeof(num_ids),
                              "event_desc num_unique_ids")) {
      return false;
    }

    if (!data->WriteStringWithSizeToData(attr.name)) return false;

    if (!data->WriteDataValue(attr.ids.data(), num_ids * sizeof(attr.ids[0]),
                              "event_desc unique_ids")) {
      return false;
    }
  }
  return true;
}

bool PerfReader::WriteCPUTopologyMetadata(DataWriter* data) const {
  PerfCPUTopologyMetadata cpu_topology;
  serializer_.DeserializeCPUTopologyMetadata(proto_->cpu_topology(),
                                             &cpu_topology);

  std::vector<string>& cores = cpu_topology.core_siblings;
  num_siblings_type num_cores = cores.size();
  if (!data->WriteDataValue(&num_cores, sizeof(num_cores), "num cores"))
    return false;
  for (string& core_name : cores) {
    if (!data->WriteStringWithSizeToData(core_name)) return false;
  }

  std::vector<string>& threads = cpu_topology.thread_siblings;
  num_siblings_type num_threads = threads.size();
  if (!data->WriteDataValue(&num_threads, sizeof(num_threads), "num threads"))
    return false;
  for (string& thread_name : threads) {
    if (!data->WriteStringWithSizeToData(thread_name)) return false;
  }

  return true;
}

bool PerfReader::WriteNUMATopologyMetadata(DataWriter* data) const {
  numa_topology_num_nodes_type num_nodes = proto_->numa_topology().size();
  if (!data->WriteDataValue(&num_nodes, sizeof(num_nodes), "num nodes"))
    return false;

  for (const auto& node_proto : proto_->numa_topology()) {
    PerfNodeTopologyMetadata node;
    serializer_.DeserializeNodeTopologyMetadata(node_proto, &node);

    if (!data->WriteDataValue(&node.id, sizeof(node.id), "node id") ||
        !data->WriteDataValue(&node.total_memory, sizeof(node.total_memory),
                              "node total memory") ||
        !data->WriteDataValue(&node.free_memory, sizeof(node.free_memory),
                              "node free memory") ||
        !data->WriteStringWithSizeToData(node.cpu_list)) {
      return false;
    }
  }
  return true;
}

bool PerfReader::WritePMUMappingsMetadata(DataWriter* data) const {
  pmu_mappings_num_mappings_type num_mappings = proto_->pmu_mappings().size();
  if (!data->WriteDataValue(&num_mappings, sizeof(num_mappings),
                            "num mappings"))
    return false;

  for (const auto& mapping_proto : proto_->pmu_mappings()) {
    PerfPMUMappingsMetadata mapping;
    serializer_.DeserializePMUMappingsMetadata(mapping_proto, &mapping);

    if (!data->WriteDataValue(&mapping.type, sizeof(mapping.type),
                              "mapping type") ||
        !data->WriteStringWithSizeToData(mapping.name)) {
      return false;
    }
  }
  return true;
}

bool PerfReader::WriteGroupDescMetadata(DataWriter* data) const {
  group_desc_num_groups_type num_groups = proto_->group_desc().size();
  if (!data->WriteDataValue(&num_groups, sizeof(num_groups), "num groups"))
    return false;

  for (const auto& group_proto : proto_->group_desc()) {
    PerfGroupDescMetadata group;
    serializer_.DeserializeGroupDescMetadata(group_proto, &group);

    if (!data->WriteStringWithSizeToData(group.name) ||
        !data->WriteDataValue(&group.leader_idx, sizeof(group.leader_idx),
                              "group leader index") ||
        !data->WriteDataValue(&group.num_members, sizeof(group.num_members),
                              "group num members")) {
      return false;
    }
  }
  return true;
}

bool PerfReader::ReadAttrEventBlock(DataReader* data, size_t size) {
  const size_t initial_offset = data->Tell();
  PerfFileAttr attr;
  if (!ReadEventAttr(data, &attr.attr)) return false;

  // attr.attr.size has been upgraded to the current size of perf_event_attr.
  const size_t actual_attr_size = data->Tell() - initial_offset;

  const size_t num_ids =
      (size - actual_attr_size) / sizeof(decltype(attr.ids)::value_type);
  if (!ReadUniqueIDs(data, num_ids, &attr.ids)) return false;

  // Event types are found many times in the perf data file.
  // Only add this event type if it is not already present.
  if (!attr.ids.empty() &&
      file_attrs_seen_.find(attr.ids[0]) != file_attrs_seen_.end()) {
    return true;
  }

  AddPerfFileAttr(attr);
  return true;
}

void PerfReader::MaybeSwapEventFields(event_t* event, bool is_cross_endian) {
  if (!is_cross_endian) return;
  uint32_t type = event->header.type;
  switch (type) {
    case PERF_RECORD_SAMPLE:
      break;
    case PERF_RECORD_MMAP:
      ByteSwap(&event->mmap.pid);
      ByteSwap(&event->mmap.tid);
      ByteSwap(&event->mmap.start);
      ByteSwap(&event->mmap.len);
      ByteSwap(&event->mmap.pgoff);
      break;
    case PERF_RECORD_MMAP2:
      ByteSwap(&event->mmap2.pid);
      ByteSwap(&event->mmap2.tid);
      ByteSwap(&event->mmap2.start);
      ByteSwap(&event->mmap2.len);
      ByteSwap(&event->mmap2.pgoff);
      ByteSwap(&event->mmap2.maj);
      ByteSwap(&event->mmap2.min);
      ByteSwap(&event->mmap2.ino);
      ByteSwap(&event->mmap2.ino_generation);
      ByteSwap(&event->mmap2.prot);
      ByteSwap(&event->mmap2.flags);
      break;
    case PERF_RECORD_FORK:
    case PERF_RECORD_EXIT:
      ByteSwap(&event->fork.pid);
      ByteSwap(&event->fork.tid);
      ByteSwap(&event->fork.ppid);
      ByteSwap(&event->fork.ptid);
      ByteSwap(&event->fork.time);
      break;
    case PERF_RECORD_COMM:
      ByteSwap(&event->comm.pid);
      ByteSwap(&event->comm.tid);
      break;
    case PERF_RECORD_LOST:
      ByteSwap(&event->lost.id);
      ByteSwap(&event->lost.lost);
      break;
    case PERF_RECORD_THROTTLE:
    case PERF_RECORD_UNTHROTTLE:
      ByteSwap(&event->throttle.time);
      ByteSwap(&event->throttle.id);
      ByteSwap(&event->throttle.stream_id);
      break;
    case PERF_RECORD_READ:
      ByteSwap(&event->read.pid);
      ByteSwap(&event->read.tid);
      ByteSwap(&event->read.value);
      ByteSwap(&event->read.time_enabled);
      ByteSwap(&event->read.time_running);
      ByteSwap(&event->read.id);
      break;
    case PERF_RECORD_AUX:
      ByteSwap(&event->aux.aux_offset);
      ByteSwap(&event->aux.aux_size);
      ByteSwap(&event->aux.flags);
      break;
    case PERF_RECORD_AUXTRACE:
      ByteSwap(&event->auxtrace.size);
      ByteSwap(&event->auxtrace.offset);
      ByteSwap(&event->auxtrace.reference);
      ByteSwap(&event->auxtrace.idx);
      ByteSwap(&event->auxtrace.tid);
      ByteSwap(&event->auxtrace.cpu);
      break;
    default:
      LOG(FATAL) << "Unknown event type: " << type;
  }

  // Do not swap the sample info fields that are not explicitly listed in the
  // struct definition of each event type. Leave that up to SampleInfoReader
  // within |serializer_|.
}

size_t PerfReader::GetNumSupportedMetadata() const {
  return GetNumBits(metadata_mask() & kSupportedMetadataMask);
}

size_t PerfReader::GetEventDescMetadataSize() const {
  if (attrs().size() > event_types().size()) {
    LOG(ERROR) << "Number of attrs (" << attrs().size() << ") cannot exceed "
               << "number of event types (" << event_types().size() << ")";
    return 0;
  }

  size_t size = 0;
  if (get_metadata_mask_bit(HEADER_EVENT_DESC)) {
    size += sizeof(event_desc_num_events) + sizeof(event_desc_attr_size);
    for (int i = 0; i < attrs().size(); ++i) {
      size += sizeof(perf_event_attr);
      size += sizeof(event_desc_num_unique_ids);
      size += ExpectedStorageSizeOf(event_types().Get(i).name());
      size += attrs().Get(i).ids_size() *
              sizeof(decltype(PerfFileAttr::ids)::value_type);
    }
  }
  return size;
}

size_t PerfReader::GetBuildIDMetadataSize() const {
  size_t size = 0;
  for (const auto& build_id : proto_->build_ids()) {
    size += sizeof(build_id_event) +
            GetUint64AlignedStringLength(build_id.filename());
  }
  return size;
}

size_t PerfReader::GetStringMetadataSize() const {
  size_t size = 0;
  if (string_metadata().has_hostname())
    size += ExpectedStorageSizeOf(string_metadata().hostname().value());
  if (string_metadata().has_kernel_version())
    size += ExpectedStorageSizeOf(string_metadata().kernel_version().value());
  if (string_metadata().has_perf_version())
    size += ExpectedStorageSizeOf(string_metadata().perf_version().value());
  if (string_metadata().has_architecture())
    size += ExpectedStorageSizeOf(string_metadata().architecture().value());
  if (string_metadata().has_cpu_description())
    size += ExpectedStorageSizeOf(string_metadata().cpu_description().value());
  if (string_metadata().has_cpu_id())
    size += ExpectedStorageSizeOf(string_metadata().cpu_id().value());

  if (!string_metadata().perf_command_line_token().empty()) {
    size += sizeof(num_string_data_type);
    for (const auto& token : string_metadata().perf_command_line_token())
      size += ExpectedStorageSizeOf(token.value());
  }
  return size;
}

size_t PerfReader::GetUint32MetadataSize() const {
  size_t size = 0;
  for (const auto& metadata : proto_->uint32_metadata())
    size += metadata.data().size() * sizeof(uint32_t);
  return size;
}

size_t PerfReader::GetUint64MetadataSize() const {
  size_t size = 0;
  for (const auto& metadata : proto_->uint64_metadata())
    size += metadata.data().size() * sizeof(uint64_t);
  return size;
}

size_t PerfReader::GetCPUTopologyMetadataSize() const {
  // Core siblings.
  size_t size = sizeof(num_siblings_type);
  for (const string& core_sibling : proto_->cpu_topology().core_siblings())
    size += ExpectedStorageSizeOf(core_sibling);

  // Thread siblings.
  size += sizeof(num_siblings_type);
  for (const string& thread_sibling : proto_->cpu_topology().thread_siblings())
    size += ExpectedStorageSizeOf(thread_sibling);

  return size;
}

size_t PerfReader::GetNUMATopologyMetadataSize() const {
  size_t size = sizeof(numa_topology_num_nodes_type);
  for (const auto& node : proto_->numa_topology()) {
    size += sizeof(node.id());
    size += sizeof(node.total_memory()) + sizeof(node.free_memory());
    size += ExpectedStorageSizeOf(node.cpu_list());
  }
  return size;
}

size_t PerfReader::GetPMUMappingsMetadataSize() const {
  size_t size = sizeof(pmu_mappings_num_mappings_type);
  for (const auto& node : proto_->pmu_mappings()) {
    size += sizeof(node.type());
    size += ExpectedStorageSizeOf(node.name());
  }
  return size;
}

size_t PerfReader::GetGroupDescMetadataSize() const {
  size_t size = sizeof(group_desc_num_groups_type);
  for (const auto& group : proto_->group_desc()) {
    size += ExpectedStorageSizeOf(group.name());
    size += sizeof(group.leader_idx());
    size += sizeof(group.num_members());
  }
  return size;
}

bool PerfReader::NeedsNumberOfStringData(u32 type) const {
  return type == HEADER_CMDLINE;
}

bool PerfReader::LocalizeMMapFilenames(
    const std::map<string, string>& filename_map) {
  CHECK(serializer_.SampleInfoReaderAvailable());

  // Search for mmap/mmap2 events for which the filename needs to be updated.
  for (PerfEvent& event : *proto_->mutable_events()) {
    if (event.header().type() != PERF_RECORD_MMAP &&
        event.header().type() != PERF_RECORD_MMAP2) {
      continue;
    }
    const string& filename = event.mmap_event().filename();
    const auto it = filename_map.find(filename);
    if (it == filename_map.end())  // not found
      continue;

    const string& new_filename = it->second;
    size_t old_len = GetUint64AlignedStringLength(filename);
    size_t new_len = GetUint64AlignedStringLength(new_filename);
    size_t new_size = event.header().size() - old_len + new_len;

    event.mutable_mmap_event()->set_filename(new_filename);
    event.mutable_header()->set_size(new_size);
  }

  return true;
}

void PerfReader::AddPerfFileAttr(const PerfFileAttr& attr) {
  serializer_.SerializePerfFileAttr(attr, proto_->add_file_attrs());

  // Generate a new SampleInfoReader with the new attr.
  serializer_.CreateSampleInfoReader(attr, is_cross_endian_);
  if (!attr.ids.empty()) {
    file_attrs_seen_.insert(attr.ids[0]);
  }
}

}  // namespace quipper