diff options
author | Yabin Cui <yabinc@google.com> | 2015-06-08 10:38:10 -0700 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2015-06-09 11:09:18 -0700 |
commit | ec12ed9010128483993a87d68edc02d3a89d56cf (patch) | |
tree | 208a0c69c45725bef9a563146af0004f0cb7191c /simpleperf | |
parent | f7383f39e5a66731e6e34ca85c1ad599f39faf1d (diff) | |
download | extras-ec12ed9010128483993a87d68edc02d3a89d56cf.tar.gz |
Simpleperf: support symbol parsing in report command.
Also fix the storage of ProcessEntry.
Bug: 19483574
Change-Id: I2182a804f6ecbd28e7aa3c1a38a6f19b86f583c9
Diffstat (limited to 'simpleperf')
-rw-r--r-- | simpleperf/Android.mk | 2 | ||||
-rw-r--r-- | simpleperf/cmd_report.cpp | 41 | ||||
-rw-r--r-- | simpleperf/cmd_report_test.cpp | 2 | ||||
-rw-r--r-- | simpleperf/dso.cpp | 109 | ||||
-rw-r--r-- | simpleperf/dso.h | 49 | ||||
-rw-r--r-- | simpleperf/environment.cpp | 2 | ||||
-rw-r--r-- | simpleperf/read_elf.cpp | 70 | ||||
-rw-r--r-- | simpleperf/read_elf.h | 12 | ||||
-rw-r--r-- | simpleperf/read_elf_test.cpp | 38 | ||||
-rw-r--r-- | simpleperf/sample_tree.cpp | 92 | ||||
-rw-r--r-- | simpleperf/sample_tree.h | 33 | ||||
-rw-r--r-- | simpleperf/sample_tree_test.cpp | 22 |
12 files changed, 411 insertions, 61 deletions
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk index d2c5276d..4642e2a5 100644 --- a/simpleperf/Android.mk +++ b/simpleperf/Android.mk @@ -34,6 +34,7 @@ libsimpleperf_src_files := \ cmd_report.cpp \ cmd_stat.cpp \ command.cpp \ + dso.cpp \ environment.cpp \ event_attr.cpp \ event_fd.cpp \ @@ -110,6 +111,7 @@ simpleperf_unit_test_src_files := \ cpu_offline_test.cpp \ environment_test.cpp \ gtest_main.cpp \ + read_elf_test.cpp \ record_file_test.cpp \ record_test.cpp \ sample_tree_test.cpp \ diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp index f2dc443d..d6aca57b 100644 --- a/simpleperf/cmd_report.cpp +++ b/simpleperf/cmd_report.cpp @@ -45,7 +45,7 @@ struct ReportItem { }; static int ComparePid(const SampleEntry& sample1, const SampleEntry& sample2) { - return sample1.process_entry->pid - sample2.process_entry->pid; + return sample1.process->pid - sample2.process->pid; } static std::string PrintHeaderPid() { @@ -53,7 +53,7 @@ static std::string PrintHeaderPid() { } static std::string PrintPid(const SampleEntry& sample) { - return android::base::StringPrintf("%d", sample.process_entry->pid); + return android::base::StringPrintf("%d", sample.process->pid); } static ReportItem report_pid_item = { @@ -63,7 +63,7 @@ static ReportItem report_pid_item = { }; static int CompareComm(const SampleEntry& sample1, const SampleEntry& sample2) { - return strcmp(sample1.process_entry->comm.c_str(), sample2.process_entry->comm.c_str()); + return strcmp(sample1.process->comm.c_str(), sample2.process->comm.c_str()); } static std::string PrintHeaderComm() { @@ -71,7 +71,7 @@ static std::string PrintHeaderComm() { } static std::string PrintComm(const SampleEntry& sample) { - return sample.process_entry->comm; + return sample.process->comm; } static ReportItem report_comm_item = { @@ -81,7 +81,7 @@ static ReportItem report_comm_item = { }; static int CompareDso(const SampleEntry& sample1, const SampleEntry& sample2) { - return strcmp(sample1.map_entry->filename.c_str(), sample2.map_entry->filename.c_str()); + return strcmp(sample1.map->dso->path.c_str(), sample2.map->dso->path.c_str()); } static std::string PrintHeaderDso() { @@ -89,10 +89,8 @@ static std::string PrintHeaderDso() { } static std::string PrintDso(const SampleEntry& sample) { - std::string filename = sample.map_entry->filename; - if (filename == DEFAULT_KERNEL_MMAP_NAME) { - filename = "[kernel.kallsyms]"; - } else if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) { + std::string filename = sample.map->dso->path; + if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) { filename = "[unknown]"; } return filename; @@ -104,8 +102,29 @@ static ReportItem report_dso_item = { .print_function = PrintDso, }; +static int CompareSymbol(const SampleEntry& sample1, const SampleEntry& sample2) { + return strcmp(sample1.symbol->name.c_str(), sample2.symbol->name.c_str()); +} + +static std::string PrintHeaderSymbol() { + return "Symbol"; +} + +static std::string PrintSymbol(const SampleEntry& sample) { + return sample.symbol->name; +} + +static ReportItem report_symbol_item = { + .compare_function = CompareSymbol, + .print_header_function = PrintHeaderSymbol, + .print_function = PrintSymbol, +}; + static std::unordered_map<std::string, ReportItem*> report_item_map = { - {"comm", &report_comm_item}, {"pid", &report_pid_item}, {"dso", &report_dso_item}, + {"comm", &report_comm_item}, + {"pid", &report_pid_item}, + {"dso", &report_dso_item}, + {"symbol", &report_symbol_item}, }; class ReportCommand : public Command { @@ -115,7 +134,7 @@ class ReportCommand : public Command { "Usage: simpleperf report [options]\n" " -i <file> specify path of record file, default is perf.data\n" " --sort key1,key2,... Select the keys to sort and print the report.\n" - " Possible keys include pid, comm, dso.\n" + " Possible keys include pid, comm, dso, symbol.\n" " Default keys are \"comm,pid,dso\"\n"), record_filename_("perf.data") { } diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp index 34d156c3..8281a15f 100644 --- a/simpleperf/cmd_report_test.cpp +++ b/simpleperf/cmd_report_test.cpp @@ -47,5 +47,5 @@ TEST_F(ReportCommandTest, sort_option_pid) { } TEST_F(ReportCommandTest, sort_option_all) { - ASSERT_TRUE(ReportCmd()->Run({"--sort", "comm,pid,dso"})); + ASSERT_TRUE(ReportCmd()->Run({"--sort", "comm,pid,dso,symbol"})); } diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp new file mode 100644 index 00000000..e8cd27c5 --- /dev/null +++ b/simpleperf/dso.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dso.h" + +#include "environment.h" +#include "read_elf.h" + +bool SymbolComparator::operator()(const std::unique_ptr<SymbolEntry>& symbol1, + const std::unique_ptr<SymbolEntry>& symbol2) { + return symbol1->addr < symbol2->addr; +} + +const SymbolEntry* DsoEntry::FindSymbol(uint64_t offset_in_dso) { + std::unique_ptr<SymbolEntry> symbol(new SymbolEntry{ + .name = "", .addr = offset_in_dso, .len = 0, + }); + + auto it = symbols.upper_bound(symbol); + if (it != symbols.begin()) { + --it; + if ((*it)->addr <= offset_in_dso && (*it)->addr + (*it)->len > offset_in_dso) { + return (*it).get(); + } + } + return nullptr; +} + +static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) { + return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' || symbol.type == 'w'); +} + +static bool KernelSymbolCallback(const KernelSymbol& kernel_symbol, DsoEntry* dso) { + if (IsKernelFunctionSymbol(kernel_symbol)) { + SymbolEntry* symbol = new SymbolEntry{ + .name = kernel_symbol.name, .addr = kernel_symbol.addr, .len = 0, + }; + dso->symbols.insert(std::unique_ptr<SymbolEntry>(symbol)); + } + return false; +} + +std::unique_ptr<DsoEntry> DsoFactory::LoadKernel() { + std::unique_ptr<DsoEntry> dso(new DsoEntry); + dso->path = "[kernel.kallsyms]"; + + ProcessKernelSymbols("/proc/kallsyms", + std::bind(&KernelSymbolCallback, std::placeholders::_1, dso.get())); + // Fix symbol.len. + auto prev_it = dso->symbols.end(); + for (auto it = dso->symbols.begin(); it != dso->symbols.end(); ++it) { + if (prev_it != dso->symbols.end()) { + (*prev_it)->len = (*it)->addr - (*prev_it)->addr; + } + prev_it = it; + } + if (prev_it != dso->symbols.end()) { + (*prev_it)->len = ULLONG_MAX - (*prev_it)->addr; + } + return dso; +} + +static void ParseSymbolCallback(const ElfFileSymbol& elf_symbol, DsoEntry* dso, + bool (*filter)(const ElfFileSymbol&)) { + if (filter(elf_symbol)) { + SymbolEntry* symbol = new SymbolEntry{ + .name = elf_symbol.name, .addr = elf_symbol.start_in_file, .len = elf_symbol.len, + }; + dso->symbols.insert(std::unique_ptr<SymbolEntry>(symbol)); + } +} + +static bool SymbolFilterForKernelModule(const ElfFileSymbol& elf_symbol) { + // TODO: Parse symbol outside of .text section. + return (elf_symbol.is_func && elf_symbol.is_in_text_section); +} + +std::unique_ptr<DsoEntry> DsoFactory::LoadKernelModule(const std::string& dso_path) { + std::unique_ptr<DsoEntry> dso(new DsoEntry); + dso->path = dso_path; + ParseSymbolsFromElfFile(dso_path, std::bind(ParseSymbolCallback, std::placeholders::_1, dso.get(), + SymbolFilterForKernelModule)); + return dso; +} + +static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) { + return elf_symbol.is_func; +} + +std::unique_ptr<DsoEntry> DsoFactory::LoadDso(const std::string& dso_path) { + std::unique_ptr<DsoEntry> dso(new DsoEntry); + dso->path = dso_path; + ParseSymbolsFromElfFile(dso_path, std::bind(ParseSymbolCallback, std::placeholders::_1, dso.get(), + SymbolFilterForDso)); + return dso; +} diff --git a/simpleperf/dso.h b/simpleperf/dso.h new file mode 100644 index 00000000..f596cd54 --- /dev/null +++ b/simpleperf/dso.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SIMPLE_PERF_DSO_H_ +#define SIMPLE_PERF_DSO_H_ + +#include <memory> +#include <set> +#include <string> + +struct SymbolEntry { + std::string name; + uint64_t addr; + uint64_t len; +}; + +struct SymbolComparator { + bool operator()(const std::unique_ptr<SymbolEntry>& symbol1, + const std::unique_ptr<SymbolEntry>& symbol2); +}; + +struct DsoEntry { + std::string path; + std::set<std::unique_ptr<SymbolEntry>, SymbolComparator> symbols; + + const SymbolEntry* FindSymbol(uint64_t offset_in_dso); +}; + +class DsoFactory { + public: + static std::unique_ptr<DsoEntry> LoadKernel(); + static std::unique_ptr<DsoEntry> LoadKernelModule(const std::string& dso_path); + static std::unique_ptr<DsoEntry> LoadDso(const std::string& dso_path); +}; + +#endif // SIMPLE_PERF_DSO_H_ diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index 0270b247..f4027fb6 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -182,7 +182,7 @@ static void GetAllModuleFiles(const std::string& path, if (android::base::EndsWith(name, ".ko")) { std::string module_name = name.substr(0, name.size() - 3); std::replace(module_name.begin(), module_name.end(), '-', '_'); - module_file_map->insert(std::make_pair(module_name, path + name)); + module_file_map->insert(std::make_pair(module_name, path + "/" + name)); } } for (auto& name : subdirs) { diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp index 1873b308..65852526 100644 --- a/simpleperf/read_elf.cpp +++ b/simpleperf/read_elf.cpp @@ -114,3 +114,73 @@ bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id) { } return result; } +template <class ELFT> +bool ParseSymbolsFromELFFile(const llvm::object::ELFFile<ELFT>* elf, + std::function<void(const ElfFileSymbol&)> callback) { + auto begin = elf->begin_symbols(); + auto end = elf->end_symbols(); + if (begin == end) { + begin = elf->begin_dynamic_symbols(); + end = elf->end_dynamic_symbols(); + } + for (; begin != end; ++begin) { + auto& elf_symbol = *begin; + + ElfFileSymbol symbol; + memset(&symbol, '\0', sizeof(symbol)); + + auto shdr = elf->getSection(&elf_symbol); + if (shdr == nullptr) { + continue; + } + auto section_name = elf->getSectionName(shdr); + if (section_name.getError() || section_name.get().empty()) { + continue; + } + + symbol.start_in_file = elf_symbol.st_value - shdr->sh_addr + shdr->sh_offset; + symbol.len = elf_symbol.st_size; + int type = elf_symbol.getType(); + if (type & STT_FUNC) { + symbol.is_func = true; + } + if (section_name.get() == ".text") { + symbol.is_in_text_section = true; + } + + auto symbol_name = elf->getSymbolName(begin); + if (symbol_name.getError()) { + continue; + } + symbol.name = symbol_name.get(); + if (symbol.name.empty()) { + continue; + } + callback(symbol); + } + return true; +} + +bool ParseSymbolsFromElfFile(const std::string& filename, + std::function<void(const ElfFileSymbol&)> callback) { + auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename)); + if (owning_binary.getError()) { + PLOG(DEBUG) << "can't open file " << filename; + return false; + } + bool result = false; + llvm::object::Binary* binary = owning_binary.get().getBinary(); + if (auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary)) { + if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) { + result = ParseSymbolsFromELFFile(elf->getELFFile(), callback); + } else if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) { + result = ParseSymbolsFromELFFile(elf->getELFFile(), callback); + } else { + PLOG(DEBUG) << "unknown elf format in file" << filename; + } + } + if (!result) { + PLOG(DEBUG) << "can't parse symbols from file " << filename; + } + return result; +} diff --git a/simpleperf/read_elf.h b/simpleperf/read_elf.h index bc65fea0..ee90c8aa 100644 --- a/simpleperf/read_elf.h +++ b/simpleperf/read_elf.h @@ -17,10 +17,22 @@ #ifndef SIMPLE_PERF_READ_ELF_H_ #define SIMPLE_PERF_READ_ELF_H_ +#include <functional> #include <string> #include "build_id.h" bool GetBuildIdFromNoteFile(const std::string& filename, BuildId* build_id); bool GetBuildIdFromElfFile(const std::string& filename, BuildId* build_id); +struct ElfFileSymbol { + uint64_t start_in_file; + uint64_t len; + bool is_func; + bool is_in_text_section; + std::string name; +}; + +bool ParseSymbolsFromElfFile(const std::string& filename, + std::function<void(const ElfFileSymbol&)> callback); + #endif // SIMPLE_PERF_READ_ELF_H_ diff --git a/simpleperf/read_elf_test.cpp b/simpleperf/read_elf_test.cpp new file mode 100644 index 00000000..bc9ef73a --- /dev/null +++ b/simpleperf/read_elf_test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "read_elf.h" + +#include <gtest/gtest.h> + +static void ParseSymbol(const ElfFileSymbol& symbol, bool* result) { + if (symbol.is_func) { + *result = true; + } +} + +TEST(read_elf, parse_symbols_from_elf_file) { + char elf_file[PATH_MAX]; + ssize_t elf_file_len = readlink("/proc/self/exe", elf_file, sizeof(elf_file)); + ASSERT_GT(elf_file_len, 0L); + ASSERT_LT(static_cast<size_t>(elf_file_len), sizeof(elf_file)); + elf_file[elf_file_len] = '\0'; + + bool result = false; + ASSERT_TRUE( + ParseSymbolsFromElfFile(elf_file, std::bind(ParseSymbol, std::placeholders::_1, &result))); + ASSERT_TRUE(result); +} diff --git a/simpleperf/sample_tree.cpp b/simpleperf/sample_tree.cpp index b980af41..5c359e84 100644 --- a/simpleperf/sample_tree.cpp +++ b/simpleperf/sample_tree.cpp @@ -18,6 +18,8 @@ #include <base/logging.h> +#include "environment.h" + bool SampleTree::MapComparator::operator()(const MapEntry* map1, const MapEntry* map2) { if (map1->pid != map2->pid) { return map1->pid < map2->pid; @@ -35,39 +37,58 @@ bool SampleTree::MapComparator::operator()(const MapEntry* map1, const MapEntry* } void SampleTree::AddProcess(int pid, const std::string& comm) { - ProcessEntry process = { + ProcessEntry* process = new ProcessEntry{ .pid = pid, .comm = comm, }; - process_tree_[pid] = process; + // TODO: Check the return value of below insertion operation when per thread comm is used. + process_tree_.insert(std::make_pair(pid, std::unique_ptr<ProcessEntry>(process))); } void SampleTree::AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, const std::string& filename) { + DsoEntry* dso = FindKernelDsoOrNew(filename); MapEntry* map = new MapEntry{ - .pid = -1, - .start_addr = start_addr, - .len = len, - .pgoff = pgoff, - .time = time, - .filename = filename, + .pid = -1, .start_addr = start_addr, .len = len, .pgoff = pgoff, .time = time, .dso = dso, }; - map_storage_.push_back(map); - kernel_map_tree_.insert(map); + map_storage_.push_back(std::unique_ptr<MapEntry>(map)); + auto pair = kernel_map_tree_.insert(map); + CHECK(pair.second); +} + +DsoEntry* SampleTree::FindKernelDsoOrNew(const std::string& filename) { + if (filename == DEFAULT_KERNEL_MMAP_NAME) { + if (kernel_dso_ == nullptr) { + kernel_dso_ = DsoFactory::LoadKernel(); + } + return kernel_dso_.get(); + } + auto it = module_dso_tree_.find(filename); + if (it == module_dso_tree_.end()) { + module_dso_tree_[filename] = DsoFactory::LoadKernelModule(filename); + it = module_dso_tree_.find(filename); + } + return it->second.get(); } void SampleTree::AddUserMap(int pid, uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, const std::string& filename) { + DsoEntry* dso = FindUserDsoOrNew(filename); MapEntry* map = new MapEntry{ - .pid = pid, - .start_addr = start_addr, - .len = len, - .pgoff = pgoff, - .time = time, - .filename = filename, + .pid = pid, .start_addr = start_addr, .len = len, .pgoff = pgoff, .time = time, .dso = dso, }; - map_storage_.push_back(map); + map_storage_.push_back(std::unique_ptr<MapEntry>(map)); RemoveOverlappedUserMap(map); - user_map_tree_.insert(map); + auto pair = user_map_tree_.insert(map); + CHECK(pair.second); +} + +DsoEntry* SampleTree::FindUserDsoOrNew(const std::string& filename) { + auto it = user_dso_tree_.find(filename); + if (it == user_dso_tree_.end()) { + user_dso_tree_[filename] = DsoFactory::LoadDso(filename); + it = user_dso_tree_.find(filename); + } + return it->second.get(); } void SampleTree::RemoveOverlappedUserMap(const MapEntry* map) { @@ -90,13 +111,14 @@ void SampleTree::RemoveOverlappedUserMap(const MapEntry* map) { const ProcessEntry* SampleTree::FindProcessEntryOrNew(int pid) { auto it = process_tree_.find(pid); if (it == process_tree_.end()) { - ProcessEntry new_entry = { + ProcessEntry* process = new ProcessEntry{ .pid = pid, .comm = "unknown", }; - auto pair = process_tree_.insert(std::make_pair(pid, new_entry)); + auto pair = process_tree_.insert(std::make_pair(pid, std::unique_ptr<ProcessEntry>(process))); it = pair.first; + CHECK(pair.second); } - return &it->second; + return it->second.get(); } static bool IsIpInMap(int pid, uint64_t ip, const MapEntry* map) { @@ -132,11 +154,12 @@ const MapEntry* SampleTree::FindUnknownMapEntryOrNew(int pid) { .len = static_cast<uint64_t>(-1), .pgoff = 0, .time = 0, - .filename = "unknown", + .dso = &unknown_dso_, }; - map_storage_.push_back(map); + map_storage_.push_back(std::unique_ptr<MapEntry>(map)); auto pair = unknown_maps_.insert(std::make_pair(pid, map)); it = pair.first; + CHECK(pair.second); } return it->second; } @@ -144,6 +167,7 @@ const MapEntry* SampleTree::FindUnknownMapEntryOrNew(int pid) { void SampleTree::AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_t period) { const ProcessEntry* process_entry = FindProcessEntryOrNew(pid); const MapEntry* map_entry = FindMapEntryOrNew(pid, ip); + const SymbolEntry* symbol_entry = FindSymbolEntry(ip, map_entry); SampleEntry find_sample = { .tid = tid, @@ -151,12 +175,14 @@ void SampleTree::AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_ .time = time, .period = period, .sample_count = 1, - .process_entry = process_entry, - .map_entry = map_entry, + .process = process_entry, + .map = map_entry, + .symbol = symbol_entry, }; auto it = sample_tree_.find(find_sample); if (it == sample_tree_.end()) { - sample_tree_.insert(find_sample); + auto pair = sample_tree_.insert(find_sample); + CHECK(pair.second); } else { SampleEntry* sample_entry = const_cast<SampleEntry*>(&*it); sample_entry->period += period; @@ -166,6 +192,20 @@ void SampleTree::AddSample(int pid, int tid, uint64_t ip, uint64_t time, uint64_ total_period_ += period; } +const SymbolEntry* SampleTree::FindSymbolEntry(uint64_t ip, const MapEntry* map_entry) { + uint64_t offset_in_file; + if (map_entry->dso == kernel_dso_.get()) { + offset_in_file = ip; + } else { + offset_in_file = ip - map_entry->start_addr + map_entry->pgoff; + } + const SymbolEntry* symbol = map_entry->dso->FindSymbol(offset_in_file); + if (symbol == nullptr) { + symbol = &unknown_symbol_; + } + return symbol; +} + void SampleTree::VisitAllSamples(std::function<void(const SampleEntry&)> callback) { if (sorted_sample_tree_.size() != sample_tree_.size()) { sorted_sample_tree_.clear(); diff --git a/simpleperf/sample_tree.h b/simpleperf/sample_tree.h index 852f4da7..6d18393d 100644 --- a/simpleperf/sample_tree.h +++ b/simpleperf/sample_tree.h @@ -17,12 +17,15 @@ #ifndef SIMPLE_PERF_SAMPLE_TREE_H_ #define SIMPLE_PERF_SAMPLE_TREE_H_ +#include <limits.h> #include <functional> #include <set> #include <string> #include <unordered_map> #include <vector> +#include "dso.h" + struct ProcessEntry { int pid; std::string comm; @@ -34,7 +37,7 @@ struct MapEntry { uint64_t len; uint64_t pgoff; uint64_t time; // Map creation time. - std::string filename; + DsoEntry* dso; }; struct SampleEntry { @@ -43,8 +46,9 @@ struct SampleEntry { uint64_t time; uint64_t period; uint64_t sample_count; - const ProcessEntry* process_entry; - const MapEntry* map_entry; + const ProcessEntry* process; + const MapEntry* map; + const SymbolEntry* symbol; }; typedef std::function<int(const SampleEntry&, const SampleEntry&)> compare_sample_func_t; @@ -58,12 +62,10 @@ class SampleTree { sorted_sample_tree_(sorted_sample_comparator_), total_samples_(0), total_period_(0) { - } - - ~SampleTree() { - for (auto& map : map_storage_) { - delete map; - } + unknown_dso_.path = "unknown"; + unknown_symbol_ = { + .name = "unknown", .addr = 0, .len = ULLONG_MAX, + }; } void AddProcess(int pid, const std::string& comm); @@ -87,6 +89,9 @@ class SampleTree { const ProcessEntry* FindProcessEntryOrNew(int pid); const MapEntry* FindMapEntryOrNew(int pid, uint64_t ip); const MapEntry* FindUnknownMapEntryOrNew(int pid); + DsoEntry* FindKernelDsoOrNew(const std::string& filename); + DsoEntry* FindUserDsoOrNew(const std::string& filename); + const SymbolEntry* FindSymbolEntry(uint64_t ip, const MapEntry* map_entry); struct MapComparator { bool operator()(const MapEntry* map1, const MapEntry* map2); @@ -116,12 +121,18 @@ class SampleTree { compare_sample_func_t compare_function; }; - std::unordered_map<int, ProcessEntry> process_tree_; + std::unordered_map<int, std::unique_ptr<ProcessEntry>> process_tree_; std::set<MapEntry*, MapComparator> kernel_map_tree_; std::set<MapEntry*, MapComparator> user_map_tree_; std::unordered_map<int, MapEntry*> unknown_maps_; - std::vector<MapEntry*> map_storage_; + std::vector<std::unique_ptr<MapEntry>> map_storage_; + + std::unique_ptr<DsoEntry> kernel_dso_; + std::unordered_map<std::string, std::unique_ptr<DsoEntry>> module_dso_tree_; + std::unordered_map<std::string, std::unique_ptr<DsoEntry>> user_dso_tree_; + DsoEntry unknown_dso_; + SymbolEntry unknown_symbol_; SampleComparator sample_comparator_; std::set<SampleEntry, SampleComparator> sample_tree_; diff --git a/simpleperf/sample_tree_test.cpp b/simpleperf/sample_tree_test.cpp index 114fa037..166297db 100644 --- a/simpleperf/sample_tree_test.cpp +++ b/simpleperf/sample_tree_test.cpp @@ -29,12 +29,12 @@ struct ExpectedSampleInMap { static void SampleMatchExpectation(const SampleEntry& sample, const ExpectedSampleInMap& expected, bool* has_error) { *has_error = true; - ASSERT_TRUE(sample.process_entry != nullptr); - ASSERT_EQ(expected.pid, sample.process_entry->pid); + ASSERT_TRUE(sample.process != nullptr); + ASSERT_EQ(expected.pid, sample.process->pid); ASSERT_EQ(expected.tid, sample.tid); - ASSERT_TRUE(sample.map_entry != nullptr); - ASSERT_EQ(expected.map_pid, sample.map_entry->pid); - ASSERT_EQ(expected.map_start_addr, sample.map_entry->start_addr); + ASSERT_TRUE(sample.map != nullptr); + ASSERT_EQ(expected.map_pid, sample.map->pid); + ASSERT_EQ(expected.map_start_addr, sample.map->start_addr); ASSERT_EQ(expected.sample_count, sample.sample_count); *has_error = false; } @@ -49,17 +49,17 @@ static void CheckSampleCallback(const SampleEntry& sample, } static int CompareSampleFunction(const SampleEntry& sample1, const SampleEntry& sample2) { - if (sample1.process_entry->pid != sample2.process_entry->pid) { - return sample1.process_entry->pid - sample2.process_entry->pid; + if (sample1.process->pid != sample2.process->pid) { + return sample1.process->pid - sample2.process->pid; } if (sample1.tid != sample2.tid) { return sample1.tid - sample2.tid; } - if (sample1.map_entry->pid != sample2.map_entry->pid) { - return sample1.map_entry->pid - sample2.map_entry->pid; + if (sample1.map->pid != sample2.map->pid) { + return sample1.map->pid - sample2.map->pid; } - if (sample1.map_entry->start_addr != sample2.map_entry->start_addr) { - return sample1.map_entry->start_addr - sample2.map_entry->start_addr; + if (sample1.map->start_addr != sample2.map->start_addr) { + return sample1.map->start_addr - sample2.map->start_addr; } return 0; } |