OSDN Git Service

simpleperf: create libsimpleperf_record library.
[android-x86/system-extras.git] / simpleperf / cmd_report.cpp
index fe93dbc..29cf743 100644 (file)
 #include <unordered_set>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 #include "command.h"
 #include "dwarf_unwind.h"
-#include "environment.h"
 #include "event_attr.h"
 #include "event_type.h"
 #include "perf_regs.h"
@@ -88,12 +89,17 @@ struct SampleEntry {
   // The data member 'callchain' can only move, not copy.
   SampleEntry(SampleEntry&&) = default;
   SampleEntry(SampleEntry&) = delete;
+
+  uint64_t GetPeriod() const {
+    return period;
+  }
 };
 
 struct SampleTree {
   std::vector<SampleEntry*> samples;
   uint64_t total_samples;
   uint64_t total_period;
+  uint64_t total_error_callchains;
 };
 
 BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file);
@@ -107,7 +113,8 @@ class ReportCmdSampleTreeBuilder
       : SampleTreeBuilder(sample_comparator),
         thread_tree_(thread_tree),
         total_samples_(0),
-        total_period_(0) {}
+        total_period_(0),
+        total_error_callchains_(0) {}
 
   void SetFilters(const std::unordered_set<int>& pid_filter,
                   const std::unordered_set<int>& tid_filter,
@@ -121,11 +128,13 @@ class ReportCmdSampleTreeBuilder
     symbol_filter_ = symbol_filter;
   }
 
-  SampleTree GetSampleTree() const {
+  SampleTree GetSampleTree() {
+    AddCallChainDuplicateInfo();
     SampleTree sample_tree;
     sample_tree.samples = GetSamples();
     sample_tree.total_samples = total_samples_;
     sample_tree.total_period = total_period_;
+    sample_tree.total_error_callchains = total_error_callchains_;
     return sample_tree;
   }
 
@@ -173,10 +182,16 @@ class ReportCmdSampleTreeBuilder
                                      const uint64_t& acc_info) override {
     const ThreadEntry* thread = sample->thread;
     const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel);
+    if (thread_tree_->IsUnknownDso(map->dso)) {
+      // The unwinders can give wrong ip addresses, which can't map to a valid dso. Skip them.
+      total_error_callchains_++;
+      return nullptr;
+    }
     uint64_t vaddr_in_file;
     const Symbol* symbol = thread_tree_->FindSymbol(map, ip, &vaddr_in_file);
     std::unique_ptr<SampleEntry> callchain_sample(new SampleEntry(
         sample->time, 0, acc_info, 0, thread, map, symbol, vaddr_in_file));
+    callchain_sample->thread_comm = sample->thread_comm;
     return InsertCallChainSample(std::move(callchain_sample), callchain);
   }
 
@@ -235,6 +250,32 @@ class ReportCmdSampleTreeBuilder
 
   uint64_t total_samples_;
   uint64_t total_period_;
+  uint64_t total_error_callchains_;
+};
+
+struct SampleTreeBuilderOptions {
+  SampleComparator<SampleEntry> comparator;
+  ThreadTree* thread_tree;
+  std::unordered_set<std::string> comm_filter;
+  std::unordered_set<std::string> dso_filter;
+  std::unordered_set<std::string> symbol_filter;
+  std::unordered_set<int> pid_filter;
+  std::unordered_set<int> tid_filter;
+  bool use_branch_address;
+  bool accumulate_callchain;
+  bool build_callchain;
+  bool use_caller_as_callchain_root;
+  bool strict_unwind_arch_check;
+
+  std::unique_ptr<ReportCmdSampleTreeBuilder> CreateSampleTreeBuilder() {
+    std::unique_ptr<ReportCmdSampleTreeBuilder> builder(
+        new ReportCmdSampleTreeBuilder(comparator, thread_tree));
+    builder->SetFilters(pid_filter, tid_filter, comm_filter, dso_filter, symbol_filter);
+    builder->SetBranchSampleOption(use_branch_address);
+    builder->SetCallChainSampleOptions(accumulate_callchain, build_callchain,
+                                       use_caller_as_callchain_root, strict_unwind_arch_check);
+    return builder;
+  }
 };
 
 using ReportCmdSampleTreeSorter = SampleTreeSorter<SampleEntry>;
