OSDN Git Service

Merge "libfec: correct verity table only if it's invalid"
[android-x86/system-extras.git] / simpleperf / cmd_report.cpp
index 90e0778..7fd0f61 100644 (file)
 #include <vector>
 
 #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"
@@ -48,16 +48,16 @@ static std::set<std::string> branch_sort_keys = {
     "dso_from", "dso_to", "symbol_from", "symbol_to",
 };
 struct BranchFromEntry {
-  uint64_t ip;
   const MapEntry* map;
   const Symbol* symbol;
+  uint64_t vaddr_in_file;
   uint64_t flags;
 
-  BranchFromEntry() : ip(0), map(nullptr), symbol(nullptr), flags(0) {}
+  BranchFromEntry()
+      : map(nullptr), symbol(nullptr), vaddr_in_file(0), flags(0) {}
 };
 
 struct SampleEntry {
-  uint64_t ip;
   uint64_t time;
   uint64_t period;
   // accumuated when appearing in other sample's callchain
@@ -67,23 +67,23 @@ struct SampleEntry {
   const char* thread_comm;
   const MapEntry* map;
   const Symbol* symbol;
+  uint64_t vaddr_in_file;
   BranchFromEntry branch_from;
   // a callchain tree representing all callchains in the sample
   CallChainRoot<SampleEntry> callchain;
 
-  SampleEntry(uint64_t ip, uint64_t time, uint64_t period,
-              uint64_t accumulated_period, uint64_t sample_count,
-              const ThreadEntry* thread, const MapEntry* map,
-              const Symbol* symbol)
-      : ip(ip),
-        time(time),
+  SampleEntry(uint64_t time, uint64_t period, uint64_t accumulated_period,
+              uint64_t sample_count, const ThreadEntry* thread,
+              const MapEntry* map, const Symbol* symbol, uint64_t vaddr_in_file)
+      : time(time),
         period(period),
         accumulated_period(accumulated_period),
         sample_count(sample_count),
         thread(thread),
         thread_comm(thread->comm),
         map(map),
-        symbol(symbol) {}
+        symbol(symbol),
+        vaddr_in_file(vaddr_in_file) {}
 
   // The data member 'callchain' can only move, not copy.
   SampleEntry(SampleEntry&&) = default;
@@ -96,6 +96,9 @@ struct SampleTree {
   uint64_t total_period;
 };
 
+BUILD_COMPARE_VALUE_FUNCTION(CompareVaddrInFile, vaddr_in_file);
+BUILD_DISPLAY_HEX64_FUNCTION(DisplayVaddrInFile, vaddr_in_file);
+
 class ReportCmdSampleTreeBuilder
     : public SampleTreeBuilder<SampleEntry, uint64_t> {
  public:
@@ -109,11 +112,13 @@ class ReportCmdSampleTreeBuilder
   void SetFilters(const std::unordered_set<int>& pid_filter,
                   const std::unordered_set<int>& tid_filter,
                   const std::unordered_set<std::string>& comm_filter,
-                  const std::unordered_set<std::string>& dso_filter) {
+                  const std::unordered_set<std::string>& dso_filter,
+                  const std::unordered_set<std::string>& symbol_filter) {
     pid_filter_ = pid_filter;
     tid_filter_ = tid_filter;
     comm_filter_ = comm_filter;
     dso_filter_ = dso_filter;
+    symbol_filter_ = symbol_filter;
   }
 
   SampleTree GetSampleTree() const {
@@ -131,11 +136,13 @@ class ReportCmdSampleTreeBuilder
         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);
+    uint64_t vaddr_in_file;
+    const Symbol* symbol =
+        thread_tree_->FindSymbol(map, r.ip_data.ip, &vaddr_in_file);
     *acc_info = r.period_data.period;
     return InsertSample(std::unique_ptr<SampleEntry>(
-        new SampleEntry(r.ip_data.ip, r.time_data.time, r.period_data.period, 0,
-                        1, thread, map, symbol)));
+        new SampleEntry(r.time_data.time, r.period_data.period, 0, 1, thread,
+                        map, symbol, vaddr_in_file)));
   }
 
   SampleEntry* CreateBranchSample(const SampleRecord& r,
@@ -143,15 +150,19 @@ class ReportCmdSampleTreeBuilder
     const ThreadEntry* thread =
         thread_tree_->FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
     const MapEntry* from_map = thread_tree_->FindMap(thread, item.from);
-    const Symbol* from_symbol = thread_tree_->FindSymbol(from_map, item.from);
+    uint64_t from_vaddr_in_file;
+    const Symbol* from_symbol =
+        thread_tree_->FindSymbol(from_map, item.from, &from_vaddr_in_file);
     const MapEntry* to_map = thread_tree_->FindMap(thread, item.to);
-    const Symbol* to_symbol = thread_tree_->FindSymbol(to_map, item.to);
+    uint64_t to_vaddr_in_file;
+    const Symbol* to_symbol =
+        thread_tree_->FindSymbol(to_map, item.to, &to_vaddr_in_file);
     std::unique_ptr<SampleEntry> sample(
-        new SampleEntry(item.to, r.time_data.time, r.period_data.period, 0, 1,
-                        thread, to_map, to_symbol));
-    sample->branch_from.ip = item.from;
+        new SampleEntry(r.time_data.time, r.period_data.period, 0, 1, thread,
+                        to_map, to_symbol, to_vaddr_in_file));
     sample->branch_from.map = from_map;
     sample->branch_from.symbol = from_symbol;
