summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2016-06-23 17:11:14 -0700
committerYabin Cui <yabinc@google.com>2016-06-23 17:26:25 -0700
commit1761a271faf98050891ba6e918993225782c811a (patch)
treea97f1ecc2a0e7a16222229d9468d77c43d03c168
parent965a99e493aea5ec88bda89629e969e3226c262c (diff)
downloadextras-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.cpp17
-rw-r--r--simpleperf/cmd_record_test.cpp4
-rw-r--r--simpleperf/cmd_report_test.cpp2
-rw-r--r--simpleperf/get_test_data.h2
-rw-r--r--simpleperf/record.cpp166
-rw-r--r--simpleperf/record.h120
-rw-r--r--simpleperf/record_file.h10
-rw-r--r--simpleperf/record_file_reader.cpp91
-rw-r--r--simpleperf/record_file_test.cpp8
-rw-r--r--simpleperf/record_file_writer.cpp39
-rw-r--r--simpleperf/testdata/perf_with_kernel_symbol.databin5325508 -> 5294792 bytes
-rw-r--r--simpleperf/testdata/perf_with_kmem_slab_callgraph.databin9831776 -> 7571716 bytes
-rw-r--r--simpleperf/thread_tree.cpp7
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
index 30b14c9f..8b1fda14 100644
--- a/simpleperf/testdata/perf_with_kernel_symbol.data
+++ b/simpleperf/testdata/perf_with_kernel_symbol.data
Binary files differ
diff --git a/simpleperf/testdata/perf_with_kmem_slab_callgraph.data b/simpleperf/testdata/perf_with_kmem_slab_callgraph.data
index 255ba2f9..cdb691fc 100644
--- a/simpleperf/testdata/perf_with_kmem_slab_callgraph.data
+++ b/simpleperf/testdata/perf_with_kmem_slab_callgraph.data
Binary files differ
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;