OSDN Git Service

simpleperf: dump only needed symbols in file feature section.
authorYabin Cui <yabinc@google.com>
Fri, 11 Nov 2016 22:53:52 +0000 (14:53 -0800)
committerYabin Cui <yabinc@google.com>
Fri, 11 Nov 2016 23:11:12 +0000 (15:11 -0800)
Instead of dumping all symbols in the hit elf files, dump only
needed symbols can save a lot of space. To do so, read perf.data
after recording to collect hit file and symbol information.

Bug: http://b/32340274
Test: test using `simpleperf record --dump-symbols` manually.
Test: run simpleperf_unit_test.
Change-Id: I480f3e2e7ccebfbb5df16a597724f5f40d62c821

simpleperf/cmd_record.cpp
simpleperf/cmd_report_sample.cpp
simpleperf/dso.cpp
simpleperf/dso.h
simpleperf/record_file.h
simpleperf/record_file_writer.cpp

index 68c50b4..8d9e5ac 100644 (file)
@@ -770,16 +770,14 @@ bool RecordCommand::ProcessRecord(Record* record) {
     }
   }
   UpdateRecordForEmbeddedElfPath(record);
-  thread_tree_.Update(*record);
   if (unwind_dwarf_callchain_ && !post_unwind_) {
+    thread_tree_.Update(*record);
     if (!UnwindRecord(record)) {
       return false;
     }
   }
   if (record->type() == PERF_RECORD_SAMPLE) {
     sample_record_count_++;
-    auto& r = *static_cast<SampleRecord*>(record);
-    CollectHitFileInfo(r);
   } else if (record->type() == PERF_RECORD_LOST) {
     lost_record_count_ += static_cast<LostRecord*>(record)->lost;
   }
