diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2017-05-22 18:14:51 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-05-22 18:14:51 +0000 |
commit | b8b93042c948bf8fa329336e43481d3d50227039 (patch) | |
tree | c1ce718db9892825761737404d0716280863971b | |
parent | 542c1b32fa9b9b878ff353336dc36df4d6f442e7 (diff) | |
parent | 63a1c3d83a68e9d94f37b71afe76d0769d744448 (diff) | |
download | extras-b8b93042c948bf8fa329336e43481d3d50227039.tar.gz |
Merge "simpleperf: support [vdso]."
-rw-r--r-- | simpleperf/cmd_record.cpp | 3 | ||||
-rw-r--r-- | simpleperf/dso.cpp | 32 | ||||
-rw-r--r-- | simpleperf/dso.h | 13 | ||||
-rw-r--r-- | simpleperf/environment.cpp | 28 | ||||
-rw-r--r-- | simpleperf/environment.h | 1 | ||||
-rw-r--r-- | simpleperf/environment_test.cpp | 18 | ||||
-rw-r--r-- | simpleperf/thread_tree.cpp | 7 | ||||
-rw-r--r-- | simpleperf/thread_tree.h | 2 |
8 files changed, 92 insertions, 12 deletions
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 27c089c3..1eff009e 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -29,6 +29,7 @@ #include <android-base/parsedouble.h> #include <android-base/parseint.h> #include <android-base/strings.h> +#include <android-base/test_utils.h> #include "command.h" #include "dwarf_unwind.h" @@ -227,12 +228,14 @@ class RecordCommand : public Command { }; bool RecordCommand::Run(const std::vector<std::string>& args) { + // 0. Do some environment preparation. if (!CheckPerfEventLimit()) { return false; } if (!InitPerfClock()) { return false; } + PrepareVdsoFile(); // 1. Parse options, and use default measured event type if not given. std::vector<std::string> workload_args; diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp index 40603fe8..4f202bcd 100644 --- a/simpleperf/dso.cpp +++ b/simpleperf/dso.cpp @@ -60,6 +60,8 @@ bool Dso::read_kernel_symbols_from_proc_; std::unordered_map<std::string, BuildId> Dso::build_id_map_; size_t Dso::dso_count_; uint32_t Dso::g_dump_id_; +std::unique_ptr<TemporaryFile> Dso::vdso_64bit_; +std::unique_ptr<TemporaryFile> Dso::vdso_32bit_; void Dso::SetDemangle(bool demangle) { demangle_ = demangle; } @@ -119,6 +121,14 @@ void Dso::SetBuildIds( build_id_map_ = std::move(map); } +void Dso::SetVdsoFile(std::unique_ptr<TemporaryFile> vdso_file, bool is_64bit) { + if (is_64bit) { + vdso_64bit_ = std::move(vdso_file); + } else { + vdso_32bit_ = std::move(vdso_file); + } +} + BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) { auto it = build_id_map_.find(path); if (it != build_id_map_.end()) { @@ -131,12 +141,12 @@ BuildId Dso::GetExpectedBuildId() { return FindExpectedBuildIdForPath(path_); } -std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, - const std::string& dso_path) { - return std::unique_ptr<Dso>(new Dso(dso_type, dso_path)); +std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path, + bool force_64bit) { + return std::unique_ptr<Dso>(new Dso(dso_type, dso_path, force_64bit)); } -Dso::Dso(DsoType type, const std::string& path) +Dso::Dso(DsoType type, const std::string& path, bool force_64bit) : type_(type), path_(path), debug_file_path_(path), @@ -158,6 +168,12 @@ Dso::Dso(DsoType type, const std::string& path) if (IsRegularFile(file_path)) { debug_file_path_ = path_in_symfs; } + } else if (path == "[vdso]") { + if (force_64bit && vdso_64bit_ != nullptr) { + debug_file_path_ = vdso_64bit_->path; + } else if (!force_64bit && vdso_32bit_ != nullptr) { + debug_file_path_ = vdso_32bit_->path; + } } size_t pos = path.find_last_of("/\\"); if (pos != std::string::npos) { @@ -179,6 +195,8 @@ Dso::~Dso() { read_kernel_symbols_from_proc_ = false; build_id_map_.clear(); g_dump_id_ = 0; + vdso_64bit_ = nullptr; + vdso_32bit_ = nullptr; } } @@ -326,11 +344,15 @@ static void VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol, } } -bool CheckReadSymbolResult(ElfStatus result, const std::string& filename) { +bool Dso::CheckReadSymbolResult(ElfStatus result, const std::string& filename) { if (result == ElfStatus::NO_ERROR) { LOG(VERBOSE) << "Read symbols from " << filename << " successfully"; return true; } else if (result == ElfStatus::NO_SYMBOL_TABLE) { + if (path_ == "[vdso]") { + // Vdso only contains dynamic symbol table, and we can't change that. + return true; + } // Lacking symbol table isn't considered as an error but worth reporting. LOG(WARNING) << filename << " doesn't contain symbol table"; return true; diff --git a/simpleperf/dso.h b/simpleperf/dso.h index b742a9cc..f41b1404 100644 --- a/simpleperf/dso.h +++ b/simpleperf/dso.h @@ -22,7 +22,10 @@ #include <unordered_map> #include <vector> +#include <android-base/test_utils.h> + #include "build_id.h" +#include "read_elf.h" struct Symbol { uint64_t addr; @@ -96,9 +99,10 @@ class Dso { static void SetBuildIds( const std::vector<std::pair<std::string, BuildId>>& build_ids); static BuildId FindExpectedBuildIdForPath(const std::string& path); + static void SetVdsoFile(std::unique_ptr<TemporaryFile> vdso_file, bool is_64bit); - static std::unique_ptr<Dso> CreateDso(DsoType dso_type, - const std::string& dso_path); + static std::unique_ptr<Dso> CreateDso(DsoType dso_type, const std::string& dso_path, + bool force_64bit = false); ~Dso(); @@ -148,8 +152,10 @@ class Dso { static std::unordered_map<std::string, BuildId> build_id_map_; static size_t dso_count_; static uint32_t g_dump_id_; + static std::unique_ptr<TemporaryFile> vdso_64bit_; + static std::unique_ptr<TemporaryFile> vdso_32bit_; - Dso(DsoType type, const std::string& path); + Dso(DsoType type, const std::string& path, bool force_64bit); void Load(); bool LoadKernel(); bool LoadKernelModule(); @@ -157,6 +163,7 @@ class Dso { bool LoadEmbeddedElfFile(); void FixupSymbolLength(); BuildId GetExpectedBuildId(); + bool CheckReadSymbolResult(ElfStatus result, const std::string& filename); const DsoType type_; // path of the shared library used by the profiled program diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index ed161c23..0ad2d6bd 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -475,3 +475,31 @@ ArchType GetMachineArch() { } return GetBuildArch(); } + +void PrepareVdsoFile() { + // vdso is an elf file in memory loaded in each process's user space by the kernel. To read + // symbols from it and unwind through it, we need to dump it into a file in storage. + // It doesn't affect much when failed to prepare vdso file, so there is no need to return values. + std::vector<ThreadMmap> thread_mmaps; + if (!GetThreadMmapsInProcess(getpid(), &thread_mmaps)) { + return; + } + const ThreadMmap* vdso_map = nullptr; + for (const auto& map : thread_mmaps) { + if (map.name == "[vdso]") { + vdso_map = ↦ + break; + } + } + if (vdso_map == nullptr) { + return; + } + std::string s(vdso_map->len, '\0'); + memcpy(&s[0], reinterpret_cast<void*>(static_cast<uintptr_t>(vdso_map->start_addr)), + vdso_map->len); + std::unique_ptr<TemporaryFile> tmpfile(new TemporaryFile); + if (!android::base::WriteStringToFile(s, tmpfile->path)) { + return; + } + Dso::SetVdsoFile(std::move(tmpfile), sizeof(size_t) == sizeof(uint64_t)); +} diff --git a/simpleperf/environment.h b/simpleperf/environment.h index 11eee2ff..2f4d58b9 100644 --- a/simpleperf/environment.h +++ b/simpleperf/environment.h @@ -89,5 +89,6 @@ static inline int gettid() { #endif ArchType GetMachineArch(); +void PrepareVdsoFile(); #endif // SIMPLE_PERF_ENVIRONMENT_H_ diff --git a/simpleperf/environment_test.cpp b/simpleperf/environment_test.cpp index 9b4cbab3..48914674 100644 --- a/simpleperf/environment_test.cpp +++ b/simpleperf/environment_test.cpp @@ -16,6 +16,9 @@ #include <gtest/gtest.h> +#include <android-base/file.h> + +#include "dso.h" #include "environment.h" TEST(environment, GetCpusFromString) { @@ -24,3 +27,18 @@ TEST(environment, GetCpusFromString) { ASSERT_EQ(GetCpusFromString("0,2-3"), std::vector<int>({0, 2, 3})); ASSERT_EQ(GetCpusFromString("1,0-3,3,4"), std::vector<int>({0, 1, 2, 3, 4})); } + +TEST(environment, PrepareVdsoFile) { + std::string content; + ASSERT_TRUE(android::base::ReadFileToString("/proc/self/maps", &content)); + if (content.find("[vdso]") == std::string::npos) { + // Vdso isn't used, no need to test. + return; + } + PrepareVdsoFile(); + std::unique_ptr<Dso> dso = Dso::CreateDso(DSO_ELF_FILE, "[vdso]", + sizeof(size_t) == sizeof(uint64_t)); + ASSERT_TRUE(dso != nullptr); + const std::vector<Symbol>& symbols = dso->GetSymbols(); + ASSERT_FALSE(symbols.empty()); +} diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp index 9def6f21..a6f86a8a 100644 --- a/simpleperf/thread_tree.cpp +++ b/simpleperf/thread_tree.cpp @@ -130,7 +130,7 @@ 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); + Dso* dso = FindUserDsoOrNew(filename, start_addr); MapEntry* map = AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, false)); FixOverlappedMap(thread->maps, map); @@ -138,10 +138,11 @@ void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr, CHECK(pair.second); } -Dso* ThreadTree::FindUserDsoOrNew(const std::string& filename) { +Dso* ThreadTree::FindUserDsoOrNew(const std::string& filename, uint64_t start_addr) { auto it = user_dso_tree_.find(filename); if (it == user_dso_tree_.end()) { - user_dso_tree_[filename] = Dso::CreateDso(DSO_ELF_FILE, filename); + bool force_64bit = start_addr > UINT_MAX; + user_dso_tree_[filename] = Dso::CreateDso(DSO_ELF_FILE, filename, force_64bit); it = user_dso_tree_.find(filename); } return it->second.get(); diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h index 79a4439c..8df6b7ba 100644 --- a/simpleperf/thread_tree.h +++ b/simpleperf/thread_tree.h @@ -123,7 +123,7 @@ class ThreadTree { private: ThreadEntry* CreateThread(int pid, int tid); Dso* FindKernelDsoOrNew(const std::string& filename); - Dso* FindUserDsoOrNew(const std::string& filename); + Dso* FindUserDsoOrNew(const std::string& filename, uint64_t start_addr = 0); MapEntry* AllocateMap(const MapEntry& value); void FixOverlappedMap(MapSet* maps, const MapEntry* map); |