// 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(§ion->offset);
|
ByteSwap(§ion->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(×tamp_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, §ion)) {
|
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
|