summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2017-04-19 21:57:21 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-04-19 21:57:22 +0000
commite3d4de1a615f5ceeebcb2a8c1e563c30cdeefc45 (patch)
treeb184869d2dfa85632e4bf4b3da15e2a3f4d8b891
parentfaa89a17fd652dcfc02a614667ee8915da2379f7 (diff)
parent0c093f3cc0dbced581c9d430e0ed8cd12def61ce (diff)
downloadextras-e3d4de1a615f5ceeebcb2a8c1e563c30cdeefc45.tar.gz
Merge "simpleperf: add --brief-callgraph option for report cmd."
-rw-r--r--simpleperf/SampleComparator.h6
-rw-r--r--simpleperf/SampleDisplayer.h13
-rw-r--r--simpleperf/callchain.h5
-rw-r--r--simpleperf/cmd_report.cpp16
-rw-r--r--simpleperf/cmd_report_test.cpp6
-rw-r--r--simpleperf/sample_tree.h37
-rw-r--r--simpleperf/scripts/report.py18
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')