diff options
author | Yabin Cui <yabinc@google.com> | 2016-06-23 17:11:14 -0700 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2016-06-23 17:26:25 -0700 |
commit | 1761a271faf98050891ba6e918993225782c811a (patch) | |
tree | a97f1ecc2a0e7a16222229d9468d77c43d03c168 | |
parent | 965a99e493aea5ec88bda89629e969e3226c262c (diff) | |
download | extras-1761a271faf98050891ba6e918993225782c811a.tar.gz |
Simpleperf: Add SPLIT and SPLIT_END records to handle big records.
Previously we split KernelSymbolRecord because it is > 65535. Then
I found TracingDataRecord can also be > 65535. So it is better to
handle big records when reading and writing perf.data.
record_file_writer.cpp splits a big record into multiple SPLIT
records followed by a SPLIT_END record, and record_file_reader.cpp
restores the big record when reading SPLIT and SPLIT_END records.
Also Add RecordHeader to represent record having size > 65535.
Bug: 29581559
Change-Id: I0b4556988f77b3431c7f1a28fce65cf225d6a067
Test: run simpleperf_unit_test.
-rw-r--r-- | simpleperf/cmd_record.cpp | 17 | ||||
-rw-r--r-- | simpleperf/cmd_record_test.cpp | 4 | ||||
-rw-r--r-- | simpleperf/cmd_report_test.cpp | 2 | ||||
-rw-r--r-- | simpleperf/get_test_data.h | 2 | ||||
-rw-r--r-- | simpleperf/record.cpp | 166 | ||||
-rw-r--r-- | simpleperf/record.h | 120 | ||||
-rw-r--r-- | simpleperf/record_file.h | 10 | ||||
-rw-r--r-- | simpleperf/record_file_reader.cpp | 91 | ||||
-rw-r--r-- | simpleperf/record_file_test.cpp | 8 | ||||
-rw-r--r-- | simpleperf/record_file_writer.cpp | 39 | ||||
-rw-r--r-- | simpleperf/testdata/perf_with_kernel_symbol.data | bin | 5325508 -> 5294792 bytes | |||
-rw-r--r-- | simpleperf/testdata/perf_with_kmem_slab_callgraph.data | bin | 9831776 -> 7571716 bytes | |||
-rw-r--r-- | simpleperf/thread_tree.cpp | 7 |
13 files changed, 265 insertions, 201 deletions
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 1695542c..14283f8e 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -639,12 +639,9 @@ bool RecordCommand::DumpKernelSymbol() { 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; @@ -791,7 +788,7 @@ bool RecordCommand::ProcessRecord(Record* record) { } 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; } @@ -808,7 +805,7 @@ bool RecordCommand::DumpSymbolForRecord(const SampleRecord& r, 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; } } @@ -817,7 +814,7 @@ bool RecordCommand::DumpSymbolForRecord(const SampleRecord& r, 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; } } @@ -912,7 +909,7 @@ bool RecordCommand::PostUnwind(const std::vector<std::string>& args) { if (!UnwindRecord(record.get())) { return false; } - return record_file_writer_->WriteData(record->BinaryFormat()); + return record_file_writer_->WriteRecord(*record); }, false); if (!result) { diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index 2306c556..ed199c95 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -227,10 +227,6 @@ static void CheckKernelSymbol(const std::string& path, bool need_kallsyms, 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); diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp index fe85f2fb..f987a7a0 100644 --- a/simpleperf/cmd_report_test.cpp +++ b/simpleperf/cmd_report_test.cpp @@ -287,7 +287,7 @@ TEST_F(ReportCommandTest, report_more_than_one_event_types) { 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) { diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h index f5cdb353..36b6ed1f 100644 --- a/simpleperf/get_test_data.h +++ b/simpleperf/get_test_data.h @@ -74,7 +74,7 @@ static const std::string PERF_DATA_WITH_KERNEL_SYMBOL = "perf_with_kernel_symbol // 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_ diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp index 4b1f3e80..e9986e50 100644 --- a/simpleperf/record.cpp +++ b/simpleperf/record.cpp @@ -68,6 +68,11 @@ void MoveToBinaryFormat(const T& data, char*& p) { 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); @@ -193,11 +198,9 @@ void Record::Dump(size_t indent) const { 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); @@ -248,11 +251,10 @@ MmapRecord MmapRecord::Create(const perf_event_attr& attr, bool in_kernel, 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); @@ -287,11 +289,9 @@ void Mmap2Record::DumpData(size_t indent) const { 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); @@ -329,11 +329,10 @@ CommRecord CommRecord::Create(const perf_event_attr& attr, uint32_t pid, 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); @@ -368,11 +367,9 @@ ForkRecord ForkRecord::Create(const perf_event_attr& attr, uint32_t pid, 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); @@ -393,11 +390,10 @@ void LostRecord::DumpData(size_t indent) const { 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) { @@ -636,10 +632,9 @@ void SampleRecord::DumpData(size_t indent) const { 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); @@ -681,13 +676,9 @@ BuildIdRecord BuildIdRecord::Create(bool in_kernel, pid_t pid, 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); @@ -702,8 +693,6 @@ std::vector<char> KernelSymbolRecord::BinaryFormat() const { 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); @@ -711,34 +700,20 @@ std::vector<char> KernelSymbolRecord::BinaryFormat() const { } 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; @@ -775,9 +750,9 @@ DsoRecord DsoRecord::Create(uint64_t dso_type, uint64_t dso_id, 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); @@ -817,9 +792,9 @@ SymbolRecord SymbolRecord::Create(uint64_t addr, uint64_t len, 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); @@ -852,11 +827,8 @@ TracingDataRecord TracingDataRecord::Create(std::vector<char> tracing_data) { 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 { @@ -870,32 +842,32 @@ 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)); } } @@ -905,13 +877,11 @@ std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer( 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; } diff --git a/simpleperf/record.h b/simpleperf/record.h index 3afee736..3b7a7175 100644 --- a/simpleperf/record.h +++ b/simpleperf/record.h @@ -47,8 +47,24 @@ enum user_record_type { 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; }; @@ -110,6 +126,46 @@ struct PerfSampleStackUserType { 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 @@ -142,17 +198,17 @@ struct SampleId { // 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() {} @@ -160,7 +216,7 @@ struct 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); } @@ -174,10 +230,7 @@ struct Record { 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; @@ -199,7 +252,7 @@ struct MmapRecord : public Record { 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(); @@ -228,7 +281,7 @@ struct Mmap2Record : public Record { 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(); @@ -244,7 +297,7 @@ struct CommRecord : public Record { 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, @@ -263,8 +316,7 @@ struct ExitOrForkRecord : public Record { } 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: @@ -272,14 +324,14 @@ struct ExitOrForkRecord : public Record { }; 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, @@ -290,10 +342,9 @@ struct LostRecord : public Record { 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: @@ -320,7 +371,7 @@ struct SampleRecord : public Record { 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; @@ -338,7 +389,7 @@ struct BuildIdRecord : public Record { 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, @@ -350,15 +401,14 @@ struct BuildIdRecord : public Record { }; 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; @@ -371,7 +421,7 @@ struct DsoRecord : public Record { 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, @@ -389,11 +439,12 @@ struct SymbolRecord : public Record { 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; }; @@ -401,10 +452,9 @@ struct SymbolRecord : public Record { 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); @@ -418,7 +468,7 @@ struct TracingDataRecord : public Record { 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: @@ -426,7 +476,7 @@ struct UnknownRecord : public Record { }; 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); diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h index a0af30e4..3b6df7e4 100644 --- a/simpleperf/record_file.h +++ b/simpleperf/record_file.h @@ -45,11 +45,7 @@ class RecordFileWriter { ~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); @@ -68,6 +64,7 @@ class RecordFileWriter { 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); @@ -132,7 +129,8 @@ class RecordFileReader { 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_; diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp index b124d281..68cee21f 100644 --- a/simpleperf/record_file_reader.cpp +++ b/simpleperf/record_file_reader.cpp @@ -66,11 +66,7 @@ bool RecordFileReader::Close() { } 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() { @@ -89,8 +85,7 @@ 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. @@ -142,8 +137,7 @@ bool RecordFileReader::ReadFeatureSectionDescriptors() { } 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); @@ -158,8 +152,7 @@ bool RecordFileReader::ReadIdsForAttr(const FileAttr& attr, std::vector<uint64_t 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; @@ -180,11 +173,10 @@ bool RecordFileReader::ReadDataSection(std::function<bool(std::unique_ptr<Record } 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(); @@ -208,36 +200,57 @@ bool RecordFileReader::ReadDataSection(std::function<bool(std::unique_ptr<Record 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) { @@ -247,7 +260,15 @@ std::unique_ptr<Record> RecordFileReader::ReadRecord() { } } } - 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) { @@ -265,8 +286,7 @@ 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; @@ -302,12 +322,11 @@ std::vector<BuildIdRecord> RecordFileReader::ReadBuildIdFeature() { 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; diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp index f9707ef8..885691da 100644 --- a/simpleperf/record_file_test.cpp +++ b/simpleperf/record_file_test.cpp @@ -63,7 +63,7 @@ TEST_F(RecordFileTest, smoke) { // 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)); @@ -117,9 +117,9 @@ TEST_F(RecordFileTest, records_sorted_by_time) { 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. diff --git a/simpleperf/record_file_writer.cpp b/simpleperf/record_file_writer.cpp index c64b9591..125bfad9 100644 --- a/simpleperf/record_file_writer.cpp +++ b/simpleperf/record_file_writer.cpp @@ -117,6 +117,45 @@ bool RecordFileWriter::WriteAttrSection(const std::vector<AttrWithId>& attr_ids) 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; diff --git a/simpleperf/testdata/perf_with_kernel_symbol.data b/simpleperf/testdata/perf_with_kernel_symbol.data Binary files differindex 30b14c9f..8b1fda14 100644 --- a/simpleperf/testdata/perf_with_kernel_symbol.data +++ b/simpleperf/testdata/perf_with_kernel_symbol.data diff --git a/simpleperf/testdata/perf_with_kmem_slab_callgraph.data b/simpleperf/testdata/perf_with_kmem_slab_callgraph.data Binary files differindex 255ba2f9..cdb691fc 100644 --- a/simpleperf/testdata/perf_with_kmem_slab_callgraph.data +++ b/simpleperf/testdata/perf_with_kmem_slab_callgraph.data diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp index 58b02006..8e4e9e9d 100644 --- a/simpleperf/thread_tree.cpp +++ b/simpleperf/thread_tree.cpp @@ -253,12 +253,7 @@ void ThreadTree::Update(const Record& record) { 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; |