+    sample->branch_from.vaddr_in_file = from_vaddr_in_file;
     sample->branch_from.flags = item.flags;
     return InsertSample(std::move(sample));
   }
@@ -162,9 +173,10 @@ class ReportCmdSampleTreeBuilder
                                      const uint64_t& acc_info) override {
     const ThreadEntry* thread = sample->thread;
     const MapEntry* map = thread_tree_->FindMap(thread, ip, in_kernel);
-    const Symbol* symbol = thread_tree_->FindSymbol(map, ip);
-    std::unique_ptr<SampleEntry> callchain_sample(
-        new SampleEntry(ip, sample->time, 0, acc_info, 0, thread, map, symbol));
+    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));
     return InsertCallChainSample(std::move(callchain_sample), callchain);
   }
 
@@ -172,10 +184,8 @@ class ReportCmdSampleTreeBuilder
     return sample->thread;
   }
 
-  void InsertCallChainForSample(SampleEntry* sample,
-                                const std::vector<SampleEntry*>& callchain,
-                                const uint64_t& acc_info) override {
-    sample->callchain.AddCallChain(callchain, acc_info);
+  uint64_t GetPeriodForCallChain(const uint64_t& acc_info) override {
+    return acc_info;
   }
 
   bool FilterSample(const SampleEntry* sample) override {
@@ -195,6 +205,11 @@ class ReportCmdSampleTreeBuilder
         dso_filter_.find(sample->map->dso->Path()) == dso_filter_.end()) {
       return false;
     }
+    if (!symbol_filter_.empty() &&
+        symbol_filter_.find(sample->symbol->DemangledName()) ==
+            symbol_filter_.end()) {
+      return false;
+    }
     return true;
   }
 
@@ -216,6 +231,7 @@ class ReportCmdSampleTreeBuilder
   std::unordered_set<int> tid_filter_;
   std::unordered_set<std::string> comm_filter_;
   std::unordered_set<std::string> dso_filter_;
+  std::unordered_set<std::string> symbol_filter_;
 
   uint64_t total_samples_;
   uint64_t total_period_;
@@ -225,10 +241,22 @@ using ReportCmdSampleTreeSorter = SampleTreeSorter<SampleEntry>;
 using ReportCmdSampleTreeDisplayer =
     SampleTreeDisplayer<SampleEntry, SampleTree>;
 
