diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2017-04-19 21:57:21 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-04-19 21:57:22 +0000 |
commit | e3d4de1a615f5ceeebcb2a8c1e563c30cdeefc45 (patch) | |
tree | b184869d2dfa85632e4bf4b3da15e2a3f4d8b891 | |
parent | faa89a17fd652dcfc02a614667ee8915da2379f7 (diff) | |
parent | 0c093f3cc0dbced581c9d430e0ed8cd12def61ce (diff) | |
download | extras-e3d4de1a615f5ceeebcb2a8c1e563c30cdeefc45.tar.gz |
Merge "simpleperf: add --brief-callgraph option for report cmd."
-rw-r--r-- | simpleperf/SampleComparator.h | 6 | ||||
-rw-r--r-- | simpleperf/SampleDisplayer.h | 13 | ||||
-rw-r--r-- | simpleperf/callchain.h | 5 | ||||
-rw-r--r-- | simpleperf/cmd_report.cpp | 16 | ||||
-rw-r--r-- | simpleperf/cmd_report_test.cpp | 6 | ||||
-rw-r--r-- | simpleperf/sample_tree.h | 37 | ||||
-rw-r--r-- | simpleperf/scripts/report.py | 18 |
7 files changed, 90 insertions, 11 deletions
diff --git a/simpleperf/SampleComparator.h b/simpleperf/SampleComparator.h index 9eefeb4f..77781811 100644 --- a/simpleperf/SampleComparator.h +++ b/simpleperf/SampleComparator.h @@ -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. diff --git a/simpleperf/SampleDisplayer.h b/simpleperf/SampleDisplayer.h index 4317582a..2dde02eb 100644 --- a/simpleperf/SampleDisplayer.h +++ b/simpleperf/SampleDisplayer.h @@ -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 diff --git a/simpleperf/callchain.h b/simpleperf/callchain.h index 2267feca..b2a0457e 100644 --- a/simpleperf/callchain.h +++ b/simpleperf/callchain.h @@ -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, diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp index f160dea2..8ebab61d 100644 --- a/simpleperf/cmd_report.cpp +++ b/simpleperf/cmd_report.cpp @@ -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)); diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp index e29c8d87..15530e5c 100644 --- a/simpleperf/cmd_report_test.cpp +++ b/simpleperf/cmd_report_test.cpp @@ -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" diff --git a/simpleperf/sample_tree.h b/simpleperf/sample_tree.h index ba6bbbe1..c7a0ac84 100644 --- a/simpleperf/sample_tree.h +++ b/simpleperf/sample_tree.h @@ -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_; diff --git a/simpleperf/scripts/report.py b/simpleperf/scripts/report.py index 6bba6771..5ed633b1 100644 --- a/simpleperf/scripts/report.py +++ b/simpleperf/scripts/report.py @@ -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') |