OSDN Git Service

simpleperf: use file records in protobuf output.
authorYabin Cui <yabinc@google.com>
Wed, 19 Oct 2016 22:06:29 +0000 (15:06 -0700)
committerYabin Cui <yabinc@google.com>
Thu, 10 Nov 2016 23:38:58 +0000 (15:38 -0800)
Dump file name and symbol name for each CallChainEntry
takes too much space. So instead we store file_id and
symbol_id for each CallChainEntry, and store file
records separately.

In CallChainEntry, replace ip with vaddr_in_file, because
vaddr_in_file is more useful in finding instructions in
elf file.

Bug: http://b/32210800
Test: simpleperf_unit_test.
Change-Id: I85542db21acbaa4d81b3c3aa7f9215f2d23c4878

simpleperf/cmd_record.cpp
simpleperf/cmd_report_sample.cpp
simpleperf/cmd_report_sample_test.cpp
simpleperf/dso.cpp
simpleperf/dso.h
simpleperf/report_sample.proto
simpleperf/thread_tree.cpp
simpleperf/thread_tree.h

index 58f4813..68c50b4 100644 (file)
@@ -944,7 +944,7 @@ bool RecordCommand::DumpBuildIdFeature() {
   BuildId build_id;
   std::vector<Dso*> dso_v = thread_tree_.GetAllDsos();
   for (Dso* dso : dso_v) {
-    if (!dso->IsHit()) {
+    if (!dso->HasDumpId()) {
       continue;
     }
     if (dso->type() == DSO_KERNEL) {
@@ -998,7 +998,7 @@ bool RecordCommand::DumpBuildIdFeature() {
 bool RecordCommand::DumpFileFeature() {
   std::vector<Dso*> dso_v = thread_tree_.GetAllDsos();
   for (Dso* dso : dso_v) {
-    if (!dso->IsHit()) {
+    if (!dso->HasDumpId()) {
       continue;
     }
     uint32_t dso_type = dso->type();
@@ -1017,14 +1017,18 @@ 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());
-  map->dso->SetHitFlag();
+  if (!map->dso->HasDumpId()) {
+    map->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]);
-      map->dso->SetHitFlag();
+      if (!map->dso->HasDumpId()) {
+        map->dso->CreateDumpId();
+      }
     }
   }
 }
index eeb6b6e..f74f6c8 100644 (file)
@@ -79,10 +79,7 @@ class ReportSampleCommand : public Command {
         report_fp_(nullptr),
         coded_os_(nullptr),
         sample_count_(0),
-        lost_count_(0) {
-    thread_tree_.ShowMarkForUnknownSymbol();
-    thread_tree_.ShowIpForUnknownSymbol();
-  }
+        lost_count_(0) {}
 
   bool Run(const std::vector<std::string>& args) override;
 
@@ -91,7 +88,14 @@ class ReportSampleCommand : public Command {
   bool DumpProtobufReport(const std::string& filename);
   bool ProcessRecord(std::unique_ptr<Record> record);
   bool PrintSampleRecordInProtobuf(const SampleRecord& record);
+  void GetCallEntry(const ThreadEntry* thread, bool in_kernel, uint64_t ip,
+                    uint64_t* pvaddr_in_file, uint32_t* pfile_id,
+                    int32_t* psymbol_id);
+  void GetCallEntry(const ThreadEntry* thread, bool in_kernel, uint64_t ip,
+                    uint64_t* pvaddr_in_file, Dso** pdso,
+                    const Symbol** psymbol);
   bool PrintLostSituationInProtobuf();
+  bool PrintFileInfoInProtobuf();
   bool PrintSampleRecord(const SampleRecord& record);
   void PrintLostSituation();
 
@@ -113,34 +117,45 @@ bool ReportSampleCommand::Run(const std::vector<std::string>& args) {
   if (!ParseOptions(args)) {
     return false;
   }
+  // 2. Prepare report fp.
+  report_fp_ = stdout;
+  std::unique_ptr<FILE, decltype(&fclose)> fp(nullptr, fclose);
+  if (!report_filename_.empty()) {
+    const char* open_mode = "w";
+    if (!dump_protobuf_report_file_.empty() && use_protobuf_) {
+      open_mode = "wb";
+    }
+    fp.reset(fopen(report_filename_.c_str(), open_mode));
+    if (fp == nullptr) {
+      PLOG(ERROR) << "failed to open " << report_filename_;
+      return false;
+    }
+    report_fp_ = fp.get();
+  }
+
+  // 3. Dump protobuf report.
   if (!dump_protobuf_report_file_.empty()) {
     return DumpProtobufReport(dump_protobuf_report_file_);
   }
-  if (use_protobuf_) {
-    GOOGLE_PROTOBUF_VERIFY_VERSION;
-  }
 
-  // 2. Open record file.
+  // 4. Open record file.
   record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
   if (record_file_reader_ == nullptr) {
     return false;
   }
   record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
 
-  // 3. Prepare report output stream.
-  report_fp_ = stdout;
-  std::unique_ptr<FILE, decltype(&fclose)> fp(nullptr, fclose);
+  if (use_protobuf_) {
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+  } else {
+    thread_tree_.ShowMarkForUnknownSymbol();
+    thread_tree_.ShowIpForUnknownSymbol();
+  }
+
+  // 5. Prepare protobuf output stream.
   std::unique_ptr<ProtobufFileWriter> protobuf_writer;
   std::unique_ptr<google::protobuf::io::CopyingOutputStreamAdaptor> protobuf_os;
   std::unique_ptr<google::protobuf::io::CodedOutputStream> protobuf_coded_os;
-  if (!report_filename_.empty()) {
-    fp.reset(fopen(report_filename_.c_str(), use_protobuf_ ? "wb" : "w"));
-    if (fp == nullptr) {
-      PLOG(ERROR) << "failed to open " << report_filename_;
-      return false;
-    }
-    report_fp_ = fp.get();
-  }
   if (use_protobuf_) {
     protobuf_writer.reset(new ProtobufFileWriter(report_fp_));
     protobuf_os.reset(new google::protobuf::io::CopyingOutputStreamAdaptor(
@@ -150,7 +165,7 @@ bool ReportSampleCommand::Run(const std::vector<std::string>& args) {
     coded_os_ = protobuf_coded_os.get();
   }
 
-  // 4. Read record file, and print samples online.
+  // 6. Read record file, and print samples online.
   if (!record_file_reader_->ReadDataSection(
           [this](std::unique_ptr<Record> record) {
             return ProcessRecord(std::move(record));
@@ -162,6 +177,9 @@ bool ReportSampleCommand::Run(const std::vector<std::string>& args) {
     if (!PrintLostSituationInProtobuf()) {
       return false;
     }
+    if (!PrintFileInfoInProtobuf()) {
+      return false;
+    }
     coded_os_->WriteLittleEndian32(0);
     if (coded_os_->HadError()) {
       LOG(ERROR) << "print protobuf report failed";
@@ -225,6 +243,13 @@ bool ReportSampleCommand::DumpProtobufReport(const std::string& filename) {
   ProtobufFileReader protobuf_reader(fp.get());
   google::protobuf::io::CopyingInputStreamAdaptor adaptor(&protobuf_reader);
   google::protobuf::io::CodedInputStream coded_is(&adaptor);
+  // map from file_id to max_symbol_id requested on the file.
+  std::unordered_map<uint32_t, int32_t> max_symbol_id_map;
+  // files[file_id] is the number of symbols in the file.
+  std::vector<uint32_t> files;
+  uint32_t max_message_size = 64 * (1 << 20);
+  uint32_t warning_message_size = 512 * (1 << 20);
+  coded_is.SetTotalBytesLimit(max_message_size, warning_message_size);
   while (true) {
     uint32_t size;
     if (!coded_is.ReadLittleEndian32(&size)) {
@@ -234,6 +259,11 @@ bool ReportSampleCommand::DumpProtobufReport(const std::string& filename) {
     if (size == 0) {
       break;
     }
+    // Handle files having large symbol table.
+    if (size > max_message_size) {
+      max_message_size = size;
+      coded_is.SetTotalBytesLimit(max_message_size, warning_message_size);
+    }
     auto limit = coded_is.PushLimit(size);
     proto::Record proto_record;
     if (!proto_record.ParseFromCodedStream(&coded_is)) {
@@ -241,26 +271,64 @@ bool ReportSampleCommand::DumpProtobufReport(const std::string& filename) {
       return false;
     }
     coded_is.PopLimit(limit);
-    if (proto_record.type() == proto::Record_Type_SAMPLE) {
+    if (proto_record.has_sample()) {
       auto& sample = proto_record.sample();
       static size_t sample_count = 0;
-      PrintIndented(0, "sample %zu:\n", ++sample_count);
-      PrintIndented(1, "time: %" PRIu64 "\n", sample.time());
-      PrintIndented(1, "thread_id: %d\n", sample.thread_id());
-      PrintIndented(1, "callchain:\n");
-      for (int j = 0; j < sample.callchain_size(); ++j) {
-        const proto::Sample_CallChainEntry& callchain = sample.callchain(j);
-        PrintIndented(2, "ip: %" PRIx64 "\n", callchain.ip());
-        PrintIndented(2, "dso: %s\n", callchain.file().c_str());
-        PrintIndented(2, "symbol: %s\n", callchain.symbol().c_str());
+      FprintIndented(report_fp_, 0, "sample %zu:\n", ++sample_count);
+      FprintIndented(report_fp_, 1, "time: %" PRIu64 "\n", sample.time());
+      FprintIndented(report_fp_, 1, "thread_id: %d\n", sample.thread_id());
+      FprintIndented(report_fp_, 1, "callchain:\n");
+      for (int i = 0; i < sample.callchain_size(); ++i) {
+        const proto::Sample_CallChainEntry& callchain = sample.callchain(i);
+        FprintIndented(report_fp_, 2, "vaddr_in_file: %" PRIx64 "\n",
+                       callchain.vaddr_in_file());
+        FprintIndented(report_fp_, 2, "file_id: %u\n", callchain.file_id());
+        int32_t symbol_id = callchain.symbol_id();
+        FprintIndented(report_fp_, 2, "symbol_id: %d\n", symbol_id);
+        if (symbol_id < -1) {
+          LOG(ERROR) << "unexpected symbol_id " << symbol_id;
+          return false;
+        }
+        if (symbol_id != -1) {
+          max_symbol_id_map[callchain.file_id()] =
+              std::max(max_symbol_id_map[callchain.file_id()], symbol_id);
+        }
       }
-    } else if (proto_record.type() == proto::Record_Type_LOST_SITUATION) {
+    } else if (proto_record.has_lost()) {
       auto& lost = proto_record.lost();
-      PrintIndented(0, "lost_situation:\n");
-      PrintIndented(1, "sample_count: %" PRIu64 "\n", lost.sample_count());
-      PrintIndented(1, "lost_count: %" PRIu64 "\n", lost.lost_count());
+      FprintIndented(report_fp_, 0, "lost_situation:\n");
+      FprintIndented(report_fp_, 1, "sample_count: %" PRIu64 "\n",
+                     lost.sample_count());
+      FprintIndented(report_fp_, 1, "lost_count: %" PRIu64 "\n",
+                     lost.lost_count());
+    } else if (proto_record.has_file()) {
+      auto& file = proto_record.file();
+      FprintIndented(report_fp_, 0, "file:\n");
+      FprintIndented(report_fp_, 1, "id: %u\n", file.id());
+      FprintIndented(report_fp_, 1, "path: %s\n", file.path().c_str());
+      for (int i = 0; i < file.symbol_size(); ++i) {
+        FprintIndented(report_fp_, 1, "symbol: %s\n", file.symbol(i).c_str());
+      }
+      if (file.id() != files.size()) {
+        LOG(ERROR) << "file id doesn't increase orderly, expected "
+                   << files.size() << ", really " << file.id();
+        return false;
+      }
+      files.push_back(file.symbol_size());
     } else {
-      LOG(ERROR) << "unexpected record type " << proto_record.type();
+      LOG(ERROR) << "unexpected record type ";
+      return false;
+    }
+  }
+  for (auto pair : max_symbol_id_map) {
+    if (pair.first >= files.size()) {
+      LOG(ERROR) << "file_id(" << pair.first << ") >= file count ("
+                 << files.size() << ")";
+      return false;
+    }
+    if (static_cast<uint32_t>(pair.second) >= files[pair.first]) {
+      LOG(ERROR) << "symbol_id(" << pair.second << ") >= symbol count ("
+                 << files[pair.first] << ") in file_id( " << pair.first << ")";
       return false;
     }
   }
@@ -285,21 +353,23 @@ bool ReportSampleCommand::ProcessRecord(std::unique_ptr<Record> record) {
 }
 
 bool ReportSampleCommand::PrintSampleRecordInProtobuf(const SampleRecord& r) {
+  uint64_t vaddr_in_file;
+  uint32_t file_id;
+  int32_t symbol_id;
   proto::Record proto_record;
-  proto_record.set_type(proto::Record_Type_SAMPLE);
   proto::Sample* sample = proto_record.mutable_sample();
   sample->set_time(r.time_data.time);
   sample->set_thread_id(r.tid_data.tid);
-  proto::Sample_CallChainEntry* callchain = sample->add_callchain();
-  callchain->set_ip(r.ip_data.ip);
 
   bool in_kernel = r.InKernel();
   const ThreadEntry* thread =
       thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
-  const MapEntry* map = thread_tree_.FindMap(thread, r.ip_data.ip, in_kernel);
-  const Symbol* symbol = thread_tree_.FindSymbol(map, r.ip_data.ip, nullptr);
-  callchain->set_symbol(symbol->DemangledName());
-  callchain->set_file(map->dso->Path());
+  GetCallEntry(thread, in_kernel, r.ip_data.ip, &vaddr_in_file, &file_id,
+               &symbol_id);
+  proto::Sample_CallChainEntry* callchain = sample->add_callchain();
+  callchain->set_vaddr_in_file(vaddr_in_file);
+  callchain->set_file_id(file_id);
+  callchain->set_symbol_id(symbol_id);
 
   if (show_callchain_ && (r.sample_type & PERF_SAMPLE_CALLCHAIN)) {
     bool first_ip = true;
@@ -325,12 +395,12 @@ bool ReportSampleCommand::PrintSampleRecordInProtobuf(const SampleRecord& r) {
             continue;
           }
         }
-        const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
-        const Symbol* symbol = thread_tree_.FindSymbol(map, ip, nullptr);
+        GetCallEntry(thread, in_kernel, ip, &vaddr_in_file, &file_id,
+                     &symbol_id);
         callchain = sample->add_callchain();
-        callchain->set_ip(ip);
-        callchain->set_symbol(symbol->DemangledName());
-        callchain->set_file(map->dso->Path());
+        callchain->set_vaddr_in_file(vaddr_in_file);
+        callchain->set_file_id(file_id);
+        callchain->set_symbol_id(symbol_id);
       }
     }
   }
@@ -342,9 +412,40 @@ bool ReportSampleCommand::PrintSampleRecordInProtobuf(const SampleRecord& r) {
   return true;
 }
 
+void ReportSampleCommand::GetCallEntry(const ThreadEntry* thread,
+                                       bool in_kernel, uint64_t ip,
+                                       uint64_t* pvaddr_in_file,
+                                       uint32_t* pfile_id,
+                                       int32_t* psymbol_id) {
+  Dso* dso;
+  const Symbol* symbol;
+  GetCallEntry(thread, in_kernel, ip, pvaddr_in_file, &dso, &symbol);
+  if (!dso->GetDumpId(pfile_id)) {
+    *pfile_id = dso->CreateDumpId();
+  }
+  if (symbol != thread_tree_.UnknownSymbol()) {
+    if (!symbol->GetDumpId(reinterpret_cast<uint32_t*>(psymbol_id))) {
+      *psymbol_id = dso->CreateSymbolDumpId(symbol);
+    }
+  } else {
+    *psymbol_id = -1;
+  }
+}
+
+void ReportSampleCommand::GetCallEntry(const ThreadEntry* thread,
+                                       bool in_kernel, uint64_t ip,
+                                       uint64_t* pvaddr_in_file, Dso** pdso,
+                                       const Symbol** psymbol) {
+  const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
+  *psymbol = thread_tree_.FindSymbol(map, ip, pvaddr_in_file, pdso);
+  // If we can't find symbol, use the dso shown in the map.
+  if (*psymbol == thread_tree_.UnknownSymbol()) {
+    *pdso = map->dso;
+  }
+}
+
 bool ReportSampleCommand::PrintLostSituationInProtobuf() {
   proto::Record proto_record;
-  proto_record.set_type(proto::Record_Type_LOST_SITUATION);
   proto::LostSituation* lost = proto_record.mutable_lost();
   lost->set_sample_count(sample_count_);
   lost->set_lost_count(lost_count_);
@@ -356,17 +457,70 @@ bool ReportSampleCommand::PrintLostSituationInProtobuf() {
   return true;
 }
 
+static bool CompareDsoByDumpId(Dso* d1, Dso* d2) {
+  uint32_t id1 = UINT_MAX;
+  d1->GetDumpId(&id1);
+  uint32_t id2 = UINT_MAX;
+  d2->GetDumpId(&id2);
+  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);
+  for (Dso* dso : dsos) {
+    uint32_t file_id;
+    if (!dso->GetDumpId(&file_id)) {
+      continue;
+    }
+    proto::Record proto_record;
+    proto::File* file = proto_record.mutable_file();
+    file->set_id(file_id);
+    file->set_path(dso->Path());
+    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(), CompareSymbolByDumpId);
+
+    for (const auto& sym : dump_symbols) {
+      std::string* symbol = file->add_symbol();
+      *symbol = sym->DemangledName();
+    }
+    coded_os_->WriteLittleEndian32(proto_record.ByteSize());
+    if (!proto_record.SerializeToCodedStream(coded_os_)) {
+      LOG(ERROR) << "failed to write file info to protobuf";
+      return false;
+    }
+  }
+  return true;
+}
+
 bool ReportSampleCommand::PrintSampleRecord(const SampleRecord& r) {
-  bool in_kernel = r.InKernel();
-  const ThreadEntry* thread =
-      thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
-  const MapEntry* map = thread_tree_.FindMap(thread, r.ip_data.ip, in_kernel);
-  const Symbol* symbol = thread_tree_.FindSymbol(map, r.ip_data.ip, nullptr);
+  uint64_t vaddr_in_file;
+  Dso* dso;
+  const Symbol* symbol;
+
   FprintIndented(report_fp_, 0, "sample:\n");
   FprintIndented(report_fp_, 1, "time: %" PRIu64 "\n", r.time_data.time);
   FprintIndented(report_fp_, 1, "thread_id: %d\n", r.tid_data.tid);
-  FprintIndented(report_fp_, 1, "ip: %" PRIx64 "\n", r.ip_data.ip);
-  FprintIndented(report_fp_, 1, "dso: %s\n", map->dso->Path().c_str());
+  bool in_kernel = r.InKernel();
+  const ThreadEntry* thread =
+      thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
+  GetCallEntry(thread, in_kernel, r.ip_data.ip, &vaddr_in_file, &dso, &symbol);
+  FprintIndented(report_fp_, 1, "vaddr_in_file: %" PRIx64 "\n", vaddr_in_file);
+  FprintIndented(report_fp_, 1, "file: %s\n", dso->Path().c_str());
   FprintIndented(report_fp_, 1, "symbol: %s\n", symbol->DemangledName());
 
   if (show_callchain_ && (r.sample_type & PERF_SAMPLE_CALLCHAIN)) {
@@ -394,10 +548,10 @@ bool ReportSampleCommand::PrintSampleRecord(const SampleRecord& r) {
             continue;
           }
         }
-        const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
-        const Symbol* symbol = thread_tree_.FindSymbol(map, ip, nullptr);
-        FprintIndented(report_fp_, 2, "ip: %" PRIx64 "\n", ip);
-        FprintIndented(report_fp_, 2, "dso: %s\n", map->dso->Path().c_str());
+        GetCallEntry(thread, in_kernel, ip, &vaddr_in_file, &dso, &symbol);
+        FprintIndented(report_fp_, 2, "vaddr_in_file: %" PRIx64 "\n",
+                       vaddr_in_file);
+        FprintIndented(report_fp_, 2, "file: %s\n", dso->Path().c_str());
         FprintIndented(report_fp_, 2, "symbol: %s\n", symbol->DemangledName());
       }
     }
index 42df179..d07f0fd 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <android-base/file.h>
 #include <android-base/test_utils.h>
 
 #include "command.h"
@@ -44,7 +45,12 @@ TEST(cmd_report_sample, show_callchain_option) {
 
 TEST(cmd_report_sample, protobuf_option) {
   TemporaryFile tmpfile;
+  TemporaryFile tmpfile2;
   ASSERT_TRUE(ReportSampleCmd()->Run({"-i", GetTestData(PERF_DATA_WITH_SYMBOLS),
                                       "-o", tmpfile.path, "--protobuf"}));
-  ASSERT_TRUE(ReportSampleCmd()->Run({"--dump-protobuf-report", tmpfile.path}));
+  ASSERT_TRUE(ReportSampleCmd()->Run(
+      {"--dump-protobuf-report", tmpfile.path, "-o", tmpfile2.path}));
+  std::string data;
+  ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, &data));
+  ASSERT_NE(data.find("file:"), std::string::npos);
 }
index 2950939..1c59e60 100644 (file)
@@ -38,7 +38,7 @@ Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len)
       len(len),
       name_(symbol_name_allocator.AllocateString(name)),
       demangled_name_(nullptr),
-      has_dumped_(false) {}
+      dump_id_(UINT_MAX) {}
 
 const char* Symbol::DemangledName() const {
   if (demangled_name_ == nullptr) {
@@ -58,6 +58,7 @@ std::string Dso::vmlinux_;
 std::string Dso::kallsyms_;
 std::unordered_map<std::string, BuildId> Dso::build_id_map_;
 size_t Dso::dso_count_;
+uint32_t Dso::g_dump_id_;
 
 void Dso::SetDemangle(bool demangle) { demangle_ = demangle; }
 
@@ -136,7 +137,11 @@ Dso::Dso(DsoType type, const std::string& path)
       debug_file_path_(path),
       min_vaddr_(std::numeric_limits<uint64_t>::max()),
       is_loaded_(false),
-      hit_flag_(false) {
+      dump_id_(UINT_MAX),
+      symbol_dump_id_(0) {
+  if (type_ == DSO_KERNEL) {
+    min_vaddr_ = 0;
+  }
   // Check if file matching path_ exists in symfs directory before using it as
   // debug_file_path_.
   if (!symfs_dir_.empty()) {
@@ -167,6 +172,7 @@ Dso::~Dso() {
     vmlinux_.clear();
     kallsyms_.clear();
     build_id_map_.clear();
+    g_dump_id_ = 0;
   }
 }
 
@@ -174,6 +180,17 @@ 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_++;
+}
+
+uint32_t Dso::CreateSymbolDumpId(const Symbol* symbol) {
+  CHECK(!symbol->HasDumpId());
+  symbol->dump_id_ = symbol_dump_id_++;
+  return symbol->dump_id_;
+}
+
 const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
   if (!is_loaded_) {
     Load();
index c464b73..75290a7 100644 (file)
@@ -26,6 +26,7 @@
 
 struct Symbol {
   uint64_t addr;
+  // TODO: make len uint32_t.
   uint64_t len;
 
   Symbol(const std::string& name, uint64_t addr, uint64_t len);
@@ -33,14 +34,24 @@ struct Symbol {
 
   const char* DemangledName() const;
 
-  bool HasDumped() const { return has_dumped_; }
+  bool HasDumpId() const {
+    return dump_id_ != UINT_MAX;
+  }
 
-  void SetDumped() const { has_dumped_ = true; }
+  bool GetDumpId(uint32_t* pdump_id) const {
+    if (!HasDumpId()) {
+      return false;
+    }
+    *pdump_id = dump_id_;
+    return true;
+  }
 
  private:
   const char* name_;
   mutable const char* demangled_name_;
-  mutable bool has_dumped_;
+  mutable uint32_t dump_id_;
+
+  friend class Dso;
 };
 
 enum DsoType {
@@ -52,7 +63,7 @@ enum DsoType {
 struct KernelSymbol;
 struct ElfFileSymbol;
 
-struct Dso {
+class Dso {
  public:
   static void SetDemangle(bool demangle);
   static std::string Demangle(const std::string& name);
@@ -80,9 +91,20 @@ struct Dso {
   // Return the file name without directory info.
   const std::string& FileName() const { return file_name_; }
 
-  // Set when there are samples hit in current dso.
-  void SetHitFlag() { hit_flag_ = true; }
-  bool IsHit() const { return hit_flag_; }
+  bool HasDumpId() {
+    return dump_id_ != UINT_MAX;
+  }
+
+  bool GetDumpId(uint32_t* pdump_id) {
+    if (!HasDumpId()) {
+      return false;
+    }
+    *pdump_id = dump_id_;
+    return true;
+  }
+
+  uint32_t CreateDumpId();
+  uint32_t CreateSymbolDumpId(const Symbol* symbol);
 
   // Return the minimum virtual address in program header.
   uint64_t MinVirtualAddress();
@@ -104,6 +126,7 @@ struct Dso {
   static std::string kallsyms_;
   static std::unordered_map<std::string, BuildId> build_id_map_;
   static size_t dso_count_;
+  static uint32_t g_dump_id_;
 
   Dso(DsoType type, const std::string& path);
   void Load();
@@ -127,7 +150,10 @@ struct Dso {
   // unknown symbols are like [libc.so+0x1234].
   std::unordered_map<uint64_t, Symbol> unknown_symbols_;
   bool is_loaded_;
-  bool hit_flag_;
+  // Used to identify current dso if it needs to be dumped.
+  uint32_t dump_id_;
+  // Used to assign dump_id for symbols in current dso.
+  uint32_t symbol_dump_id_;
 };
 
 const char* DsoTypeToString(DsoType dso_type);
index d6c42e6..c591aba 100644 (file)
@@ -16,15 +16,22 @@ option java_outer_classname = "SimpleperfReport";
 
 message Sample {
   optional uint64 time = 1;
+  optional int32 thread_id = 2;
 
   message CallChainEntry {
-    optional uint64 ip = 1;
-    optional string symbol = 2;
-    optional string file = 3;
+    // virtual address of the instruction in elf file
+    optional uint64 vaddr_in_file = 1;
+
+    // index of the elf file containing the instruction
+    optional uint32 file_id = 2;
+
+    // symbol_id refers to the name of the function containing the instruction.
+    // If the function name is found, it is a valid index in the symbol table
+    // of File with 'id' field being file_id, otherwise it is -1.
+    optional int32 symbol_id = 3;
   }
 
-  repeated CallChainEntry callchain = 2;
-  optional int32 thread_id = 3;
+  repeated CallChainEntry callchain = 3;
 }
 
 message LostSituation {
@@ -32,17 +39,21 @@ message LostSituation {
   optional uint64 lost_count = 2;
 }
 
-message Record {
-  enum Type {
-    UNKOWN = 0;
-    SAMPLE = 1;
-    LOST_SITUATION = 2;
-  }
+message File {
+  // unique id for each file, starting from 0, and add 1 each time.
+  optional uint32 id = 1;
 
-  // Identifies which field is filled in.
-  optional Type type = 1;
+  // file path, like /system/lib/libc.so.
+  optional string path = 2;
 
-  // One of the following will be filled in.
-  optional Sample sample = 2;
-  optional LostSituation lost = 3;
+  // symbol table of the file.
+  repeated string symbol = 3;
+}
+
+message Record {
+  oneof record_data {
+    Sample sample = 1;
+    LostSituation lost = 2;
+    File file = 3;
+  }
 }
\ No newline at end of file
index 5868502..84cfb59 100644 (file)
@@ -209,14 +209,10 @@ const MapEntry* ThreadTree::FindMap(const ThreadEntry* thread, uint64_t ip) {
 }
 
 const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip,
-                                     uint64_t* pvaddr_in_file) {
+                                     uint64_t* pvaddr_in_file, Dso** pdso) {
   uint64_t vaddr_in_file;
   Dso* dso = map->dso;
-  if (dso == kernel_dso_.get()) {
-    vaddr_in_file = ip;
-  } else {
-    vaddr_in_file = ip - map->start_addr + map->dso->MinVirtualAddress();
-  }
+  vaddr_in_file = ip - map->start_addr + map->dso->MinVirtualAddress();
   const Symbol* symbol = dso->FindSymbol(vaddr_in_file);
   if (symbol == nullptr && map->in_kernel && dso != kernel_dso_.get()) {
     // It is in a kernel module, but we can't find the kernel module file, or
@@ -241,6 +237,9 @@ const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip,
   if (pvaddr_in_file != nullptr) {
     *pvaddr_in_file = vaddr_in_file;
   }
+  if (pdso != nullptr) {
+    *pdso = dso;
+  }
   return symbol;
 }
 
index cd82d20..3108fe4 100644 (file)
@@ -96,7 +96,7 @@ class ThreadTree {
   // Find map for an ip address when we don't know whether it is in kernel.
   const MapEntry* FindMap(const ThreadEntry* thread, uint64_t ip);
   const Symbol* FindSymbol(const MapEntry* map, uint64_t ip,
-                           uint64_t* pvaddr_in_file);
+                           uint64_t* pvaddr_in_file, Dso** pdso = nullptr);
   const Symbol* FindKernelSymbol(uint64_t ip);
   const Symbol* UnknownSymbol() const { return &unknown_symbol_; }