+using ReportCmdCallgraphDisplayer =
+    CallgraphDisplayer<SampleEntry, CallChainNode<SampleEntry>>;
+
+class ReportCmdCallgraphDisplayerWithVaddrInFile
+    : public ReportCmdCallgraphDisplayer {
+ protected:
+  std::string PrintSampleName(const SampleEntry* sample) override {
+    return android::base::StringPrintf("%s [+0x%" PRIx64 "]",
+                                       sample->symbol->DemangledName(),
+                                       sample->vaddr_in_file);
+  }
+};
+
 struct EventAttrWithName {
   perf_event_attr attr;
   std::string name;
-  std::vector<uint64_t> event_ids;
 };
 
 class ReportCommand : public Command {
@@ -247,18 +275,35 @@ class ReportCommand : public Command {
 "-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 callee mode.\n"
+"                      Default is caller mode.\n"
 "-i <file>  Specify path of record file, default is perf.data.\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"
-"--sort key1,key2,...  Select the keys to sort and print the report.\n"
-"                      Possible keys include pid, tid, comm, dso, symbol,\n"
-"                      dso_from, dso_to, symbol_from, symbol_to.\n"
-"                      dso_from, dso_to, symbol_from, symbol_to can only be\n"
-"                      used with -b option.\n"
-"                      Default keys are \"comm,pid,tid,dso,symbol\"\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"
+"                      Possible keys include:\n"
+"                        pid             -- process id\n"
+"                        tid             -- thread id\n"
+"                        comm            -- thread name (can be changed during\n"
+"                                           the lifetime of a thread)\n"
+"                        dso             -- shared library\n"
+"                        symbol          -- function name in the shared library\n"
+"                        vaddr_in_file   -- virtual address in the shared\n"
+"                                           library\n"
+"                      Keys can only be used with -b option:\n"
+"                        dso_from        -- shared library branched from\n"
+"                        dso_to          -- shared library branched to\n"
+"                        symbol_from     -- name of function branched from\n"
+"                        symbol_to       -- name of function branched to\n"
+"                      The default sort keys are:\n"
+"                        comm,pid,tid,dso,symbol\n"
+"--symbols symbol1;symbol2;...    Report only for selected symbols.\n"
 "--symfs <dir>         Look for files with symbols relative to this directory.\n"
 "--tids tid1,tid2,...  Report only for selected tids.\n"
 "--vmlinux <file>      Parse kernel symbols from <file>.\n"
@@ -270,7 +315,9 @@ class ReportCommand : public Command {
         system_wide_collection_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
-        callgraph_show_callee_(true) {}
+        callgraph_show_callee_(false),
+        callgraph_max_stack_(UINT32_MAX),
+        callgraph_percent_limit_(0) {}
 
   bool Run(const std::vector<std::string>& args);
 
@@ -299,6 +346,8 @@ class ReportCommand : public Command {
   bool accumulate_callchain_;
   bool print_callgraph_;
   bool callgraph_show_callee_;
+  uint32_t callgraph_max_stack_;
+  double callgraph_percent_limit_;
 
   std::string report_filename_;
 };
@@ -336,12 +385,14 @@ bool ReportCommand::Run(const std::vector<std::string>& args) {
 
 bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
   bool demangle = true;
+  bool show_ip_for_unknown_symbol = true;
   std::string symfs_dir;
   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;
 
@@ -379,40 +430,62 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
       }
       record_filename_ = args[i];
 
+    } 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;
 
     } else if (args[i] == "--no-demangle") {
       demangle = false;
+    } else if (args[i] == "--no-show-ip") {
+      show_ip_for_unknown_symbol = false;
     } else if (args[i] == "-o") {
       if (!NextArgumentOrError(args, &i)) {
         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);
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
       std::vector<std::string> strs = android::base::Split(args[i], ",");
-      std::vector<int> ids;
       for (const auto& s : strs) {
         int id;
         if (!android::base::ParseInt(s.c_str(), &id, 0)) {
-          LOG(ERROR) << "invalid id in " << args[i] << " option: " << s;
+          LOG(ERROR) << "invalid id in " << option << " option: " << s;
           return false;
         }
-        ids.push_back(id);
+        filter.insert(id);
       }
-      std::unordered_set<int>& filter =
-          (args[i] == "--pids" ? pid_filter : tid_filter);
-      filter.insert(ids.begin(), ids.end());
 
     } else if (args[i] == "--sort") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
       sort_keys = android::base::Split(args[i], ",");
+    } else if (args[i] == "--symbols") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      std::vector<std::string> strs = android::base::Split(args[i], ";");
+      symbol_filter.insert(strs.begin(), strs.end());
     } else if (args[i] == "--symfs") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -438,6 +511,10 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
     Dso::SetVmlinux(vmlinux);
   }
 
