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) {
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.
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, ' ');
private:
uint32_t max_stack_;
double percent_limit_;
+ bool brief_callgraph_;
};
// SampleDisplayer is a class using a collections of display functions to show a
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,
symbol_filter_ = symbol_filter;
}
- SampleTree GetSampleTree() const {
+ SampleTree GetSampleTree() {
+ AddCallChainDuplicateInfo();
SampleTree sample_tree;
sample_tree.samples = GetSamples();
sample_tree.total_samples = total_samples_;
"-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"
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);
uint32_t callgraph_max_stack_;
double callgraph_percent_limit_;
bool raw_period_;
+ bool brief_callgraph_;
std::string report_filename_;
};
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") {
ReportCmdCallgraphDisplayerWithVaddrInFile());
} else {
displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
- callgraph_max_stack_, callgraph_percent_limit_));
+ callgraph_max_stack_, callgraph_percent_limit_, brief_callgraph_));
}
}
}
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));
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"
#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"
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());
}
added_set.insert(sample);
InsertCallChainForSample(sample, callchain, acc_info);
+ UpdateCallChainParentInfo(sample, parent);
+ parent = sample;
}
}
}
});
}
+ 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_;
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
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
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
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')