OSDN Git Service

simpleperf: add --brief-callgraph option for report cmd.
authorYabin Cui <yabinc@google.com>
Wed, 19 Apr 2017 18:48:44 +0000 (11:48 -0700)
committerYabin Cui <yabinc@google.com>
Wed, 19 Apr 2017 18:57:11 +0000 (11:57 -0700)
Remove duplicated callgraphs (which appears as a sub graph in
another place) in --brief-callgraph mode.
Accept brief callgraph in report.py.
Add unit test for --brief-callgraph option.

Bug: http://b/37444055
Test: run simpleperf_unit_test.
Test: manually run report.py.
Change-Id: I41977762552ec66807f394558352da6cbefaec2e

simpleperf/SampleComparator.h
simpleperf/SampleDisplayer.h
simpleperf/callchain.h
simpleperf/cmd_report.cpp
simpleperf/cmd_report_test.cpp
simpleperf/sample_tree.h
simpleperf/scripts/report.py

index 9eefeb4..7778181 100644 (file)
@@ -60,6 +60,7 @@ BUILD_COMPARE_STRING_FUNCTION(CompareDsoFrom,
                               branch_from.map->dso->Path().c_str());
 BUILD_COMPARE_STRING_FUNCTION(CompareSymbolFrom,
                               branch_from.symbol->DemangledName());
+BUILD_COMPARE_VALUE_FUNCTION(CompareCallGraphDuplicated, callchain.duplicated);
 
 template <typename EntryT>
 int CompareTotalPeriod(const EntryT* sample1, const EntryT* sample2) {
@@ -68,6 +69,11 @@ int CompareTotalPeriod(const EntryT* sample1, const EntryT* sample2) {
   return Compare(period2, period1);
 }
 
+template <typename EntryT>
+int ComparePeriod(const EntryT* sample1, const EntryT* sample2) {
+  return Compare(sample2->period, sample1->period);
+}
+
 // SampleComparator is a class using a collection of compare functions to
 // compare two samples.
 
index 4317582..2dde02e 100644 (file)
@@ -106,13 +106,21 @@ class CallgraphDisplayer {
 
  public:
   CallgraphDisplayer(uint32_t max_stack = UINT32_MAX,
-                     double percent_limit = 0.0)
-      : max_stack_(max_stack), percent_limit_(percent_limit) {}
+                     double percent_limit = 0.0,
+                     bool brief_callgraph = false)
+      : max_stack_(max_stack), percent_limit_(percent_limit), brief_callgraph_(brief_callgraph) {}
 
   virtual ~CallgraphDisplayer() {}
 
   void operator()(FILE* fp, const SampleT* sample) {
+    if (sample->callchain.children.empty()) {
+      return;
+    }
     std::string prefix = "       ";
+    if (brief_callgraph_ && sample->callchain.duplicated) {
+      fprintf(fp, "%s[skipped in brief callgraph mode]\n", prefix.c_str());
+      return;
+    }
     fprintf(fp, "%s|\n", prefix.c_str());
     fprintf(fp, "%s-- %s\n", prefix.c_str(), PrintSampleName(sample).c_str());
     prefix.append(3, ' ');
@@ -169,6 +177,7 @@ class CallgraphDisplayer {
  private:
   uint32_t max_stack_;
   double percent_limit_;
+  bool brief_callgraph_;
 };
 
 // SampleDisplayer is a class using a collections of display functions to show a
index 2267fec..b2a0457 100644 (file)
@@ -38,10 +38,13 @@ struct CallChainNode {
 template <typename EntryT>
 struct CallChainRoot {
   typedef CallChainNode<EntryT> NodeT;
+  // If duplicated = true, this call tree is part of another call tree.
+  // And we don't need to show it in brief callgraph report mode.
+  bool duplicated;
   uint64_t children_period;
   std::vector<std::unique_ptr<NodeT>> children;
 
-  CallChainRoot() : children_period(0) {}
+  CallChainRoot() : duplicated(false), children_period(0) {}
 
   void AddCallChain(
       const std::vector<EntryT*>& callchain, uint64_t period,
index f160dea..8ebab61 100644 (file)
@@ -126,7 +126,8 @@ 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_;
@@ -300,6 +301,7 @@ class ReportCommand : public Command {
 "-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"
+"--brief-callgraph     Print brief call graph.\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"
@@ -351,7 +353,8 @@ class ReportCommand : public Command {
         callgraph_show_callee_(false),
         callgraph_max_stack_(UINT32_MAX),
         callgraph_percent_limit_(0),
-        raw_period_(false) {}
+        raw_period_(false),
+        brief_callgraph_(false) {}
 
   bool Run(const std::vector<std::string>& args);
 
@@ -386,6 +389,7 @@ class ReportCommand : public Command {
   uint32_t callgraph_max_stack_;
   double callgraph_percent_limit_;
   bool raw_period_;
+  bool brief_callgraph_;
 
   std::string report_filename_;
 };
@@ -432,6 +436,8 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
   for (size_t i = 0; i < args.size(); ++i) {
     if (args[i] == "-b") {
       use_branch_address_ = true;
+    } else if (args[i] == "--brief-callgraph") {
+      brief_callgraph_ = true;
     } else if (args[i] == "--children") {
       accumulate_callchain_ = true;
     } else if (args[i] == "--comms" || args[i] == "--dsos") {
@@ -640,7 +646,7 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
             ReportCmdCallgraphDisplayerWithVaddrInFile());
       } else {
         displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
-            callgraph_max_stack_, callgraph_percent_limit_));
+            callgraph_max_stack_, callgraph_percent_limit_, brief_callgraph_));
       }
     }
   }
@@ -650,6 +656,10 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
 
   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));