@@ -890,6 +888,18 @@ bool RecordCommand::PostUnwind(const std::vector<std::string>& args) {
 
 bool RecordCommand::DumpAdditionalFeatures(
     const std::vector<std::string>& args) {
+  // Read data section of perf.data to collect hit file information.
+  thread_tree_.ClearThreadAndMap();
+  auto callback = [&](const Record* r) {
+    thread_tree_.Update(*r);
+    if (r->type() == PERF_RECORD_SAMPLE) {
+      CollectHitFileInfo(*reinterpret_cast<const SampleRecord*>(r));
+    }
+  };
+  if (!record_file_writer_->ReadDataSection(callback)) {
+    return false;
+  }
+
   size_t feature_count = 4;
   if (branch_sampling_) {
     feature_count++;
@@ -1003,9 +1013,20 @@ bool RecordCommand::DumpFileFeature() {
     }
     uint32_t dso_type = dso->type();
     uint64_t min_vaddr = dso->MinVirtualAddress();
+
+    // Dumping all symbols in hit files takes too much space, so only dump
+    // needed symbols.
     const std::vector<Symbol>& symbols = dso->GetSymbols();
+    std::vector<const Symbol*> dump_symbols;
+    for (const auto& sym : symbols) {
+      if (sym.HasDumpId()) {
+        dump_symbols.push_back(&sym);
+      }
+    }
+    std::sort(dump_symbols.begin(), dump_symbols.end(), Symbol::CompareByAddr);
+
     if (!record_file_writer_->WriteFileFeature(dso->Path(), dso_type, min_vaddr,
-                                               symbols)) {
+                                               dump_symbols)) {
       return false;
     }
   }
@@ -1017,17 +1038,53 @@ void RecordCommand::CollectHitFileInfo(const SampleRecord& r) {
       thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
   const MapEntry* map =
       thread_tree_.FindMap(thread, r.ip_data.ip, r.InKernel());
-  if (!map->dso->HasDumpId()) {
-    map->dso->CreateDumpId();
+  Dso* dso = map->dso;
+  const Symbol* symbol;
+  if (dump_symbols_) {
+    symbol = thread_tree_.FindSymbol(map, r.ip_data.ip, nullptr, &dso);
+    if (!symbol->HasDumpId()) {
+      dso->CreateSymbolDumpId(symbol);
+    }
+  }
+  if (!dso->HasDumpId()) {
+    dso->CreateDumpId();
   }
   if (r.sample_type & PERF_SAMPLE_CALLCHAIN) {
-    size_t ip_nr = r.callchain_data.ip_nr;
-    const uint64_t* ips = r.callchain_data.ips;
-    for (size_t i = 0; i < ip_nr; ++i) {
-      // Even if a sample is in kernel, its callchain can be in user space.
-      map = thread_tree_.FindMap(thread, ips[i]);
-      if (!map->dso->HasDumpId()) {
-        map->dso->CreateDumpId();
+    bool in_kernel = r.InKernel();
+    bool first_ip = true;
+    for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) {
+      uint64_t ip = r.callchain_data.ips[i];
+      if (ip >= PERF_CONTEXT_MAX) {
+        switch (ip) {
+          case PERF_CONTEXT_KERNEL:
+            in_kernel = true;
+            break;
+          case PERF_CONTEXT_USER:
+            in_kernel = false;
+            break;
+          default:
+            LOG(DEBUG) << "Unexpected perf_context in callchain: " << std::hex
+                       << ip;
+        }
+      } else {
+        if (first_ip) {
+          first_ip = false;
+          // Remove duplication with sample ip.
+          if (ip == r.ip_data.ip) {
+            continue;
+          }
+        }
+        map = thread_tree_.FindMap(thread, ip, in_kernel);
+        dso = map->dso;
+        if (dump_symbols_) {
+          symbol = thread_tree_.FindSymbol(map, ip, nullptr, &dso);
+          if (!symbol->HasDumpId()) {
+            dso->CreateSymbolDumpId(symbol);
+          }
+        }
+        if (!dso->HasDumpId()) {
+          dso->CreateDumpId();
+        }
       }
     }
   }
index f74f6c8..886d1b4 100644 (file)
@@ -465,14 +465,6 @@ static bool CompareDsoByDumpId(Dso* d1, Dso* d2) {
   return id1 < id2;
 }
 
-static bool CompareSymbolByDumpId(const Symbol* s1, const Symbol* s2) {
-  uint32_t id1 = UINT_MAX;
-  s1->GetDumpId(&id1);
-  uint32_t id2 = UINT_MAX;
-  s2->GetDumpId(&id2);
-  return id1 < id2;
-}
-
 bool ReportSampleCommand::PrintFileInfoInProtobuf() {
   std::vector<Dso*> dsos = thread_tree_.GetAllDsos();
   std::sort(dsos.begin(), dsos.end(), CompareDsoByDumpId);
@@ -492,7 +484,8 @@ bool ReportSampleCommand::PrintFileInfoInProtobuf() {
         dump_symbols.push_back(&sym);
       }
     }
-    std::sort(dump_symbols.begin(), dump_symbols.end(), CompareSymbolByDumpId);
+    std::sort(dump_symbols.begin(), dump_symbols.end(),
+              Symbol::CompareByDumpId);
 
     for (const auto& sym : dump_symbols) {
       std::string* symbol = file->add_symbol();
index 1c59e60..c889416 100644 (file)
@@ -176,10 +176,6 @@ Dso::~Dso() {
   }
 }
 
-static bool CompareSymbol(const Symbol& symbol1, const Symbol& symbol2) {
-  return symbol1.addr < symbol2.addr;
-}
-
 uint32_t Dso::CreateDumpId() {
   CHECK(!HasDumpId());
   return dump_id_ = g_dump_id_++;
@@ -196,7 +192,9 @@ const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
     Load();
   }
   if (!symbols_.empty()) {
-    auto it = std::upper_bound(symbols_.begin(), symbols_.end(), Symbol("", vaddr_in_dso, 0), CompareSymbol);
+    auto it = std::upper_bound(symbols_.begin(), symbols_.end(),
+                               Symbol("", vaddr_in_dso, 0),
+                               Symbol::CompareValueByAddr);
     if (it != symbols_.begin()) {
       --it;
       if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) {
@@ -275,7 +273,7 @@ void Dso::Load() {
     }
   }
   if (result) {
-    std::sort(symbols_.begin(), symbols_.end(), CompareSymbol);
+    std::sort(symbols_.begin(), symbols_.end(), Symbol::CompareValueByAddr);
     FixupSymbolLength();
   } else {
     symbols_.clear();
index 75290a7..17b3988 100644 (file)
@@ -46,6 +46,22 @@ struct Symbol {
     return true;
   }
 
+  static bool CompareByDumpId(const Symbol* s1, const Symbol* s2) {
+    uint32_t id1 = UINT_MAX;
+    s1->GetDumpId(&id1);
+    uint32_t id2 = UINT_MAX;
+    s2->GetDumpId(&id2);
+    return id1 < id2;
+  }
+
+  static bool CompareByAddr(const Symbol* s1, const Symbol* s2) {
+    return s1->addr < s2->addr;
+  }
+
+  static bool CompareValueByAddr(const Symbol& s1, const Symbol& s2) {
+    return s1.addr < s2.addr;
+  }
+
  private:
   const char* name_;
   mutable const char* demangled_name_;
index 13b372f..a2a5b5b 100644 (file)
@@ -45,6 +45,8 @@ class RecordFileWriter {
   bool WriteAttrSection(const std::vector<EventAttrWithId>& attr_ids);
   bool WriteRecord(const Record& record);
 
+  bool ReadDataSection(const std::function<void(const Record*)>& callback);
+
   bool BeginWriteFeatures(size_t feature_count);
   bool WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records);
   bool WriteFeatureString(int feature, const std::string& s);
@@ -53,7 +55,7 @@ class RecordFileWriter {
   bool WriteFileFeature(const std::string& file_path,
                         uint32_t file_type,
                         uint64_t min_vaddr,
-                        const std::vector<Symbol>& symbols);
+                        const std::vector<const Symbol*>& symbols);
   bool EndWriteFeatures();
 
   // Normally, Close() should be called after writing. But if something
@@ -69,6 +71,7 @@ class RecordFileWriter {
   bool WriteFileHeader();
   bool WriteData(const void* buf, size_t len);
   bool Write(const void* buf, size_t len);
+  bool Read(void* buf, size_t len);
   bool GetFilePos(uint64_t* file_pos);
   bool WriteStringWithLength(const std::string& s);
   bool WriteFeatureBegin(int feature);
index 1eb072a..802a66d 100644 (file)
@@ -173,6 +173,39 @@ bool RecordFileWriter::Write(const void* buf, size_t len) {
   return true;
 }
 
+bool RecordFileWriter::Read(void* buf, size_t len) {
+  if (len != 0u && fread(buf, len, 1, record_fp_) != 1) {
+    PLOG(ERROR) << "failed to read record file '" << filename_ << "'";
+    return false;
+  }
+  return true;
+}
+
+bool RecordFileWriter::ReadDataSection(const std::function<void(const Record*)>& callback) {
+  if (fseek(record_fp_, data_section_offset_, SEEK_SET) == -1) {
+    PLOG(ERROR) << "fseek() failed";
+    return false;
+  }
+  std::vector<char> record_buf(512);
+  uint64_t read_pos = 0;
+  while (read_pos < data_section_size_) {
+    if (!Read(record_buf.data(), Record::header_size())) {
+      return false;
+    }
+    RecordHeader header(record_buf.data());
+    if (record_buf.size() < header.size) {
+      record_buf.resize(header.size);
+    }
+    if (!Read(record_buf.data() + Record::header_size(), header.size - Record::header_size())) {
+      return false;
+    }
+    read_pos += header.size;
+    std::unique_ptr<Record> r = ReadRecordFromBuffer(event_attr_, header.type, record_buf.data());
+    callback(r.get());
+  }
+  return true;
+}
+
 bool RecordFileWriter::GetFilePos(uint64_t* file_pos) {
   off_t offset = ftello(record_fp_);
   if (offset == -1) {
@@ -264,11 +297,11 @@ bool RecordFileWriter::WriteBranchStackFeature() {
 bool RecordFileWriter::WriteFileFeature(const std::string& file_path,
                                         uint32_t file_type,
                                         uint64_t min_vaddr,
-                                        const std::vector<Symbol>& symbols) {
+                                        const std::vector<const Symbol*>& symbols) {
   uint32_t size = file_path.size() + 1 + sizeof(uint32_t) * 2 +
       sizeof(uint64_t) + symbols.size() * (sizeof(uint64_t) + sizeof(uint32_t));
   for (const auto& symbol : symbols) {
-    size += strlen(symbol.Name()) + 1;
+    size += strlen(symbol->Name()) + 1;
   }
   std::vector<char> buf(sizeof(uint32_t) + size);
   char* p = buf.data();
@@ -279,10 +312,10 @@ bool RecordFileWriter::WriteFileFeature(const std::string& file_path,
   uint32_t symbol_count = static_cast<uint32_t>(symbols.size());
   MoveToBinaryFormat(symbol_count, p);
   for (const auto& symbol : symbols) {
-    MoveToBinaryFormat(symbol.addr, p);
-    uint32_t len = symbol.len;
+    MoveToBinaryFormat(symbol->addr, p);
+    uint32_t len = symbol->len;
     MoveToBinaryFormat(len, p);
-    MoveToBinaryFormat(symbol.Name(), strlen(symbol.Name()) + 1, p);
+    MoveToBinaryFormat(symbol->Name(), strlen(symbol->Name()) + 1, p);
   }
   CHECK_EQ(buf.size(), static_cast<size_t>(p - buf.data()));