summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2016-06-29 12:18:11 -0700
committerYabin Cui <yabinc@google.com>2016-06-29 13:32:57 -0700
commit9970a2344333f2c19d9126cfec4f833f50ff2f22 (patch)
tree49fb22bf07f936b96ef63a5ffae87e3c9336eb52
parentd937202a3b23e6908c2109d5a96da237ee9cbba8 (diff)
downloadextras-9970a2344333f2c19d9126cfec4f833f50ff2f22.tar.gz
Simpleperf: add vaddr_in_file sort key in report command.
Currently report command can't report more details than function name. After adding vaddr_in_file sort key, it can report the place of the instruction being sampled. vaddr_in_file is the instruction's virtual address in elf file, which matches output generated by objdump. Bug: 29826956 Test: run simpleperf_unit_test. Change-Id: Ifad4dfb7c60014a03c01ffbfd0b972858f1a4884
-rw-r--r--simpleperf/SampleComparator.h11
-rw-r--r--simpleperf/SampleDisplayer.h105
-rw-r--r--simpleperf/callchain.h30
-rw-r--r--simpleperf/cmd_kmem.cpp12
-rw-r--r--simpleperf/cmd_record.cpp2
-rw-r--r--simpleperf/cmd_report.cpp121
-rw-r--r--simpleperf/cmd_report_sample.cpp8
-rw-r--r--simpleperf/cmd_report_test.cpp6
-rw-r--r--simpleperf/sample_tree.h15
-rw-r--r--simpleperf/sample_tree_test.cpp3
-rw-r--r--simpleperf/thread_tree.cpp87
-rw-r--r--simpleperf/thread_tree.h48
12 files changed, 279 insertions, 169 deletions
diff --git a/simpleperf/SampleComparator.h b/simpleperf/SampleComparator.h
index 8f9b2e4b..9eefeb4f 100644
--- a/simpleperf/SampleComparator.h
+++ b/simpleperf/SampleComparator.h
@@ -85,7 +85,7 @@ class SampleComparator {
other.compare_v_.end());
}
- bool operator()(const EntryT* sample1, const EntryT* sample2) {
+ bool operator()(const EntryT* sample1, const EntryT* sample2) const {
for (const auto& func : compare_v_) {
int ret = func(sample1, sample2);
if (ret != 0) {
@@ -95,6 +95,15 @@ class SampleComparator {
return false;
}
+ bool IsSameSample(const EntryT* sample1, const EntryT* sample2) const {
+ for (const auto& func : compare_v_) {
+ if (func(sample1, sample2) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
bool empty() const { return compare_v_.empty(); }
private:
diff --git a/simpleperf/SampleDisplayer.h b/simpleperf/SampleDisplayer.h
index 168c9e20..606f6394 100644
--- a/simpleperf/SampleDisplayer.h
+++ b/simpleperf/SampleDisplayer.h
@@ -19,6 +19,7 @@
#include <inttypes.h>
+#include <functional>
#include <string>
#include <android-base/logging.h>
@@ -49,11 +50,11 @@ std::string DisplaySelfOverhead(const EntryT* sample, const InfoT* info) {
return android::base::StringPrintf("%" PRIu64, sample->display_part); \
}
-#define BUILD_DISPLAY_HEX64_FUNCTION(function_name, display_part) \
- template <typename EntryT> \
- std::string function_name(const EntryT* sample) { \
- return android::base::StringPrintf("0x%" PRIx64, sample->display_part);\
-}
+#define BUILD_DISPLAY_HEX64_FUNCTION(function_name, display_part) \
+ template <typename EntryT> \
+ std::string function_name(const EntryT* sample) { \
+ return android::base::StringPrintf("0x%" PRIx64, sample->display_part); \
+ }
BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count);
@@ -92,51 +93,60 @@ std::string DisplaySymbolFrom(const EntryT* sample) {
return sample->branch_from.symbol->DemangledName();
}
-template <typename CallChainNodeT>
-void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix,
- const std::unique_ptr<CallChainNodeT>& node,
- uint64_t parent_period, bool last) {
- if (depth > 20) {
- LOG(WARNING) << "truncated callgraph at depth " << depth;
- return;
- }
- prefix += "|";
- fprintf(fp, "%s\n", prefix.c_str());
- if (last) {
- prefix.back() = ' ';
- }
- std::string percentage_s = "-- ";
- if (node->period + node->children_period != parent_period) {
- double percentage =
- 100.0 * (node->period + node->children_period) / parent_period;
- percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage);
- }
- fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(),
- node->chain[0]->symbol->DemangledName());
- prefix.append(percentage_s.size(), ' ');
- for (size_t i = 1; i < node->chain.size(); ++i) {
- fprintf(fp, "%s%s\n", prefix.c_str(),
- node->chain[i]->symbol->DemangledName());
+template <typename SampleT, typename CallChainNodeT>
+class CallgraphDisplayer {
+ public:
+ virtual ~CallgraphDisplayer() {}
+
+ void operator()(FILE* fp, const SampleT* sample) {
+ std::string prefix = " ";
+ fprintf(fp, "%s|\n", prefix.c_str());
+ fprintf(fp, "%s-- %s\n", prefix.c_str(), PrintSampleName(sample).c_str());
+ prefix.append(3, ' ');
+ for (size_t i = 0; i < sample->callchain.children.size(); ++i) {
+ DisplayCallGraphEntry(fp, 1, prefix, sample->callchain.children[i],
+ sample->callchain.children_period,
+ (i + 1 == sample->callchain.children.size()));
+ }
}
- for (size_t i = 0; i < node->children.size(); ++i) {
- DisplayCallGraphEntry(fp, depth + 1, prefix, node->children[i],
- node->children_period,
- (i + 1 == node->children.size()));
+
+ void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix,
+ const std::unique_ptr<CallChainNodeT>& node,
+ uint64_t parent_period, bool last) {
+ if (depth > 20) {
+ LOG(WARNING) << "truncated callgraph at depth " << depth;
+ return;
+ }
+ prefix += "|";
+ fprintf(fp, "%s\n", prefix.c_str());
+ if (last) {
+ prefix.back() = ' ';
+ }
+ std::string percentage_s = "-- ";
+ if (node->period + node->children_period != parent_period) {
+ double percentage =
+ 100.0 * (node->period + node->children_period) / parent_period;
+ percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage);
+ }
+ fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(),
+ PrintSampleName(node->chain[0]).c_str());
+ prefix.append(percentage_s.size(), ' ');
+ for (size_t i = 1; i < node->chain.size(); ++i) {
+ fprintf(fp, "%s%s\n", prefix.c_str(),
+ PrintSampleName(node->chain[i]).c_str());
+ }
+ for (size_t i = 0; i < node->children.size(); ++i) {
+ DisplayCallGraphEntry(fp, depth + 1, prefix, node->children[i],
+ node->children_period,
+ (i + 1 == node->children.size()));
+ }
}
-}
-template <typename EntryT>
-void DisplayCallgraph(FILE* fp, const EntryT* sample) {
- std::string prefix = " ";
- fprintf(fp, "%s|\n", prefix.c_str());
- fprintf(fp, "%s-- %s\n", prefix.c_str(), sample->symbol->DemangledName());
- prefix.append(3, ' ');
- for (size_t i = 0; i < sample->callchain.children.size(); ++i) {
- DisplayCallGraphEntry(fp, 1, prefix, sample->callchain.children[i],
- sample->callchain.children_period,
- (i + 1 == sample->callchain.children.size()));
+ protected:
+ virtual std::string PrintSampleName(const SampleT* sample) {
+ return sample->symbol->DemangledName();
}
-}
+};
// SampleDisplayer is a class using a collections of display functions to show a
// sample.
@@ -147,7 +157,8 @@ class SampleDisplayer {
typedef std::string (*display_sample_func_t)(const EntryT*);
typedef std::string (*display_sample_with_info_func_t)(const EntryT*,
const InfoT*);
- typedef void (*exclusive_display_sample_func_t)(FILE* fp, const EntryT*);
+ using exclusive_display_sample_func_t =
+ std::function<void(FILE*, const EntryT*)>;
private:
struct Item {
diff --git a/simpleperf/callchain.h b/simpleperf/callchain.h
index 54820b14..2267feca 100644
--- a/simpleperf/callchain.h
+++ b/simpleperf/callchain.h
@@ -20,6 +20,7 @@
#include <string.h>
#include <algorithm>
+#include <functional>
#include <memory>
#include <queue>
#include <vector>
@@ -42,9 +43,11 @@ struct CallChainRoot {
CallChainRoot() : children_period(0) {}
- void AddCallChain(const std::vector<EntryT*>& callchain, uint64_t period) {
+ void AddCallChain(
+ const std::vector<EntryT*>& callchain, uint64_t period,
+ std::function<bool(const EntryT*, const EntryT*)> is_same_sample) {
children_period += period;
- NodeT* p = FindMatchingNode(children, callchain[0]);
+ NodeT* p = FindMatchingNode(children, callchain[0], is_same_sample);
if (p == nullptr) {
std::unique_ptr<NodeT> new_node = AllocateNode(callchain, 0, period, 0);
children.push_back(std::move(new_node));
@@ -53,7 +56,7 @@ struct CallChainRoot {
size_t callchain_pos = 0;
while (true) {
size_t match_length =
- GetMatchingLengthInNode(p, callchain, callchain_pos);
+ GetMatchingLengthInNode(p, callchain, callchain_pos, is_same_sample);
CHECK_GT(match_length, 0u);
callchain_pos += match_length;
bool find_child = true;
@@ -67,7 +70,8 @@ struct CallChainRoot {
}
p->children_period += period;
if (find_child) {
- NodeT* np = FindMatchingNode(p->children, callchain[callchain_pos]);
+ NodeT* np = FindMatchingNode(p->children, callchain[callchain_pos],
+ is_same_sample);
if (np != nullptr) {
p = np;
continue;
@@ -96,32 +100,30 @@ struct CallChainRoot {
}
private:
- NodeT* FindMatchingNode(const std::vector<std::unique_ptr<NodeT>>& nodes,
- const EntryT* sample) {
+ NodeT* FindMatchingNode(
+ const std::vector<std::unique_ptr<NodeT>>& nodes, const EntryT* sample,
+ std::function<bool(const EntryT*, const EntryT*)> is_same_sample) {
for (auto& node : nodes) {
- if (MatchSampleByName(node->chain.front(), sample)) {
+ if (is_same_sample(node->chain.front(), sample)) {
return node.get();
}
}
return nullptr;
}
- size_t GetMatchingLengthInNode(NodeT* node, const std::vector<EntryT*>& chain,
- size_t chain_start) {
+ size_t GetMatchingLengthInNode(
+ NodeT* node, const std::vector<EntryT*>& chain, size_t chain_start,
+ std::function<bool(const EntryT*, const EntryT*)> is_same_sample) {
size_t i, j;
for (i = 0, j = chain_start; i < node->chain.size() && j < chain.size();
++i, ++j) {
- if (!MatchSampleByName(node->chain[i], chain[j])) {
+ if (!is_same_sample(node->chain[i], chain[j])) {
break;
}
}
return i;
}
- bool MatchSampleByName(const EntryT* sample1, const EntryT* sample2) {
- return strcmp(sample1->symbol->Name(), sample2->symbol->Name()) == 0;
- }
-
void SplitNode(NodeT* parent, size_t parent_length) {
std::unique_ptr<NodeT> child = AllocateNode(
parent->chain, parent_length, parent->period, parent->children_period);
diff --git a/simpleperf/cmd_kmem.cpp b/simpleperf/cmd_kmem.cpp
index 3c3023f5..c0370a40 100644
--- a/simpleperf/cmd_kmem.cpp
+++ b/simpleperf/cmd_kmem.cpp
@@ -157,7 +157,7 @@ class SlabSampleTreeBuilder
// tracepoint events because of lacking
// perf_arch_fetch_caller_regs().
LOG(WARNING) << "simpleperf may not get callchains for tracepoint"
- << " events because of lacking kernel support.";
+ << " events because of lacking kernel support.";
}
}
} else {
@@ -224,12 +224,10 @@ class SlabSampleTreeBuilder
const ThreadEntry* GetThreadOfSample(SlabSample*) override { return nullptr; }
- void InsertCallChainForSample(SlabSample* sample,
- const std::vector<SlabSample*>& callchain,
- const SlabAccumulateInfo&) override {
+ uint64_t GetPeriodForCallChain(const SlabAccumulateInfo&) override {
// Decide the percentage of callchain by the sample_count, so use 1 as the
// period when calling AddCallChain().
- sample->callchain.AddCallChain(callchain, 1);
+ return 1;
}
void UpdateSummary(const SlabSample* sample) override {
@@ -260,6 +258,8 @@ class SlabSampleTreeBuilder
using SlabSampleTreeSorter = SampleTreeSorter<SlabSample>;
using SlabSampleTreeDisplayer = SampleTreeDisplayer<SlabSample, SlabSampleTree>;
+using SlabSampleCallgraphDisplayer =
+ CallgraphDisplayer<SlabSample, CallChainNode<SlabSample>>;
struct EventAttrWithName {
perf_event_attr attr;
@@ -502,7 +502,7 @@ bool KmemCommand::PrepareToBuildSampleTree() {
std::string accumulated_name = accumulate_callchain_ ? "Accumulated_" : "";
if (print_callgraph_) {
- displayer.AddExclusiveDisplayFunction(DisplayCallgraph);
+ displayer.AddExclusiveDisplayFunction(SlabSampleCallgraphDisplayer());
}
for (const auto& key : slab_sort_keys_) {
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 14283f8e..441c7332 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -809,7 +809,7 @@ bool RecordCommand::DumpSymbolForRecord(const SampleRecord& r,
return false;
}
}
- const Symbol* symbol = thread_tree_.FindSymbol(map, ip);
+ const Symbol* symbol = thread_tree_.FindSymbol(map, ip, nullptr);
if (!symbol->HasDumped()) {
symbol->SetDumped();
SymbolRecord symbol_record = SymbolRecord::Create(
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index ef2a93dd..0b0e9977 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -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:
@@ -131,11 +134,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 +148,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 +171,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 +182,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 {
@@ -225,6 +233,19 @@ 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;
@@ -253,12 +274,25 @@ class ReportCommand : public Command {
"--no-demangle Don't demangle symbol names.\n"
"-o report_file_name Set report file name, default is stdout.\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"
"--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"
@@ -447,9 +481,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 +506,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,6 +526,25 @@ 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());
+ }
+ }
+ }
sample_tree_builder_.reset(
new ReportCmdSampleTreeBuilder(comparator, &thread_tree_));
diff --git a/simpleperf/cmd_report_sample.cpp b/simpleperf/cmd_report_sample.cpp
index 3bb12905..b254e82c 100644
--- a/simpleperf/cmd_report_sample.cpp
+++ b/simpleperf/cmd_report_sample.cpp
@@ -276,7 +276,7 @@ bool ReportSampleCommand::PrintSampleRecordInProtobuf(const SampleRecord& r) {
const ThreadEntry* thread =
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);
+ const Symbol* symbol = thread_tree_.FindSymbol(map, r.ip_data.ip, nullptr);
callchain->set_symbol(symbol->DemangledName());
callchain->set_file(map->dso->Path());
@@ -305,7 +305,7 @@ bool ReportSampleCommand::PrintSampleRecordInProtobuf(const SampleRecord& r) {
}
}
const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
- const Symbol* symbol = thread_tree_.FindSymbol(map, ip);
+ const Symbol* symbol = thread_tree_.FindSymbol(map, ip, nullptr);
callchain = sample->add_callchain();
callchain->set_ip(ip);
callchain->set_symbol(symbol->DemangledName());
@@ -326,7 +326,7 @@ bool ReportSampleCommand::PrintSampleRecord(const SampleRecord& r) {
const ThreadEntry* thread =
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);
+ const Symbol* symbol = thread_tree_.FindSymbol(map, r.ip_data.ip, nullptr);
FprintIndented(report_fp_, 0, "sample:\n");
FprintIndented(report_fp_, 1, "time: %" PRIu64 "\n", r.time_data.time);
FprintIndented(report_fp_, 1, "ip: %" PRIx64 "\n", r.ip_data.ip);
@@ -359,7 +359,7 @@ bool ReportSampleCommand::PrintSampleRecord(const SampleRecord& r) {
}
}
const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
- const Symbol* symbol = thread_tree_.FindSymbol(map, ip);
+ const Symbol* symbol = thread_tree_.FindSymbol(map, ip, nullptr);
FprintIndented(report_fp_, 2, "ip: %" PRIx64 "\n", ip);
FprintIndented(report_fp_, 2, "dso: %s\n", map->dso->Path().c_str());
FprintIndented(report_fp_, 2, "symbol: %s\n", symbol->DemangledName());
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index f987a7a0..ba95399f 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -296,6 +296,12 @@ TEST_F(ReportCommandTest, report_dumped_symbols) {
ASSERT_NE(content.find("page_fault"), std::string::npos);
}
+TEST_F(ReportCommandTest, report_sort_vaddr_in_file) {
+ Report(PERF_DATA, {"--sort", "vaddr_in_file"});
+ ASSERT_TRUE(success);
+ ASSERT_NE(content.find("VaddrInFile"), std::string::npos);
+}
+
#if defined(__linux__)
static std::unique_ptr<Command> RecordCmd() {
diff --git a/simpleperf/sample_tree.h b/simpleperf/sample_tree.h
index 86becbe3..eb4e38ae 100644
--- a/simpleperf/sample_tree.h
+++ b/simpleperf/sample_tree.h
@@ -54,6 +54,7 @@ class SampleTreeBuilder {
SampleTreeBuilder(SampleComparator<EntryT> comparator)
: sample_set_(comparator),
accumulate_callchain_(false),
+ sample_comparator_(comparator),
callchain_sample_set_(comparator),
use_branch_address_(false),
build_callchain_(false),
@@ -189,9 +190,7 @@ class SampleTreeBuilder {
const std::vector<EntryT*>& callchain,
const AccumulateInfoT& acc_info) = 0;
virtual const ThreadEntry* GetThreadOfSample(EntryT*) = 0;
- virtual void InsertCallChainForSample(EntryT* sample,
- const std::vector<EntryT*>& callchain,
- const AccumulateInfoT& acc_info) = 0;
+ virtual uint64_t GetPeriodForCallChain(const AccumulateInfoT& acc_info) = 0;
virtual bool FilterSample(const EntryT*) { return true; }
virtual void UpdateSummary(const EntryT*) {}
@@ -245,10 +244,20 @@ class SampleTreeBuilder {
return InsertSample(std::move(sample));
}
+ void InsertCallChainForSample(EntryT* sample,
+ const std::vector<EntryT*>& callchain,
+ const AccumulateInfoT& acc_info) {
+ uint64_t period = GetPeriodForCallChain(acc_info);
+ sample->callchain.AddCallChain(callchain, period, [&](const EntryT* s1, const EntryT* s2) {
+ return sample_comparator_.IsSameSample(s1, s2);
+ });
+ }
+
std::set<EntryT*, SampleComparator<EntryT>> sample_set_;
bool accumulate_callchain_;
private:
+ 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_;
diff --git a/simpleperf/sample_tree_test.cpp b/simpleperf/sample_tree_test.cpp
index a88ecf98..a3c7811f 100644
--- a/simpleperf/sample_tree_test.cpp
+++ b/simpleperf/sample_tree_test.cpp
@@ -84,8 +84,7 @@ class TestSampleTreeBuilder : public SampleTreeBuilder<SampleEntry, int> {
const ThreadEntry* GetThreadOfSample(SampleEntry*) override {
return nullptr;
}
- void InsertCallChainForSample(SampleEntry*, const std::vector<SampleEntry*>&,
- const int&) override {}
+ uint64_t GetPeriodForCallChain(const int&) override { return 0; }
void MergeSample(SampleEntry* sample1, SampleEntry* sample2) override {
sample1->sample_count += sample2->sample_count;
}
diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp
index 8e4e9e9d..d81e8f48 100644
--- a/simpleperf/thread_tree.cpp
+++ b/simpleperf/thread_tree.cpp
@@ -26,12 +26,13 @@
namespace simpleperf {
-bool MapComparator::operator()(const MapEntry* map1, const MapEntry* map2) const {
+bool MapComparator::operator()(const MapEntry* map1,
+ const MapEntry* map2) const {
if (map1->start_addr != map2->start_addr) {
return map1->start_addr < map2->start_addr;
}
- // Compare map->len instead of map->get_end_addr() here. Because we set map's len
- // to std::numeric_limits<uint64_t>::max() in FindMapByAddr(), which makes
+ // Compare map->len instead of map->get_end_addr() here. Because we set map's
+ // len to std::numeric_limits<uint64_t>::max() in FindMapByAddr(), which makes
// map->get_end_addr() overflow.
if (map1->len != map2->len) {
return map1->len < map2->len;
@@ -50,11 +51,13 @@ void ThreadTree::AddThread(int pid, int tid, const std::string& comm) {
"unknown", // comm
std::set<MapEntry*, MapComparator>(), // maps
};
- auto pair = thread_tree_.insert(std::make_pair(tid, std::unique_ptr<ThreadEntry>(thread)));
+ auto pair = thread_tree_.insert(
+ std::make_pair(tid, std::unique_ptr<ThreadEntry>(thread)));
CHECK(pair.second);
it = pair.first;
}
- thread_comm_storage_.push_back(std::unique_ptr<std::string>(new std::string(comm)));
+ thread_comm_storage_.push_back(
+ std::unique_ptr<std::string>(new std::string(comm)));
it->second->comm = thread_comm_storage_.back()->c_str();
}
@@ -73,21 +76,23 @@ ThreadEntry* ThreadTree::FindThreadOrNew(int pid, int tid) {
} else {
if (pid != it->second.get()->pid) {
// TODO: b/22185053.
- LOG(DEBUG) << "unexpected (pid, tid) pair: expected (" << it->second.get()->pid << ", " << tid
- << "), actual (" << pid << ", " << tid << ")";
+ LOG(DEBUG) << "unexpected (pid, tid) pair: expected ("
+ << it->second.get()->pid << ", " << tid << "), actual (" << pid
+ << ", " << tid << ")";
}
}
return it->second.get();
}
-void ThreadTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time,
- const std::string& filename) {
+void ThreadTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff,
+ uint64_t time, const std::string& filename) {
// kernel map len can be 0 when record command is not run in supervisor mode.
if (len == 0) {
return;
}
Dso* dso = FindKernelDsoOrNew(filename);
- MapEntry* map = AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, true));
+ MapEntry* map =
+ AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, true));
FixOverlappedMap(&kernel_map_tree_, map);
auto pair = kernel_map_tree_.insert(map);
CHECK(pair.second);
@@ -105,11 +110,13 @@ Dso* ThreadTree::FindKernelDsoOrNew(const std::string& filename) {
return it->second.get();
}
-void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len, uint64_t pgoff,
- uint64_t time, const std::string& filename) {
+void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr,
+ uint64_t len, uint64_t pgoff, uint64_t time,
+ const std::string& filename) {
ThreadEntry* thread = FindThreadOrNew(pid, tid);
Dso* dso = FindUserDsoOrNew(filename);
- MapEntry* map = AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, false));
+ MapEntry* map =
+ AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, false));
FixOverlappedMap(&thread->maps, map);
auto pair = thread->maps.insert(map);
CHECK(pair.second);
@@ -130,7 +137,8 @@ MapEntry* ThreadTree::AllocateMap(const MapEntry& value) {
return map;
}
-void ThreadTree::FixOverlappedMap(std::set<MapEntry*, MapComparator>* map_set, const MapEntry* map) {
+void ThreadTree::FixOverlappedMap(std::set<MapEntry*, MapComparator>* map_set,
+ const MapEntry* map) {
for (auto it = map_set->begin(); it != map_set->end();) {
if ((*it)->start_addr >= map->get_end_addr()) {
// No more overlapped maps.
@@ -141,14 +149,16 @@ void ThreadTree::FixOverlappedMap(std::set<MapEntry*, MapComparator>* map_set, c
} else {
MapEntry* old = *it;
if (old->start_addr < map->start_addr) {
- MapEntry* before = AllocateMap(MapEntry(old->start_addr, map->start_addr - old->start_addr,
- old->pgoff, old->time, old->dso, old->in_kernel));
+ MapEntry* before = AllocateMap(
+ MapEntry(old->start_addr, map->start_addr - old->start_addr,
+ old->pgoff, old->time, old->dso, old->in_kernel));
map_set->insert(before);
}
if (old->get_end_addr() > map->get_end_addr()) {
- MapEntry* after = AllocateMap(
- MapEntry(map->get_end_addr(), old->get_end_addr() - map->get_end_addr(),
- map->get_end_addr() - old->start_addr + old->pgoff, old->time, old->dso, old->in_kernel));
+ MapEntry* after = AllocateMap(MapEntry(
+ map->get_end_addr(), old->get_end_addr() - map->get_end_addr(),
+ map->get_end_addr() - old->start_addr + old->pgoff, old->time,
+ old->dso, old->in_kernel));
map_set->insert(after);
}
@@ -161,8 +171,10 @@ static bool IsAddrInMap(uint64_t addr, const MapEntry* map) {
return (addr >= map->start_addr && addr < map->get_end_addr());
}
-static MapEntry* FindMapByAddr(const std::set<MapEntry*, MapComparator>& maps, uint64_t addr) {
- // Construct a map_entry which is strictly after the searched map_entry, based on MapComparator.
+static MapEntry* FindMapByAddr(const std::set<MapEntry*, MapComparator>& maps,
+ uint64_t addr) {
+ // Construct a map_entry which is strictly after the searched map_entry, based
+ // on MapComparator.
MapEntry find_map(addr, std::numeric_limits<uint64_t>::max(), 0,
std::numeric_limits<uint64_t>::max(), nullptr, false);
auto it = maps.upper_bound(&find_map);
@@ -172,7 +184,8 @@ static MapEntry* FindMapByAddr(const std::set<MapEntry*, MapComparator>& maps, u
return nullptr;
}
-const MapEntry* ThreadTree::FindMap(const ThreadEntry* thread, uint64_t ip, bool in_kernel) {
+const MapEntry* ThreadTree::FindMap(const ThreadEntry* thread, uint64_t ip,
+ bool in_kernel) {
MapEntry* result = nullptr;
if (!in_kernel) {
result = FindMapByAddr(thread->maps, ip);
@@ -191,7 +204,8 @@ const MapEntry* ThreadTree::FindMap(const ThreadEntry* thread, uint64_t ip) {
return result != nullptr ? result : &unknown_map_;
}
-const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip) {
+const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip,
+ uint64_t* pvaddr_in_file) {
uint64_t vaddr_in_file;
if (map->dso == kernel_dso_.get()) {
vaddr_in_file = ip;
@@ -209,12 +223,15 @@ const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip) {
if (symbol == nullptr) {
symbol = &unknown_symbol_;
}
+ if (pvaddr_in_file != nullptr) {
+ *pvaddr_in_file = vaddr_in_file;
+ }
return symbol;
}
const Symbol* ThreadTree::FindKernelSymbol(uint64_t ip) {
const MapEntry* map = FindMap(nullptr, ip, true);
- return FindSymbol(map, ip);
+ return FindSymbol(map, ip, nullptr);
}
void ThreadTree::ClearThreadAndMap() {
@@ -228,22 +245,23 @@ void ThreadTree::Update(const Record& record) {
if (record.type() == PERF_RECORD_MMAP) {
const MmapRecord& r = *static_cast<const MmapRecord*>(&record);
if (r.InKernel()) {
- AddKernelMap(r.data.addr, r.data.len, r.data.pgoff, r.sample_id.time_data.time,
- r.filename);
+ AddKernelMap(r.data.addr, r.data.len, r.data.pgoff,
+ r.sample_id.time_data.time, r.filename);
} else {
- AddThreadMap(r.data.pid, r.data.tid, r.data.addr, r.data.len, r.data.pgoff,
- r.sample_id.time_data.time, r.filename);
+ AddThreadMap(r.data.pid, r.data.tid, r.data.addr, r.data.len,
+ r.data.pgoff, r.sample_id.time_data.time, r.filename);
}
} else if (record.type() == PERF_RECORD_MMAP2) {
const Mmap2Record& r = *static_cast<const Mmap2Record*>(&record);
if (r.InKernel()) {
- AddKernelMap(r.data.addr, r.data.len, r.data.pgoff, r.sample_id.time_data.time,
- r.filename);
+ AddKernelMap(r.data.addr, r.data.len, r.data.pgoff,
+ r.sample_id.time_data.time, r.filename);
} else {
- std::string filename =
- (r.filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) ? "[unknown]" : r.filename;
- AddThreadMap(r.data.pid, r.data.tid, r.data.addr, r.data.len, r.data.pgoff,
- r.sample_id.time_data.time, filename);
+ std::string filename = (r.filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP)
+ ? "[unknown]"
+ : r.filename;
+ AddThreadMap(r.data.pid, r.data.tid, r.data.addr, r.data.len,
+ r.data.pgoff, r.sample_id.time_data.time, filename);
}
} else if (record.type() == PERF_RECORD_COMM) {
const CommRecord& r = *static_cast<const CommRecord*>(&record);
@@ -272,4 +290,3 @@ void ThreadTree::Update(const Record& record) {
}
} // namespace simpleperf
-
diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h
index b58c5061..113d61df 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -38,15 +38,17 @@ struct MapEntry {
Dso* dso;
bool in_kernel;
- MapEntry(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, Dso* dso, bool in_kernel)
- : start_addr(start_addr), len(len), pgoff(pgoff), time(time), dso(dso), in_kernel(in_kernel) {
- }
- MapEntry() {
- }
-
- uint64_t get_end_addr() const {
- return start_addr + len;
- }
+ MapEntry(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time,
+ Dso* dso, bool in_kernel)
+ : start_addr(start_addr),
+ len(len),
+ pgoff(pgoff),
+ time(time),
+ dso(dso),
+ in_kernel(in_kernel) {}
+ MapEntry() {}
+
+ uint64_t get_end_addr() const { return start_addr + len; }
};
struct MapComparator {
@@ -65,28 +67,30 @@ struct ThreadEntry {
// symbols in executable binaries mapped in the monitored threads.
class ThreadTree {
public:
- ThreadTree() : unknown_symbol_("unknown", 0, std::numeric_limits<unsigned long long>::max()) {
+ ThreadTree()
+ : unknown_symbol_("unknown", 0,
+ std::numeric_limits<unsigned long long>::max()) {
unknown_dso_ = Dso::CreateDso(DSO_ELF_FILE, "unknown");
- unknown_map_ =
- MapEntry(0, std::numeric_limits<unsigned long long>::max(), 0, 0, unknown_dso_.get(), false);
+ unknown_map_ = MapEntry(0, std::numeric_limits<unsigned long long>::max(),
+ 0, 0, unknown_dso_.get(), false);
kernel_dso_ = Dso::CreateDso(DSO_KERNEL, DEFAULT_KERNEL_MMAP_NAME);
}
void AddThread(int pid, int tid, const std::string& comm);
void ForkThread(int pid, int tid, int ppid, int ptid);
ThreadEntry* FindThreadOrNew(int pid, int tid);
- void AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time,
- const std::string& filename);
- void AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len, uint64_t pgoff,
+ void AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff,
uint64_t time, const std::string& filename);
- const MapEntry* FindMap(const ThreadEntry* thread, uint64_t ip, bool in_kernel);
+ void AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len,
+ uint64_t pgoff, uint64_t time, const std::string& filename);
+ const MapEntry* FindMap(const ThreadEntry* thread, uint64_t ip,
+ bool in_kernel);
// Find map for an ip address when we don't know whether it is in kernel.
const MapEntry* FindMap(const ThreadEntry* thread, uint64_t ip);
- const Symbol* FindSymbol(const MapEntry* map, uint64_t ip);
+ const Symbol* FindSymbol(const MapEntry* map, uint64_t ip,
+ uint64_t* pvaddr_in_file);
const Symbol* FindKernelSymbol(uint64_t ip);
- const MapEntry* UnknownMap() const {
- return &unknown_map_;
- }
+ const MapEntry* UnknownMap() const { return &unknown_map_; }
// Clear thread and map information, but keep loaded dso information. It saves
// the time to reload dso information.
@@ -99,7 +103,8 @@ class ThreadTree {
Dso* FindKernelDsoOrNew(const std::string& filename);
Dso* FindUserDsoOrNew(const std::string& filename);
MapEntry* AllocateMap(const MapEntry& value);
- void FixOverlappedMap(std::set<MapEntry*, MapComparator>* map_set, const MapEntry* map);
+ void FixOverlappedMap(std::set<MapEntry*, MapComparator>* map_set,
+ const MapEntry* map);
std::unordered_map<int, std::unique_ptr<ThreadEntry>> thread_tree_;
std::vector<std::unique_ptr<std::string>> thread_comm_storage_;
@@ -122,5 +127,4 @@ using MapEntry = simpleperf::MapEntry;
using ThreadEntry = simpleperf::ThreadEntry;
using ThreadTree = simpleperf::ThreadTree;
-
#endif // SIMPLE_PERF_THREAD_TREE_H_