index e29c8d8..15530e5 100644 (file)
@@ -464,6 +464,12 @@ TEST_F(ReportCommandTest, raw_period_option) {
   ASSERT_EQ(content.find("%"), std::string::npos);
 }
 
+TEST_F(ReportCommandTest, brief_callgraph_option) {
+  Report(CALLGRAPH_FP_PERF_DATA, {"-g", "--brief-callgraph"});
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("skipped in brief callgraph mode"), std::string::npos);
+}
+
 #if defined(__linux__)
 #include "event_selection_set.h"
 
index ba6bbbe..c7a0ac8 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef SIMPLE_PERF_SAMPLE_TREE_H_
 #define SIMPLE_PERF_SAMPLE_TREE_H_
 
+#include <unordered_map>
+
 #include "callchain.h"
 #include "dwarf_unwind.h"
 #include "perf_regs.h"
@@ -157,6 +159,7 @@ class SampleTreeBuilder {
         if (use_caller_as_callchain_root_) {
           std::reverse(callchain.begin(), callchain.end());
         }
+        EntryT* parent = nullptr;
         while (callchain.size() >= 2) {
           EntryT* sample = callchain[0];
           callchain.erase(callchain.begin());
@@ -166,6 +169,8 @@ class SampleTreeBuilder {
           }
           added_set.insert(sample);
           InsertCallChainForSample(sample, callchain, acc_info);
+          UpdateCallChainParentInfo(sample, parent);
+          parent = sample;
         }
       }
     }
@@ -253,16 +258,48 @@ class SampleTreeBuilder {
         });
   }
 
+  void AddCallChainDuplicateInfo() {
+    if (build_callchain_) {
+      for (EntryT* sample : sample_set_) {
+        auto it = callchain_parent_map_.find(sample);
+        if (it != callchain_parent_map_.end() && !it->second.has_multiple_parents) {
+          sample->callchain.duplicated = true;
+        }
+      }
+    }
+  }
+
   std::set<EntryT*, SampleComparator<EntryT>> sample_set_;
   bool accumulate_callchain_;
 
  private:
+  void UpdateCallChainParentInfo(EntryT* sample, EntryT* parent) {
+    if (parent == nullptr) {
+      return;
+    }
+    auto it = callchain_parent_map_.find(sample);
+    if (it == callchain_parent_map_.end()) {
+      CallChainParentInfo info;
+      info.parent = parent;
+      info.has_multiple_parents = false;
+      callchain_parent_map_[sample] = info;
+    } else if (it->second.parent != parent) {
+      it->second.has_multiple_parents = true;
+    }
+  }
+
   const SampleComparator<EntryT> sample_comparator_;
   // If a CallChainSample is filtered out, it is stored in callchain_sample_set_
   // and only used in other EntryT's callchain.
   std::set<EntryT*, SampleComparator<EntryT>> callchain_sample_set_;
   std::vector<std::unique_ptr<EntryT>> sample_storage_;
 
+  struct CallChainParentInfo {
+    EntryT* parent;
+    bool has_multiple_parents;
+  };
+  std::unordered_map<EntryT*, CallChainParentInfo> callchain_parent_map_;
+
   bool use_branch_address_;
   bool build_callchain_;
   bool use_caller_as_callchain_root_;
index 6bba677..5ed633b 100644 (file)
@@ -111,6 +111,8 @@ def parse_event_reports(lines):
   vertical_columns = []
   last_node = None
 
+  has_skipped_callgraph = False
+
   for line in lines[line_id:]:
     if not line:
       in_report_context = not in_report_context
@@ -139,6 +141,10 @@ def parse_event_reports(lines):
 
       if not line.strip('| \t'):
         continue
+      if line.find('skipped in brief callgraph mode') != -1:
+        has_skipped_callgraph = True
+        continue
+
       if line.find('-') == -1:
         line = line.strip('| \t')
         function_name = line
@@ -168,6 +174,9 @@ def parse_event_reports(lines):
         call_tree_stack[depth] = node
         last_node = node
 
+  if has_skipped_callgraph:
+      log_warning('some callgraphs are skipped in brief callgraph mode')
+
   return event_reports
 
 
@@ -230,20 +239,19 @@ class ReportWindow(object):
 
   def display_call_tree(self, tree, parent_id, node, indent):
     id = parent_id
-    indent_str = '  ' * indent
+    indent_str = '    ' * indent
 
     if node.percentage != 100.0:
-      percentage_str = '%.2f%%' % node.percentage
+      percentage_str = '%.2f%% ' % node.percentage
     else:
       percentage_str = ''
-    first_open = True if node.percentage == 100.0 else False
 
     for i in range(len(node.call_stack)):
       s = indent_str
-      s += '+ ' if node.children else '  '
+      s += '+ ' if node.children and i == len(node.call_stack) - 1 else '  '
       s += percentage_str if i == 0 else ' ' * len(percentage_str)
       s += node.call_stack[i]
-      child_open = first_open if i == 0 else True
+      child_open = False if i == len(node.call_stack) - 1 and indent > 1 else True
       id = tree.insert(id, 'end', None, values=[s], open=child_open,
                        tag='set_font')