@@ -266,22 +307,29 @@ class ReportCommand : public Command {
             "report", "report sampling information in perf.data",
             // clang-format off
 "Usage: simpleperf report [options]\n"
+"The default options are: -i perf.data --sort comm,pid,tid,dso,symbol.\n"
 "-b    Use the branch-to addresses in sampled take branches instead of the\n"
 "      instruction addresses. Only valid for perf.data recorded with -b/-j\n"
 "      option.\n"
 "--children    Print the overhead accumulated by appearing in the callchain.\n"
 "--comms comm1,comm2,...   Report only for selected comms.\n"
 "--dsos dso1,dso2,...      Report only for selected dsos.\n"
+"--full-callgraph  Print full call graph. Used with -g option. By default,\n"
+"                  brief call graph is printed.\n"
 "-g [callee|caller]    Print call graph. If callee mode is used, the graph\n"
 "                      shows how functions are called from others. Otherwise,\n"
 "                      the graph shows how functions call others.\n"
 "                      Default is caller mode.\n"
 "-i <file>  Specify path of record file, default is perf.data.\n"
+"--kallsyms <file>     Set the file to read kernel symbols.\n"
+"--max-stack <frames>  Set max stack frames shown when printing call graph.\n"
 "-n         Print the sample count for each item.\n"
 "--no-demangle         Don't demangle symbol names.\n"
 "--no-show-ip          Don't show vaddr in file for unknown symbols.\n"
 "-o report_file_name   Set report file name, default is stdout.\n"
+"--percent-limit <percent>  Set min percentage shown when printing call graph.\n"
 "--pids pid1,pid2,...  Report only for selected pids.\n"
+"--raw-period          Report period count instead of period percentage.\n"
 "--sort key1,key2,...  Select keys used to sort and print the report. The\n"
 "                      appearance order of keys decides the order of keys used\n"
 "                      to sort and print the report.\n"
@@ -313,7 +361,11 @@ class ReportCommand : public Command {
         system_wide_collection_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
-        callgraph_show_callee_(false) {}
+        callgraph_show_callee_(false),
+        callgraph_max_stack_(UINT32_MAX),
+        callgraph_percent_limit_(0),
+        raw_period_(false),
+        brief_callgraph_(true) {}
 
   bool Run(const std::vector<std::string>& args);
 
@@ -332,8 +384,11 @@ class ReportCommand : public Command {
   std::unique_ptr<RecordFileReader> record_file_reader_;
   std::vector<EventAttrWithName> event_attrs_;
   ThreadTree thread_tree_;
-  SampleTree sample_tree_;
-  std::unique_ptr<ReportCmdSampleTreeBuilder> sample_tree_builder_;
+  // Create a SampleTreeBuilder and SampleTree for each event_attr.
+  std::vector<SampleTree> sample_tree_;
+  SampleTreeBuilderOptions sample_tree_builder_options_;
+  std::vector<std::unique_ptr<ReportCmdSampleTreeBuilder>> sample_tree_builder_;
+
   std::unique_ptr<ReportCmdSampleTreeSorter> sample_tree_sorter_;
   std::unique_ptr<ReportCmdSampleTreeDisplayer> sample_tree_displayer_;
   bool use_branch_address_;
@@ -342,6 +397,10 @@ class ReportCommand : public Command {
   bool accumulate_callchain_;
   bool print_callgraph_;
   bool callgraph_show_callee_;
+  uint32_t callgraph_max_stack_;
+  double callgraph_percent_limit_;
+  bool raw_period_;
+  bool brief_callgraph_;
 
   std::string report_filename_;
 };
@@ -384,11 +443,6 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
   std::string vmlinux;
   bool print_sample_count = false;
   std::vector<std::string> sort_keys = {"comm", "pid", "tid", "dso", "symbol"};
-  std::unordered_set<std::string> comm_filter;
-  std::unordered_set<std::string> dso_filter;
-  std::unordered_set<std::string> symbol_filter;
-  std::unordered_set<int> pid_filter;
-  std::unordered_set<int> tid_filter;
 
   for (size_t i = 0; i < args.size(); ++i) {
     if (args[i] == "-b") {
@@ -397,13 +451,15 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
       accumulate_callchain_ = true;
     } else if (args[i] == "--comms" || args[i] == "--dsos") {
       std::unordered_set<std::string>& filter =
-          (args[i] == "--comms" ? comm_filter : dso_filter);
+          (args[i] == "--comms" ? sample_tree_builder_options_.comm_filter
+                                : sample_tree_builder_options_.dso_filter);
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
       std::vector<std::string> strs = android::base::Split(args[i], ",");
       filter.insert(strs.begin(), strs.end());
-
+    } else if (args[i] == "--full-callgraph") {
+      brief_callgraph_ = false;
     } else if (args[i] == "-g") {
       print_callgraph_ = true;
       accumulate_callchain_ = true;
@@ -424,6 +480,24 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
       }
       record_filename_ = args[i];
 
+    } else if (args[i] == "--kallsyms") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      std::string kallsyms;
+      if (!android::base::ReadFileToString(args[i], &kallsyms)) {
+        LOG(ERROR) << "Can't read kernel symbols from " << args[i];
+        return false;
+      }
+      Dso::SetKallsyms(kallsyms);
+    } else if (args[i] == "--max-stack") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      if (!android::base::ParseUint(args[i].c_str(), &callgraph_max_stack_)) {
+        LOG(ERROR) << "invalid arg for --max-stack: " << args[i];
+        return false;
+      }
     } else if (args[i] == "-n") {
       print_sample_count = true;
 
@@ -436,11 +510,19 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
         return false;
       }
       report_filename_ = args[i];
-
+    } else if (args[i] == "--percent-limit") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      if (!android::base::ParseDouble(args[i].c_str(),
+                                      &callgraph_percent_limit_, 0.0)) {
+        LOG(ERROR) << "invalid arg for --percent-limit: " << args[i];
+      }
     } else if (args[i] == "--pids" || args[i] == "--tids") {
       const std::string& option = args[i];
       std::unordered_set<int>& filter =
-          (option == "--pids" ? pid_filter : tid_filter);
+          (option == "--pids" ? sample_tree_builder_options_.pid_filter
+                              : sample_tree_builder_options_.tid_filter);
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
@@ -453,7 +535,8 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
         }
         filter.insert(id);
       }
