OSDN Git Service

Simpleperf: Add SPLIT and SPLIT_END records to handle big records.
authorYabin Cui <yabinc@google.com>
Fri, 24 Jun 2016 00:11:14 +0000 (17:11 -0700)
committerYabin Cui <yabinc@google.com>
Fri, 24 Jun 2016 00:26:25 +0000 (17:26 -0700)
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.

13 files changed:
simpleperf/cmd_record.cpp
simpleperf/cmd_record_test.cpp
simpleperf/cmd_report_test.cpp
simpleperf/get_test_data.h
simpleperf/record.cpp
simpleperf/record.h
simpleperf/record_file.h
simpleperf/record_file_reader.cpp
simpleperf/record_file_test.cpp
simpleperf/record_file_writer.cpp
simpleperf/testdata/perf_with_kernel_symbol.data
simpleperf/testdata/perf_with_kmem_slab_callgraph.data
simpleperf/thread_tree.cpp

index 1695542..14283f8 100644 (file)
@@ -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) {
index 2306c55..ed199c9 100644 (file)
@@ -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);
index fe85f2f..f987a7a 100644 (file)
@@ -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) {
index f5cdb35..36b6ed1 100644 (file)
@@ -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_
index 4b1f3e8..e9986e5 100644 (file)
@@ -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;
 }
index 3afee73..3b7a717 100644 (file)
@@ -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);
 
index a0af30e..3b6df7e 100644 (file)
@@ -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_;
index b124d28..68cee21 100644 (file)
@@ -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;
index f9707ef..885691d 100644 (file)
@@ -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.
index c64b959..125bfad 100644 (file)
@@ -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;
index 30b14c9..8b1fda1 100644 (file)
Binary files a/simpleperf/testdata/perf_with_kernel_symbol.data and b/simpleperf/testdata/perf_with_kernel_symbol.data differ
index 255ba2f..cdb691f 100644 (file)
Binary files a/simpleperf/testdata/perf_with_kmem_slab_callgraph.data and b/simpleperf/testdata/perf_with_kmem_slab_callgraph.data differ
index 58b0200..8e4e9e9 100644 (file)
@@ -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;