return false;
}
}
- std::vector<KernelSymbolRecord> records =
- KernelSymbolRecord::Create(std::move(kallsyms));
- for (auto& r : records) {
- if (!ProcessRecord(&r)) {
- return false;
- }
+ KernelSymbolRecord r = KernelSymbolRecord::Create(std::move(kallsyms));
+ if (!ProcessRecord(&r)) {
+ return false;
}
}
return true;
} else if (record->type() == PERF_RECORD_LOST) {
lost_record_count_ += static_cast<LostRecord*>(record)->lost;
}
- bool result = record_file_writer_->WriteData(record->BinaryFormat());
+ bool result = record_file_writer_->WriteRecord(*record);
return result;
}
map->dso->SetDumped();
DsoRecord dso_record =
DsoRecord::Create(map->dso->type(), map->dso->id(), map->dso->Path());
- if (!record_file_writer_->WriteData(dso_record.BinaryFormat())) {
+ if (!record_file_writer_->WriteRecord(dso_record)) {
return false;
}
}
symbol->SetDumped();
SymbolRecord symbol_record = SymbolRecord::Create(
symbol->addr, symbol->len, symbol->Name(), map->dso->id());
- if (!record_file_writer_->WriteData(symbol_record.BinaryFormat())) {
+ if (!record_file_writer_->WriteRecord(symbol_record)) {
return false;
}
}
if (!UnwindRecord(record.get())) {
return false;
}
- return record_file_writer_->WriteData(record->BinaryFormat());
+ return record_file_writer_->WriteRecord(*record);
},
false);
if (!result) {
for (const auto& record : records) {
if (record->type() == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) {
has_kernel_symbol_records = true;
- auto& r = *static_cast<KernelSymbolRecord*>(record.get());
- if (!r.end_of_symbols) {
- ASSERT_FALSE(r.kallsyms.empty());
- }
}
}
ASSERT_EQ(need_kallsyms, has_kernel_symbol_records);
TEST_F(ReportCommandTest, report_kernel_symbol) {
Report(PERF_DATA_WITH_KERNEL_SYMBOL);
ASSERT_TRUE(success);
- ASSERT_NE(content.find("perf_event_comm_output"), std::string::npos);
+ ASSERT_NE(content.find("perf_event_aux"), std::string::npos);
}
TEST_F(ReportCommandTest, report_dumped_symbols) {
// perf_with_symbols.data is generated by `sudo simpleperf record --dump-symbols sleep 1`.
static const std::string PERF_DATA_WITH_SYMBOLS = "perf_with_symbols.data";
-// perf_kmem_slab_callgraph.data is generated by `simpleperf kmem record --slab --call-graph fp sleep 0.0001`.
+// perf_kmem_slab_callgraph.data is generated by `simpleperf kmem record --slab --call-graph fp -f 100 sleep 0.0001`.
static const std::string PERF_DATA_WITH_KMEM_SLAB_CALLGRAPH_RECORD = "perf_with_kmem_slab_callgraph.data";
#endif // SIMPLE_PERF_GET_TEST_DATA_H_
p += sizeof(T);
}
+template <>
+void MoveToBinaryFormat(const RecordHeader& data, char*& p) {
+ data.MoveToBinaryFormat(p);
+}
+
template <class T>
void MoveToBinaryFormat(const T* data_p, size_t n, char*& p) {
size_t size = n * sizeof(T);
uint64_t Record::Timestamp() const { return sample_id.time_data.time; }
-MmapRecord::MmapRecord(const perf_event_attr& attr,
- const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+MmapRecord::MmapRecord(const perf_event_attr& attr, const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(data, p);
filename = p;
p += Align(filename.size() + 1, 8);
return record;
}
-Mmap2Record::Mmap2Record(const perf_event_attr& attr,
- const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+Mmap2Record::Mmap2Record(const perf_event_attr& attr, const char* p)
+ : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(data, p);
filename = p;
p += Align(filename.size() + 1, 8);
data.flags, filename.c_str());
}
-CommRecord::CommRecord(const perf_event_attr& attr,
- const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+CommRecord::CommRecord(const perf_event_attr& attr, const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(data, p);
comm = p;
p += Align(strlen(p) + 1, 8);
return record;
}
-ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr,
- const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const char* p)
+ : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(data, p);
CHECK_LE(p, end);
sample_id.ReadFromBinaryFormat(attr, p, end);
return record;
}
-LostRecord::LostRecord(const perf_event_attr& attr,
- const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+LostRecord::LostRecord(const perf_event_attr& attr, const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(id, p);
MoveFromBinaryFormat(lost, p);
CHECK_LE(p, end);
PrintIndented(indent, "id %" PRIu64 ", lost %" PRIu64 "\n", id, lost);
}
-SampleRecord::SampleRecord(const perf_event_attr& attr,
- const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+SampleRecord::SampleRecord(const perf_event_attr& attr, const char* p)
+ : Record(p) {
+ const char* end = p + size();
+ p += header_size();
sample_type = attr.sample_type;
if (sample_type & PERF_SAMPLE_IDENTIFIER) {
uint64_t SampleRecord::Timestamp() const { return time_data.time; }
-BuildIdRecord::BuildIdRecord(const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+BuildIdRecord::BuildIdRecord(const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(pid, p);
build_id = BuildId(p, BUILD_ID_SIZE);
p += Align(build_id.Size(), 8);
return record;
}
-KernelSymbolRecord::KernelSymbolRecord(const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
- uint32_t end_flag;
- MoveFromBinaryFormat(end_flag, p);
- end_of_symbols = (end_flag == 1);
+KernelSymbolRecord::KernelSymbolRecord(const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
uint32_t size;
MoveFromBinaryFormat(size, p);
kallsyms.resize(size);
std::vector<char> buf(size());
char* p = buf.data();
MoveToBinaryFormat(header, p);
- uint32_t end_flag = (end_of_symbols ? 1 : 0);
- MoveToBinaryFormat(end_flag, p);
uint32_t size = static_cast<uint32_t>(kallsyms.size());
MoveToBinaryFormat(size, p);
memcpy(p, kallsyms.data(), size);
}
void KernelSymbolRecord::DumpData(size_t indent) const {
- PrintIndented(indent, "end_of_symbols: %s\n",
- end_of_symbols ? "true" : "false");
PrintIndented(indent, "kallsyms: %s\n", kallsyms.c_str());
}
-std::vector<KernelSymbolRecord> KernelSymbolRecord::Create(
- const std::string& kallsyms) {
- std::vector<KernelSymbolRecord> result;
- size_t left_bytes = kallsyms.size();
- const size_t max_bytes_per_record = 64000;
- const char* p = kallsyms.c_str();
- while (left_bytes > 0) {
- size_t used_bytes = std::min(left_bytes, max_bytes_per_record);
- KernelSymbolRecord r;
- r.SetTypeAndMisc(SIMPLE_PERF_RECORD_KERNEL_SYMBOL, 0);
- r.end_of_symbols = (used_bytes == left_bytes);
- r.kallsyms.assign(p, used_bytes);
- r.SetSize(r.header_size() + 8 + Align(r.kallsyms.size(), 8));
- result.push_back(r);
- p += used_bytes;
- left_bytes -= used_bytes;
- }
- return result;
+KernelSymbolRecord KernelSymbolRecord::Create(std::string kallsyms) {
+ KernelSymbolRecord r;
+ r.SetTypeAndMisc(SIMPLE_PERF_RECORD_KERNEL_SYMBOL, 0);
+ r.kallsyms = std::move(kallsyms);
+ r.SetSize(r.header_size() + 4 + Align(r.kallsyms.size(), 8));
+ return r;
}
-DsoRecord::DsoRecord(const perf_event_header* pheader) : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+DsoRecord::DsoRecord(const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(dso_type, p);
MoveFromBinaryFormat(dso_id, p);
dso_name = p;
return record;
}
-SymbolRecord::SymbolRecord(const perf_event_header* pheader) : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
+SymbolRecord::SymbolRecord(const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
MoveFromBinaryFormat(addr, p);
MoveFromBinaryFormat(len, p);
MoveFromBinaryFormat(dso_id, p);
return record;
}
-TracingDataRecord::TracingDataRecord(const perf_event_header* pheader) : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
+TracingDataRecord::TracingDataRecord(const char* p) : Record(p) {
+ const char* end = p + size();
+ p += header_size();
uint32_t size;
MoveFromBinaryFormat(size, p);
data.resize(size);
return record;
}
-UnknownRecord::UnknownRecord(const perf_event_header* pheader)
- : Record(pheader) {
- const char* p = reinterpret_cast<const char*>(pheader + 1);
- const char* end = reinterpret_cast<const char*>(pheader) + size();
- data.insert(data.end(), p, end);
+UnknownRecord::UnknownRecord(const char* p) : Record(p) {
+ data.insert(data.end(), p + header_size(), p + size());
}
std::vector<char> UnknownRecord::BinaryFormat() const {
void UnknownRecord::DumpData(size_t) const {}
std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
- const perf_event_header* pheader) {
- switch (pheader->type) {
+ uint32_t type, const char* p) {
+ switch (type) {
case PERF_RECORD_MMAP:
- return std::unique_ptr<Record>(new MmapRecord(attr, pheader));
+ return std::unique_ptr<Record>(new MmapRecord(attr, p));
case PERF_RECORD_MMAP2:
- return std::unique_ptr<Record>(new Mmap2Record(attr, pheader));
+ return std::unique_ptr<Record>(new Mmap2Record(attr, p));
case PERF_RECORD_COMM:
- return std::unique_ptr<Record>(new CommRecord(attr, pheader));
+ return std::unique_ptr<Record>(new CommRecord(attr, p));
case PERF_RECORD_EXIT:
- return std::unique_ptr<Record>(new ExitRecord(attr, pheader));
+ return std::unique_ptr<Record>(new ExitRecord(attr, p));
case PERF_RECORD_FORK:
- return std::unique_ptr<Record>(new ForkRecord(attr, pheader));
+ return std::unique_ptr<Record>(new ForkRecord(attr, p));
case PERF_RECORD_LOST:
- return std::unique_ptr<Record>(new LostRecord(attr, pheader));
+ return std::unique_ptr<Record>(new LostRecord(attr, p));
case PERF_RECORD_SAMPLE:
- return std::unique_ptr<Record>(new SampleRecord(attr, pheader));
+ return std::unique_ptr<Record>(new SampleRecord(attr, p));
case PERF_RECORD_TRACING_DATA:
- return std::unique_ptr<Record>(new TracingDataRecord(pheader));
+ return std::unique_ptr<Record>(new TracingDataRecord(p));
case SIMPLE_PERF_RECORD_KERNEL_SYMBOL:
- return std::unique_ptr<Record>(new KernelSymbolRecord(pheader));
+ return std::unique_ptr<Record>(new KernelSymbolRecord(p));
case SIMPLE_PERF_RECORD_DSO:
- return std::unique_ptr<Record>(new DsoRecord(pheader));
+ return std::unique_ptr<Record>(new DsoRecord(p));
case SIMPLE_PERF_RECORD_SYMBOL:
- return std::unique_ptr<Record>(new SymbolRecord(pheader));
+ return std::unique_ptr<Record>(new SymbolRecord(p));
default:
- return std::unique_ptr<Record>(new UnknownRecord(pheader));
+ return std::unique_ptr<Record>(new UnknownRecord(p));
}
}
const char* p = buf;
const char* end = buf + buf_size;
while (p < end) {
- const perf_event_header* header =
- reinterpret_cast<const perf_event_header*>(p);
- uint32_t size = header->size;
- CHECK_LE(p + size, end);
- CHECK_NE(0u, size);
- result.push_back(ReadRecordFromBuffer(attr, header));
- p += size;
+ RecordHeader header(p);
+ CHECK_LE(p + header.size, end);
+ CHECK_NE(0u, header.size);
+ result.push_back(ReadRecordFromBuffer(attr, header.type, p));
+ p += header.size;
}
return result;
}
SIMPLE_PERF_RECORD_KERNEL_SYMBOL,
SIMPLE_PERF_RECORD_DSO,
SIMPLE_PERF_RECORD_SYMBOL,
+ SIMPLE_PERF_RECORD_SPLIT,
+ SIMPLE_PERF_RECORD_SPLIT_END,
};
+// perf_event_header uses u16 to store record size. However, that is not
+// enough for storing records like KERNEL_SYMBOL or TRACING_DATA. So define
+// a simpleperf_record_header struct to store record header for simpleperf
+// defined records (type > SIMPLE_PERF_RECORD_TYPE_START).
+struct simpleperf_record_header {
+ uint32_t type;
+ uint16_t size1;
+ uint16_t size0;
+};
+
+static_assert(
+ sizeof(simpleperf_record_header) == sizeof(perf_event_header),
+ "simpleperf_record_header should have the same size as perf_event_header");
+
struct PerfSampleIpType {
uint64_t ip;
};
uint64_t dyn_size;
};
+struct RecordHeader {
+ public:
+ uint32_t type;
+ uint16_t misc;
+ uint32_t size;
+
+ RecordHeader() : type(0), misc(0), size(0) {}
+
+ RecordHeader(const char* p) {
+ auto pheader = reinterpret_cast<const perf_event_header*>(p);
+ if (pheader->type < SIMPLE_PERF_RECORD_TYPE_START) {
+ type = pheader->type;
+ misc = pheader->misc;
+ size = pheader->size;
+ } else {
+ auto sheader = reinterpret_cast<const simpleperf_record_header*>(p);
+ type = sheader->type;
+ misc = 0;
+ size = (sheader->size1 << 16) | sheader->size0;
+ }
+ }
+
+ void MoveToBinaryFormat(char*& p) const {
+ if (type < SIMPLE_PERF_RECORD_TYPE_START) {
+ auto pheader = reinterpret_cast<perf_event_header*>(p);
+ pheader->type = type;
+ pheader->misc = misc;
+ CHECK_LT(size, 1u << 16);
+ pheader->size = static_cast<uint16_t>(size);
+ } else {
+ auto sheader = reinterpret_cast<simpleperf_record_header*>(p);
+ sheader->type = type;
+ CHECK_EQ(misc, 0u);
+ sheader->size1 = size >> 16;
+ sheader->size0 = size & 0xffff;
+ }
+ p += sizeof(perf_event_header);
+ }
+};
+
// SampleId is optional at the end of a record in binary format. Its content is
// determined by sample_id_all and sample_type in perf_event_attr. To avoid the
// complexity of referring to perf_event_attr each time, we copy sample_id_all
// Usually one record contains the following three parts in order in binary
// format:
-// perf_event_header (at the head of a record, containing type and size info)
+// RecordHeader (at the head of a record, containing type and size info)
// data depends on the record type
-// sample_id (optional part at the end of a record)
-// We hold the common parts (perf_event_header and sample_id) in the base class
+// SampleId (optional part at the end of a record)
+// We hold the common parts (RecordHeader and SampleId) in the base class
// Record, and hold the type specific data part in classes derived from Record.
struct Record {
- perf_event_header header;
+ RecordHeader header;
SampleId sample_id;
- Record() { memset(&header, 0, sizeof(header)); }
- Record(const perf_event_header* pheader) { header = *pheader; }
+ Record() {}
+ Record(const char* p) : header(p) {}
virtual ~Record() {}
uint16_t misc() const { return header.misc; }
- size_t size() const { return header.size; }
+ uint32_t size() const { return header.size; }
static uint32_t header_size() { return sizeof(perf_event_header); }
header.misc = misc;
}
- void SetSize(uint32_t size) {
- CHECK_LT(size, 1u << 16);
- header.size = size;
- }
+ void SetSize(uint32_t size) { header.size = size; }
void Dump(size_t indent = 0) const;
virtual std::vector<char> BinaryFormat() const = 0;
MmapRecord() { // For CreateMmapRecord.
}
- MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader);
+ MmapRecord(const perf_event_attr& attr, const char* p);
std::vector<char> BinaryFormat() const override;
void AdjustSizeBasedOnData();
Mmap2Record() {}
- Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader);
+ Mmap2Record(const perf_event_attr& attr, const char* p);
std::vector<char> BinaryFormat() const override;
void AdjustSizeBasedOnData();
CommRecord() {}
- CommRecord(const perf_event_attr& attr, const perf_event_header* pheader);
+ CommRecord(const perf_event_attr& attr, const char* p);
std::vector<char> BinaryFormat() const override;
static CommRecord Create(const perf_event_attr& attr, uint32_t pid,
} data;
ExitOrForkRecord() {}
- ExitOrForkRecord(const perf_event_attr& attr,
- const perf_event_header* pheader);
+ ExitOrForkRecord(const perf_event_attr& attr, const char* p);
std::vector<char> BinaryFormat() const override;
protected:
};
struct ExitRecord : public ExitOrForkRecord {
- ExitRecord(const perf_event_attr& attr, const perf_event_header* pheader)
- : ExitOrForkRecord(attr, pheader) {}
+ ExitRecord(const perf_event_attr& attr, const char* p)
+ : ExitOrForkRecord(attr, p) {}
};
struct ForkRecord : public ExitOrForkRecord {
ForkRecord() {}
- ForkRecord(const perf_event_attr& attr, const perf_event_header* pheader)
- : ExitOrForkRecord(attr, pheader) {}
+ ForkRecord(const perf_event_attr& attr, const char* p)
+ : ExitOrForkRecord(attr, p) {}
static ForkRecord Create(const perf_event_attr& attr, uint32_t pid,
uint32_t tid, uint32_t ppid, uint32_t ptid,
uint64_t id;
uint64_t lost;
- LostRecord() {
- }
+ LostRecord() {}
- LostRecord(const perf_event_attr& attr, const perf_event_header* pheader);
+ LostRecord(const perf_event_attr& attr, const char* p);
std::vector<char> BinaryFormat() const override;
protected:
PerfSampleRegsUserType regs_user_data; // Valid if PERF_SAMPLE_REGS_USER.
PerfSampleStackUserType stack_user_data; // Valid if PERF_SAMPLE_STACK_USER.
- SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader);
+ SampleRecord(const perf_event_attr& attr, const char* p);
std::vector<char> BinaryFormat() const override;
void AdjustSizeBasedOnData();
uint64_t Timestamp() const override;
BuildIdRecord() {}
- BuildIdRecord(const perf_event_header* pheader);
+ BuildIdRecord(const char* p);
std::vector<char> BinaryFormat() const override;
static BuildIdRecord Create(bool in_kernel, pid_t pid,
};
struct KernelSymbolRecord : public Record {
- bool end_of_symbols;
std::string kallsyms;
KernelSymbolRecord() {}
- KernelSymbolRecord(const perf_event_header* pheader);
+ KernelSymbolRecord(const char* p);
std::vector<char> BinaryFormat() const override;
- static std::vector<KernelSymbolRecord> Create(const std::string& kallsyms);
+ static KernelSymbolRecord Create(std::string kallsyms);
protected:
void DumpData(size_t indent) const override;
DsoRecord() {}
- DsoRecord(const perf_event_header* pheader);
+ DsoRecord(const char* p);
std::vector<char> BinaryFormat() const override;
static DsoRecord Create(uint64_t dso_type, uint64_t dso_id,
SymbolRecord() {}
- SymbolRecord(const perf_event_header* pheader);
+ SymbolRecord(const char* p);
std::vector<char> BinaryFormat() const override;
static SymbolRecord Create(uint64_t addr, uint64_t len,
const std::string& name, uint64_t dso_id);
+
protected:
void DumpData(size_t indent) const override;
};
struct TracingDataRecord : public Record {
std::vector<char> data;
- TracingDataRecord() {
- }
+ TracingDataRecord() {}
- TracingDataRecord(const perf_event_header* pheader);
+ TracingDataRecord(const char* p);
std::vector<char> BinaryFormat() const override;
static TracingDataRecord Create(std::vector<char> tracing_data);
struct UnknownRecord : public Record {
std::vector<char> data;
- UnknownRecord(const perf_event_header* pheader);
+ UnknownRecord(const char* p);
std::vector<char> BinaryFormat() const override;
protected:
};
std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
- const perf_event_header* pheader);
+ uint32_t type, const char* p);
std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(
const perf_event_attr& attr, const char* buf, size_t buf_size);
~RecordFileWriter();
bool WriteAttrSection(const std::vector<AttrWithId>& attr_ids);
- bool WriteData(const void* buf, size_t len);
-
- bool WriteData(const std::vector<char>& data) {
- return WriteData(data.data(), data.size());
- }
+ bool WriteRecord(const Record& record);
bool WriteFeatureHeader(size_t feature_count);
bool WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records);
std::vector<std::string>* hit_kernel_modules,
std::vector<std::string>* hit_user_files);
bool WriteFileHeader();
+ bool WriteData(const void* buf, size_t len);
bool Write(const void* buf, size_t len);
bool SeekFileEnd(uint64_t* file_end);
bool WriteFeatureBegin(uint64_t* start_offset);
bool ReadAttrSection();
bool ReadIdsForAttr(const PerfFileFormat::FileAttr& attr, std::vector<uint64_t>* ids);
bool ReadFeatureSectionDescriptors();
- std::unique_ptr<Record> ReadRecord();
+ std::unique_ptr<Record> ReadRecord(size_t* nbytes_read);
+ bool Read(void* buf, size_t len);
const std::string filename_;
FILE* record_fp_;
}
bool RecordFileReader::ReadHeader() {
- if (fread(&header_, sizeof(header_), 1, record_fp_) != 1) {
- PLOG(ERROR) << "failed to read file " << filename_;
- return false;
- }
- return true;
+ return Read(&header_, sizeof(header_));
}
bool RecordFileReader::ReadAttrSection() {
}
for (size_t i = 0; i < attr_count; ++i) {
std::vector<char> buf(header_.attr_size);
- if (fread(&buf[0], buf.size(), 1, record_fp_) != 1) {
- PLOG(ERROR) << "failed to read " << filename_;
+ if (!Read(buf.data(), buf.size())) {
return false;
}
// The size of perf_event_attr is changing between different linux kernel versions.
}
for (const auto& id : features) {
SectionDesc desc;
- if (fread(&desc, sizeof(desc), 1, record_fp_) != 1) {
- PLOG(ERROR) << "failed to read " << filename_;
+ if (!Read(&desc, sizeof(desc))) {
return false;
}
feature_section_descriptors_.emplace(id, desc);
return false;
}
ids->resize(id_count);
- if (fread(ids->data(), attr.ids.size, 1, record_fp_) != 1) {
- PLOG(ERROR) << "failed to read file " << filename_;
+ if (!Read(ids->data(), attr.ids.size)) {
return false;
}
return true;
}
RecordCache cache(has_timestamp);
for (size_t nbytes_read = 0; nbytes_read < header_.data.size;) {
- std::unique_ptr<Record> record = ReadRecord();
+ std::unique_ptr<Record> record = ReadRecord(&nbytes_read);
if (record == nullptr) {
return false;
}
- nbytes_read += record->size();
if (sorted) {
cache.Push(std::move(record));
record = cache.Pop();
return true;
}
-std::unique_ptr<Record> RecordFileReader::ReadRecord() {
- std::vector<char> buf(sizeof(perf_event_header));
- if (fread(buf.data(), sizeof(perf_event_header), 1, record_fp_) != 1) {
- PLOG(ERROR) << "failed to read file " << filename_;
+std::unique_ptr<Record> RecordFileReader::ReadRecord(size_t* nbytes_read) {
+ std::vector<char> buf(Record::header_size());
+ if (!Read(buf.data(), buf.size())) {
return nullptr;
}
- perf_event_header* pheader = reinterpret_cast<perf_event_header*>(&buf[0]);
- if (buf.size() < pheader->size) {
- buf.resize(pheader->size);
- pheader = reinterpret_cast<perf_event_header*>(&buf[0]);
- }
- if (buf.size() > sizeof(perf_event_header)) {
- if (fread(&buf[sizeof(perf_event_header)], pheader->size - sizeof(perf_event_header), 1, record_fp_) != 1) {
- PLOG(ERROR) << "failed to read file " << filename_;
+ RecordHeader header(buf.data());
+ if (header.type == SIMPLE_PERF_RECORD_SPLIT) {
+ // Read until meeting a RECORD_SPLIT_END record.
+ buf.clear();
+ size_t cur_size = 0;
+ char header_buf[Record::header_size()];
+ while (header.type == SIMPLE_PERF_RECORD_SPLIT) {
+ size_t bytes_to_read = header.size - Record::header_size();
+ buf.resize(cur_size + bytes_to_read);
+ if (!Read(&buf[cur_size], bytes_to_read)) {
+ return nullptr;
+ }
+ cur_size += bytes_to_read;
+ *nbytes_read += header.size;
+ if (!Read(header_buf, Record::header_size())) {
+ return nullptr;
+ }
+ header = RecordHeader(header_buf);
+ }
+ if (header.type != SIMPLE_PERF_RECORD_SPLIT_END) {
+ LOG(ERROR) << "SPLIT records are not followed by a SPLIT_END record.";
return nullptr;
}
+ *nbytes_read += header.size;
+ header = RecordHeader(buf.data());
+ } else if (Record::header_size() < header.size) {
+ buf.resize(header.size);
+ if (!Read(&buf[Record::header_size()], header.size - Record::header_size())) {
+ return nullptr;
+ }
+ *nbytes_read += header.size;
}
+
const perf_event_attr* attr = &file_attrs_[0].attr;
- if (file_attrs_.size() > 1 && pheader->type < PERF_RECORD_USER_DEFINED_TYPE_START) {
+ if (file_attrs_.size() > 1 && header.type < PERF_RECORD_USER_DEFINED_TYPE_START) {
bool has_event_id = false;
uint64_t event_id;
- if (pheader->type == PERF_RECORD_SAMPLE) {
- if (pheader->size > event_id_pos_in_sample_records_ + sizeof(uint64_t)) {
+ if (header.type == PERF_RECORD_SAMPLE) {
+ if (header.size > event_id_pos_in_sample_records_ + sizeof(uint64_t)) {
has_event_id = true;
event_id = *reinterpret_cast<uint64_t*>(&buf[event_id_pos_in_sample_records_]);
}
} else {
- if (pheader->size > event_id_reverse_pos_in_non_sample_records_) {
+ if (header.size > event_id_reverse_pos_in_non_sample_records_) {
has_event_id = true;
- event_id = *reinterpret_cast<uint64_t*>(&buf[pheader->size - event_id_reverse_pos_in_non_sample_records_]);
+ event_id = *reinterpret_cast<uint64_t*>(&buf[header.size - event_id_reverse_pos_in_non_sample_records_]);
}
}
if (has_event_id) {
}
}
}
- return ReadRecordFromBuffer(*attr, pheader);
+ return ReadRecordFromBuffer(*attr, header.type, buf.data());
+}
+
+bool RecordFileReader::Read(void* buf, size_t len) {
+ if (fread(buf, len, 1, record_fp_) != 1) {
+ PLOG(FATAL) << "failed to read file " << filename_;
+ return false;
+ }
+ return true;
}
bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) {
PLOG(ERROR) << "failed to fseek()";
return false;
}
- if (fread(data->data(), data->size(), 1, record_fp_) != 1) {
- PLOG(ERROR) << "failed to read " << filename_;
+ if (!Read(data->data(), data->size())) {
return false;
}
return true;
const char* end = buf.data() + buf.size();
std::vector<BuildIdRecord> result;
while (p < end) {
- const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
- CHECK_LE(p + header->size, end);
- BuildIdRecord record(header);
+ BuildIdRecord record(p);
// Set type explicitly as the perf.data produced by perf doesn't set it.
record.SetTypeAndMisc(PERF_RECORD_BUILD_ID, record.misc());
result.push_back(record);
+ CHECK_LE(p + record.size(), end);
p += record.size();
}
return result;
// Write data section.
MmapRecord mmap_record = MmapRecord::Create(*(attr_ids_[0].attr), true, 1, 1, 0x1000, 0x2000,
0x3000, "mmap_record_example", attr_ids_[0].ids[0]);
- ASSERT_TRUE(writer->WriteData(mmap_record.BinaryFormat()));
+ ASSERT_TRUE(writer->WriteRecord(mmap_record));
// Write feature section.
ASSERT_TRUE(writer->WriteFeatureHeader(1));
r1.sample_id.time_data.time = 2;
r2.sample_id.time_data.time = 1;
r3.sample_id.time_data.time = 3;
- ASSERT_TRUE(writer->WriteData(r1.BinaryFormat()));
- ASSERT_TRUE(writer->WriteData(r2.BinaryFormat()));
- ASSERT_TRUE(writer->WriteData(r3.BinaryFormat()));
+ ASSERT_TRUE(writer->WriteRecord(r1));
+ ASSERT_TRUE(writer->WriteRecord(r2));
+ ASSERT_TRUE(writer->WriteRecord(r3));
ASSERT_TRUE(writer->Close());
// Read from a record file.
return true;
}
+bool RecordFileWriter::WriteRecord(const Record& record) {
+ std::vector<char> data = record.BinaryFormat();
+ // linux-tools-perf only accepts records with size <= 65535 bytes. To make
+ // perf.data generated by simpleperf be able to be parsed by linux-tools-perf,
+ // Split simpleperf custom records which are > 65535 into a bunch of
+ // RECORD_SPLIT records, followed by a RECORD_SPLIT_END record.
+ constexpr uint32_t RECORD_SIZE_LIMIT = 65535;
+ if (record.size() <= RECORD_SIZE_LIMIT) {
+ WriteData(data.data(), data.size());
+ return true;
+ }
+ CHECK_GT(record.type(), SIMPLE_PERF_RECORD_TYPE_START);
+ const char* p = data.data();
+ uint32_t left_bytes = static_cast<uint32_t>(data.size());
+ RecordHeader header;
+ header.type = SIMPLE_PERF_RECORD_SPLIT;
+ char header_buf[Record::header_size()];
+ char* header_p;
+ while (left_bytes > 0) {
+ uint32_t bytes_to_write = std::min(RECORD_SIZE_LIMIT - Record::header_size(), left_bytes);
+ header.size = bytes_to_write + Record::header_size();
+ header_p = header_buf;
+ header.MoveToBinaryFormat(header_p);
+ if (!WriteData(header_buf, Record::header_size())) {
+ return false;
+ }
+ if (!WriteData(p, bytes_to_write)) {
+ return false;
+ }
+ p += bytes_to_write;
+ left_bytes -= bytes_to_write;
+ }
+ header.type = SIMPLE_PERF_RECORD_SPLIT_END;
+ header.size = Record::header_size();
+ header_p = header_buf;
+ header.MoveToBinaryFormat(header_p);
+ return WriteData(header_buf, Record::header_size());
+}
+
bool RecordFileWriter::WriteData(const void* buf, size_t len) {
if (!Write(buf, len)) {
return false;
ForkThread(r.data.pid, r.data.tid, r.data.ppid, r.data.ptid);
} else if (record.type() == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) {
const auto& r = *static_cast<const KernelSymbolRecord*>(&record);
- static std::string kallsyms;
- kallsyms += r.kallsyms;
- if (r.end_of_symbols) {
- Dso::SetKallsyms(std::move(kallsyms));
- kallsyms.clear();
- }
+ Dso::SetKallsyms(std::move(r.kallsyms));
} else if (record.type() == SIMPLE_PERF_RECORD_DSO) {
auto& r = *static_cast<const DsoRecord*>(&record);
Dso* dso = nullptr;