-
+    } else if (args[i] == "--raw-period") {
+      raw_period_ = true;
     } else if (args[i] == "--sort") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -464,7 +547,7 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
         return false;
       }
       std::vector<std::string> strs = android::base::Split(args[i], ";");
-      symbol_filter.insert(strs.begin(), strs.end());
+      sample_tree_builder_options_.symbol_filter.insert(strs.begin(), strs.end());
     } else if (args[i] == "--symfs") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -498,10 +581,19 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
   SampleComparator<SampleEntry> comparator;
 
   if (accumulate_callchain_) {
-    displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead);
-    displayer.AddDisplayFunction("Self", DisplaySelfOverhead);
+    if (raw_period_) {
+      displayer.AddDisplayFunction("Children", DisplayAccumulatedPeriod);
+      displayer.AddDisplayFunction("Self", DisplaySelfPeriod);
+    } else {
+      displayer.AddDisplayFunction("Children", DisplayAccumulatedOverhead);
+      displayer.AddDisplayFunction("Self", DisplaySelfOverhead);
+    }
   } else {
-    displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead);
+    if (raw_period_) {
+      displayer.AddDisplayFunction("Overhead", DisplaySelfPeriod);
+    } else {
+      displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead);
+    }
   }
   if (print_sample_count) {
     displayer.AddDisplayFunction("Sample", DisplaySampleCount);
@@ -563,18 +655,21 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
         displayer.AddExclusiveDisplayFunction(
             ReportCmdCallgraphDisplayerWithVaddrInFile());
       } else {
-        displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer());
+        displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
+            callgraph_max_stack_, callgraph_percent_limit_, brief_callgraph_));
       }
     }
   }
 
-  sample_tree_builder_.reset(
-      new ReportCmdSampleTreeBuilder(comparator, &thread_tree_));
-  sample_tree_builder_->SetFilters(pid_filter, tid_filter, comm_filter,
-                                   dso_filter, symbol_filter);
+  sample_tree_builder_options_.comparator = comparator;
+  sample_tree_builder_options_.thread_tree = &thread_tree_;
 
   SampleComparator<SampleEntry> sort_comparator;
   sort_comparator.AddCompareFunction(CompareTotalPeriod);
+  if (print_callgraph_) {
+    sort_comparator.AddCompareFunction(CompareCallGraphDuplicated);
+  }
+  sort_comparator.AddCompareFunction(ComparePeriod);
   sort_comparator.AddComparator(comparator);
   sample_tree_sorter_.reset(new ReportCmdSampleTreeSorter(sort_comparator));
   sample_tree_displayer_.reset(new ReportCmdSampleTreeDisplayer(displayer));
