From 9970a2344333f2c19d9126cfec4f833f50ff2f22 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 29 Jun 2016 12:18:11 -0700 Subject: 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 --- simpleperf/SampleComparator.h | 11 +++- simpleperf/SampleDisplayer.h | 105 ++++++++++++++++++--------------- simpleperf/callchain.h | 30 +++++----- simpleperf/cmd_kmem.cpp | 12 ++-- simpleperf/cmd_record.cpp | 2 +- simpleperf/cmd_report.cpp | 121 ++++++++++++++++++++++++++++----------- simpleperf/cmd_report_sample.cpp | 8 +-- simpleperf/cmd_report_test.cpp | 6 ++ simpleperf/sample_tree.h | 15 ++++- simpleperf/sample_tree_test.cpp | 3 +- simpleperf/thread_tree.cpp | 87 +++++++++++++++++----------- simpleperf/thread_tree.h | 48 +++++++++------- 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 +#include #include #include @@ -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 \ - 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 \ + 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 -void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix, - const std::unique_ptr& 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 +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& 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 -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; 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 #include +#include #include #include #include @@ -42,9 +43,11 @@ struct CallChainRoot { CallChainRoot() : children_period(0) {} - void AddCallChain(const std::vector& callchain, uint64_t period) { + void AddCallChain( + const std::vector& callchain, uint64_t period, + std::function 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 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>& nodes, - const EntryT* sample) { + NodeT* FindMatchingNode( + const std::vector>& nodes, const EntryT* sample, + std::function 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& chain, - size_t chain_start) { + size_t GetMatchingLengthInNode( + NodeT* node, const std::vector& chain, size_t chain_start, + std::function 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 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& 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; using SlabSampleTreeDisplayer = SampleTreeDisplayer; +using SlabSampleCallgraphDisplayer = + CallgraphDisplayer>; 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 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 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 { 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( - 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 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 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 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& 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; using ReportCmdSampleTreeDisplayer = SampleTreeDisplayer; +using ReportCmdCallgraphDisplayer = + CallgraphDisplayer>; + +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 Look for files with symbols relative to this directory.\n" "--tids tid1,tid2,... Report only for selected tids.\n" "--vmlinux Parse kernel symbols from .\n" @@ -447,9 +481,6 @@ bool ReportCommand::ParseOptions(const std::vector& 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& 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& 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 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 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& callchain, const AccumulateInfoT& acc_info) = 0; virtual const ThreadEntry* GetThreadOfSample(EntryT*) = 0; - virtual void InsertCallChainForSample(EntryT* sample, - const std::vector& 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& 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> sample_set_; bool accumulate_callchain_; private: + const SampleComparator 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> 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 { const ThreadEntry* GetThreadOfSample(SampleEntry*) override { return nullptr; } - void InsertCallChainForSample(SampleEntry*, const std::vector&, - 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::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::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(), // maps }; - auto pair = thread_tree_.insert(std::make_pair(tid, std::unique_ptr(thread))); + auto pair = thread_tree_.insert( + std::make_pair(tid, std::unique_ptr(thread))); CHECK(pair.second); it = pair.first; } - thread_comm_storage_.push_back(std::unique_ptr(new std::string(comm))); + thread_comm_storage_.push_back( + std::unique_ptr(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* map_set, const MapEntry* map) { +void ThreadTree::FixOverlappedMap(std::set* 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* 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& 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& 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::max(), 0, std::numeric_limits::max(), nullptr, false); auto it = maps.upper_bound(&find_map); @@ -172,7 +184,8 @@ static MapEntry* FindMapByAddr(const std::set& 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(&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(&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(&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::max()) { + ThreadTree() + : unknown_symbol_("unknown", 0, + std::numeric_limits::max()) { unknown_dso_ = Dso::CreateDso(DSO_ELF_FILE, "unknown"); - unknown_map_ = - MapEntry(0, std::numeric_limits::max(), 0, 0, unknown_dso_.get(), false); + unknown_map_ = MapEntry(0, std::numeric_limits::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* map_set, const MapEntry* map); + void FixOverlappedMap(std::set* map_set, + const MapEntry* map); std::unordered_map> thread_tree_; std::vector> 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_ -- cgit v1.2.3