+  if (show_ip_for_unknown_symbol) {
+    thread_tree_.ShowIpForUnknownSymbol();
+  }
+
   SampleDisplayer<SampleEntry, SampleTree> displayer;
   SampleComparator<SampleEntry> comparator;
 
@@ -447,9 +524,6 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
   } else {
     displayer.AddDisplayFunction("Overhead", DisplaySelfOverhead);
   }
-  if (print_callgraph_) {
-    displayer.AddExclusiveDisplayFunction(DisplayCallgraph);
-  }
   if (print_sample_count) {
     displayer.AddDisplayFunction("Sample", DisplaySampleCount);
   }
@@ -475,6 +549,9 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
     } else if (key == "symbol") {
       comparator.AddCompareFunction(CompareSymbol);
       displayer.AddDisplayFunction("Symbol", DisplaySymbol);
+    } else if (key == "vaddr_in_file") {
+      comparator.AddCompareFunction(CompareVaddrInFile);
+      displayer.AddDisplayFunction("VaddrInFile", DisplayVaddrInFile);
     } else if (key == "dso_from") {
       comparator.AddCompareFunction(CompareDsoFrom);
       displayer.AddDisplayFunction("Source Shared Object", DisplayDsoFrom);
@@ -492,11 +569,31 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
       return false;
     }
   }
+  if (print_callgraph_) {
+    bool has_symbol_key = false;
+    bool has_vaddr_in_file_key = false;
+    for (const auto& key : sort_keys) {
+      if (key == "symbol") {
+        has_symbol_key = true;
+      } else if (key == "vaddr_in_file") {
+        has_vaddr_in_file_key = true;
+      }
+    }
+    if (has_symbol_key) {
+      if (has_vaddr_in_file_key) {
+        displayer.AddExclusiveDisplayFunction(
+            ReportCmdCallgraphDisplayerWithVaddrInFile());
+      } else {
+        displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
+            callgraph_max_stack_, callgraph_percent_limit_));
+      }
+    }
+  }
 
   sample_tree_builder_.reset(
       new ReportCmdSampleTreeBuilder(comparator, &thread_tree_));
   sample_tree_builder_->SetFilters(pid_filter, tid_filter, comm_filter,
-                                   dso_filter);
+                                   dso_filter, symbol_filter);
 
   SampleComparator<SampleEntry> sort_comparator;
   sort_comparator.AddCompareFunction(CompareTotalPeriod);
@@ -507,16 +604,11 @@ 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;
-    attr.event_ids = attr_with_id.ids;
-    const EventType* event_type =
-        FindEventTypeByConfig(attr.attr.type, attr.attr.config);
-    if (event_type != nullptr) {
-      attr.name = event_type->name;
-    }
+    attr.name = GetEventNameByAttr(attr.attr);
     event_attrs_.push_back(attr);
   }
   if (use_branch_address_) {
@@ -587,7 +679,6 @@ bool ReportCommand::ReadFeaturesFromRecordFile() {
 }
 
 bool ReportCommand::ReadSampleTreeFromRecordFile() {
-  thread_tree_.AddThread(0, 0, "swapper");
   sample_tree_builder_->SetBranchSampleOption(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.
@@ -613,7 +704,7 @@ bool ReportCommand::ProcessRecord(std::unique_ptr<Record> record) {
         *static_cast<const SampleRecord*>(record.get()));
   } else if (record->type() == PERF_RECORD_TRACING_DATA) {
     const auto& r = *static_cast<TracingDataRecord*>(record.get());
-    if (!ProcessTracingData(r.data)) {
+    if (!ProcessTracingData(std::vector<char>(r.data, r.data + r.data_size))) {
       return false;
     }
   }