@@ -582,7 +677,7 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
 }
 
 bool ReportCommand::ReadEventAttrFromRecordFile() {
-  std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
+  std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
   for (const auto& attr_with_id : attrs) {
     EventAttrWithName attr;
     attr.attr = *attr_with_id.attr;
@@ -607,13 +702,7 @@ bool ReportCommand::ReadEventAttrFromRecordFile() {
 }
 
 bool ReportCommand::ReadFeaturesFromRecordFile() {
-  std::vector<BuildIdRecord> records =
-      record_file_reader_->ReadBuildIdFeature();
-  std::vector<std::pair<std::string, BuildId>> build_ids;
-  for (auto& r : records) {
-    build_ids.push_back(std::make_pair(r.filename, r.build_id));
-  }
-  Dso::SetBuildIds(build_ids);
+  record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
 
   std::string arch =
       record_file_reader_->ReadFeatureString(PerfFileFormat::FEAT_ARCH);
@@ -657,28 +746,36 @@ bool ReportCommand::ReadFeaturesFromRecordFile() {
 }
 
 bool ReportCommand::ReadSampleTreeFromRecordFile() {
-  sample_tree_builder_->SetBranchSampleOption(use_branch_address_);
+  sample_tree_builder_options_.use_branch_address = use_branch_address_;
   // Normally do strict arch check when unwinding stack. But allow unwinding
   // 32-bit processes on 64-bit devices for system wide profiling.
-  bool strict_unwind_arch_check = !system_wide_collection_;
-  sample_tree_builder_->SetCallChainSampleOptions(
-      accumulate_callchain_, print_callgraph_, !callgraph_show_callee_,
-      strict_unwind_arch_check);
+  sample_tree_builder_options_.strict_unwind_arch_check = !system_wide_collection_;
+  sample_tree_builder_options_.accumulate_callchain = accumulate_callchain_;
+  sample_tree_builder_options_.build_callchain = print_callgraph_;
+  sample_tree_builder_options_.use_caller_as_callchain_root = !callgraph_show_callee_;
+
+  for (size_t i = 0; i < event_attrs_.size(); ++i) {
+    sample_tree_builder_.push_back(sample_tree_builder_options_.CreateSampleTreeBuilder());
+  }
+
   if (!record_file_reader_->ReadDataSection(
           [this](std::unique_ptr<Record> record) {
             return ProcessRecord(std::move(record));
           })) {
     return false;
   }
-  sample_tree_ = sample_tree_builder_->GetSampleTree();
-  sample_tree_sorter_->Sort(sample_tree_.samples, print_callgraph_);
+  for (size_t i = 0; i < sample_tree_builder_.size(); ++i) {
+    sample_tree_.push_back(sample_tree_builder_[i]->GetSampleTree());
+    sample_tree_sorter_->Sort(sample_tree_.back().samples, print_callgraph_);
+  }
   return true;
 }
 
 bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
   thread_tree_.Update(*record);
   if (record->type() == PERF_RECORD_SAMPLE) {
-    sample_tree_builder_->ProcessSampleRecord(
+    size_t attr_id = record_file_reader_->GetAttrIndexOfRecord(record.get());
+    sample_tree_builder_[attr_id]->ProcessSampleRecord(
         *static_cast<const SampleRecord*>(record.get()));
   } else if (record->type() == PERF_RECORD_TRACING_DATA) {
     const auto& r = *static_cast<TracingDataRecord*>(record.get());
@@ -712,8 +809,23 @@ bool ReportCommand::PrintReport() {
     file_handler.reset(report_fp);
   }
   PrintReportContext(report_fp);
-  sample_tree_displayer_->DisplaySamples(report_fp, sample_tree_.samples,
-                                         &sample_tree_);
+  for (size_t i = 0; i < event_attrs_.size(); ++i) {
+    if (i != 0) {
+      fprintf(report_fp, "\n");
+    }
+    EventAttrWithName& attr = event_attrs_[i];
+    SampleTree& sample_tree = sample_tree_[i];
+    fprintf(report_fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(),
+            attr.attr.type, attr.attr.config);
+    fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree.total_samples);
+    if (sample_tree.total_error_callchains != 0) {
+      fprintf(report_fp, "Error Callchains: %" PRIu64 ", %f%%\n",
+              sample_tree.total_error_callchains,
+              sample_tree.total_error_callchains * 100.0 / sample_tree.total_samples);
+    }
+    fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree.total_period);
+    sample_tree_displayer_->DisplaySamples(report_fp, sample_tree.samples, &sample_tree);
+  }
   fflush(report_fp);
   if (ferror(report_fp) != 0) {
     PLOG(ERROR) << "print report failed";
@@ -727,12 +839,6 @@ void ReportCommand::PrintReportContext(FILE* report_fp) {
     fprintf(report_fp, "Cmdline: %s\n", record_cmdline_.c_str());
   }
   fprintf(report_fp, "Arch: %s\n", GetArchString(record_file_arch_).c_str());
-  for (const auto& attr : event_attrs_) {
-    fprintf(report_fp, "Event: %s (type %u, config %llu)\n", attr.name.c_str(),
-            attr.attr.type, attr.attr.config);
-  }
-  fprintf(report_fp, "Samples: %" PRIu64 "\n", sample_tree_.total_samples);
-  fprintf(report_fp, "Event count: %" PRIu64 "\n\n", sample_tree_.total_period);
 }
 
 }  // namespace