summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2015-06-08 10:38:10 -0700
committerYabin Cui <yabinc@google.com>2015-06-09 11:09:18 -0700
commitec12ed9010128483993a87d68edc02d3a89d56cf (patch)
tree208a0c69c45725bef9a563146af0004f0cb7191c /simpleperf
parentf7383f39e5a66731e6e34ca85c1ad599f39faf1d (diff)
downloadextras-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.mk2
-rw-r--r--simpleperf/cmd_report.cpp41
-rw-r--r--simpleperf/cmd_report_test.cpp2
-rw-r--r--simpleperf/dso.cpp109
-rw-r--r--simpleperf/dso.h49
-rw-r--r--simpleperf/environment.cpp2
-rw-r--r--simpleperf/read_elf.cpp70
-rw-r--r--simpleperf/read_elf.h12
-rw-r--r--simpleperf/read_elf_test.cpp38
-rw-r--r--simpleperf/sample_tree.cpp92
-rw-r--r--simpleperf/sample_tree.h33
-rw-r--r--simpleperf/sample_tree_test.cpp22
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;
}