diff options
author | Yabin Cui <yabinc@google.com> | 2018-03-26 17:34:00 -0700 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2018-03-28 17:16:27 -0700 |
commit | 516a87cd05e6f7dcf2c45fd8ba6d1d0e1e1e7bdd (patch) | |
tree | 642ab8433692d1862900ac93c7a24fc131ef4822 /simpleperf/dso.cpp | |
parent | 81ed0ea0e64d536287e823431f4999f183cc3cf1 (diff) | |
download | extras-516a87cd05e6f7dcf2c45fd8ba6d1d0e1e1e7bdd.tar.gz |
simpleperf: support showing symbols for interpreted java code.
To convert from a dex_pc (returned by libunwindstack) to a symbol name,
we need below things:
1. The mapping info of the vdex file containing the dex_pc.
2. The offsets of dex files in the vdex file.
So make below changes:
1. Record none executable maps when profiling java code.
2. Refactor dso code to add a new type for dex file, using DexFileDso
to store dex file offsets in a vdex file, and load symbols from that
vdex file.
3. Add read_dex_file.cpp to read java symbols using libdexfile.
4. Change the format of file section in record_file_format.h, to store
dex file offsets in vdex files.
Bug: http://b/73126888
Bug: http://b/77236599
Test: Run simpleperf to profile several apps manually, can see
Test: callstacks of both java code and native code.
Test: Run simpleperf_unit_test.
Change-Id: I08005a03beb3df1a70db034bc463f555934856ba
Diffstat (limited to 'simpleperf/dso.cpp')
-rw-r--r-- | simpleperf/dso.cpp | 480 |
1 files changed, 222 insertions, 258 deletions
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp index 4825ac72..f3e9b2ce 100644 --- a/simpleperf/dso.cpp +++ b/simpleperf/dso.cpp @@ -28,6 +28,7 @@ #include "environment.h" #include "read_apk.h" +#include "read_dex_file.h" #include "read_elf.h" #include "utils.h" @@ -38,7 +39,8 @@ Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len) len(len), name_(symbol_name_allocator.AllocateString(name)), demangled_name_(nullptr), - dump_id_(UINT_MAX) {} + dump_id_(UINT_MAX) { +} const char* Symbol::DemangledName() const { if (demangled_name_ == nullptr) { @@ -141,41 +143,14 @@ BuildId Dso::GetExpectedBuildId() { return FindExpectedBuildIdForPath(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, bool force_64bit) +Dso::Dso(DsoType type, const std::string& path, const std::string& debug_file_path) : type_(type), path_(path), - debug_file_path_(path), - min_vaddr_(std::numeric_limits<uint64_t>::max()), + debug_file_path_(debug_file_path), is_loaded_(false), dump_id_(UINT_MAX), symbol_dump_id_(0), symbol_warning_loglevel_(android::base::WARNING) { - if (type_ == DSO_KERNEL) { - min_vaddr_ = 0; - } - // Check if file matching path_ exists in symfs directory before using it as - // debug_file_path_. - if (!symfs_dir_.empty()) { - std::string path_in_symfs = symfs_dir_ + path_; - std::tuple<bool, std::string, std::string> tuple = - SplitUrlInApk(path_in_symfs); - std::string file_path = - std::get<0>(tuple) ? std::get<1>(tuple) : path_in_symfs; - if (IsRegularFile(file_path)) { - debug_file_path_ = path_in_symfs; - } - } else if (path == "[vdso]") { - if (force_64bit && !vdso_64bit_.empty()) { - debug_file_path_ = vdso_64bit_; - } else if (!force_64bit && !vdso_32bit_.empty()) { - debug_file_path_ = vdso_32bit_; - } - } size_t pos = path.find_last_of("/\\"); if (pos != std::string::npos) { file_name_ = path.substr(pos + 1); @@ -214,15 +189,13 @@ const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) { if (!is_loaded_) { Load(); } - if (!symbols_.empty()) { - auto it = std::upper_bound(symbols_.begin(), symbols_.end(), - Symbol("", vaddr_in_dso, 0), - Symbol::CompareValueByAddr); - if (it != symbols_.begin()) { - --it; - if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) { - return &*it; - } + auto it = std::upper_bound(symbols_.begin(), symbols_.end(), + Symbol("", vaddr_in_dso, 0), + Symbol::CompareValueByAddr); + if (it != symbols_.begin()) { + --it; + if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) { + return &*it; } } if (!unknown_symbols_.empty()) { @@ -234,13 +207,6 @@ const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) { return nullptr; } -const std::vector<Symbol>& Dso::GetSymbols() { - if (!is_loaded_) { - Load(); - } - return symbols_; -} - void Dso::SetSymbols(std::vector<Symbol>* symbols) { symbols_ = std::move(*symbols); symbols->clear(); @@ -250,252 +216,248 @@ void Dso::AddUnknownSymbol(uint64_t vaddr_in_dso, const std::string& name) { unknown_symbols_.insert(std::make_pair(vaddr_in_dso, Symbol(name, vaddr_in_dso, 1))); } -uint64_t Dso::MinVirtualAddress() { - if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) { - min_vaddr_ = 0; - if (type_ == DSO_ELF_FILE) { - BuildId build_id = GetExpectedBuildId(); - - uint64_t addr; - ElfStatus result = ReadMinExecutableVirtualAddressFromElfFile( - GetDebugFilePath(), build_id, &addr); - if (result != ElfStatus::NO_ERROR) { - LOG(WARNING) << "failed to read min virtual address of " - << GetDebugFilePath() << ": " << result; - } else { - min_vaddr_ = addr; - } - } - } - return min_vaddr_; -} - -static std::vector<Symbol> MergeSortedSymbols(const std::vector<Symbol>& s1, - const std::vector<Symbol>& s2) { - std::vector<Symbol> result; - std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result), - Symbol::CompareValueByAddr); - return result; -} - void Dso::Load() { is_loaded_ = true; - std::vector<Symbol> dumped_symbols; - if (!symbols_.empty()) { - // If symbols has been read from file feature section of perf.data, move it to - // dumped_symbols, so later we can merge them with symbols read from file system. - dumped_symbols = std::move(symbols_); - symbols_.clear(); - // Don't warn missing symbol table if we have dumped symbols in perf.data. - symbol_warning_loglevel_ = android::base::DEBUG; - } - bool result = false; - switch (type_) { - case DSO_KERNEL: - result = LoadKernel(); - break; - case DSO_KERNEL_MODULE: - result = LoadKernelModule(); - break; - case DSO_ELF_FILE: { - if (std::get<0>(SplitUrlInApk(path_))) { - result = LoadEmbeddedElfFile(); - } else { - result = LoadElfFile(); - } - break; - } - } - if (result) { - std::sort(symbols_.begin(), symbols_.end(), Symbol::CompareValueByAddr); - FixupSymbolLength(); - } else { - symbols_.clear(); - } - - if (symbols_.empty()) { - symbols_ = std::move(dumped_symbols); - } else if (!dumped_symbols.empty()) { - symbols_ = MergeSortedSymbols(symbols_, dumped_symbols); - } - + std::vector<Symbol> symbols = LoadSymbols(); if (symbols_.empty()) { - LOG(DEBUG) << "failed to load dso: " << path_; - } -} - -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, - std::vector<Symbol>* symbols) { - if (IsKernelFunctionSymbol(kernel_symbol)) { - symbols->emplace_back(Symbol(kernel_symbol.name, kernel_symbol.addr, 0)); - } - return false; -} - -static void VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol, - std::vector<Symbol>* symbols) { - if (elf_symbol.is_func) { - symbols->emplace_back( - Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len)); + symbols_ = std::move(symbols); + } else { + std::vector<Symbol> merged_symbols; + std::set_union(symbols_.begin(), symbols_.end(), symbols.begin(), symbols.end(), + std::back_inserter(merged_symbols), Symbol::CompareValueByAddr); + symbols_ = std::move(merged_symbols); } } -bool Dso::CheckReadSymbolResult(ElfStatus result, const std::string& filename) { +static void ReportReadElfSymbolResult(ElfStatus result, const std::string& path, + const std::string& debug_file_path, + android::base::LogSeverity warning_loglevel = android::base::WARNING) { if (result == ElfStatus::NO_ERROR) { - LOG(VERBOSE) << "Read symbols from " << filename << " successfully"; - return true; + LOG(VERBOSE) << "Read symbols from " << debug_file_path << " successfully"; } else if (result == ElfStatus::NO_SYMBOL_TABLE) { - if (path_ == "[vdso]") { + if (path == "[vdso]") { // Vdso only contains dynamic symbol table, and we can't change that. - return true; + return; } // Lacking symbol table isn't considered as an error but worth reporting. - LOG(symbol_warning_loglevel_) << filename << " doesn't contain symbol table"; - return true; + LOG(warning_loglevel) << debug_file_path << " doesn't contain symbol table"; } else { - LOG(symbol_warning_loglevel_) << "failed to read symbols from " << filename << ": " << result; - return false; + LOG(warning_loglevel) << "failed to read symbols from " << debug_file_path << ": " << result; + } +} + +static void SortAndFixSymbols(std::vector<Symbol>& symbols) { + std::sort(symbols.begin(), symbols.end(), Symbol::CompareValueByAddr); + Symbol* prev_symbol = nullptr; + for (auto& symbol : symbols) { + if (prev_symbol != nullptr && prev_symbol->len == 0) { + prev_symbol->len = symbol.addr - prev_symbol->addr; + } + prev_symbol = &symbol; } } -bool Dso::LoadKernel() { - BuildId build_id = GetExpectedBuildId(); - if (!vmlinux_.empty()) { - ElfStatus result = ParseSymbolsFromElfFile(vmlinux_, build_id, - std::bind(VmlinuxSymbolCallback, std::placeholders::_1, &symbols_)); - return CheckReadSymbolResult(result, vmlinux_); - } else if (!kallsyms_.empty()) { - ProcessKernelSymbols(kallsyms_, std::bind(&KernelSymbolCallback, - std::placeholders::_1, &symbols_)); - bool all_zero = true; - for (const auto& symbol : symbols_) { - if (symbol.addr != 0) { - all_zero = false; - break; +class ElfDso : public Dso { + public: + ElfDso(const std::string& path, const std::string& debug_file_path) + : Dso(DSO_ELF_FILE, path, debug_file_path), + min_vaddr_(std::numeric_limits<uint64_t>::max()) {} + + uint64_t MinVirtualAddress() override { + if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) { + min_vaddr_ = 0; + if (type_ == DSO_ELF_FILE) { + BuildId build_id = GetExpectedBuildId(); + + uint64_t addr; + ElfStatus result = ReadMinExecutableVirtualAddressFromElfFile( + GetDebugFilePath(), build_id, &addr); + if (result != ElfStatus::NO_ERROR) { + LOG(WARNING) << "failed to read min virtual address of " + << GetDebugFilePath() << ": " << result; + } else { + min_vaddr_ = addr; + } } } - if (all_zero) { - LOG(symbol_warning_loglevel_) - << "Symbol addresses in /proc/kallsyms on device are all zero. " - "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible."; - symbols_.clear(); - return false; + return min_vaddr_; + } + + void SetMinVirtualAddress(uint64_t min_vaddr) override { + min_vaddr_ = min_vaddr; + } + + protected: + std::vector<Symbol> LoadSymbols() override { + std::vector<Symbol> symbols; + BuildId build_id = GetExpectedBuildId(); + auto symbol_callback = [&](const ElfFileSymbol& symbol) { + if (symbol.is_func || (symbol.is_label && symbol.is_in_text_section)) { + symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len); + } + }; + ElfStatus status; + std::tuple<bool, std::string, std::string> tuple = SplitUrlInApk(debug_file_path_); + if (std::get<0>(tuple)) { + status = ParseSymbolsFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id, + symbol_callback); + } else { + status = ParseSymbolsFromElfFile(debug_file_path_, build_id, symbol_callback); } - } else if (read_kernel_symbols_from_proc_ || !build_id.IsEmpty()) { - // Try /proc/kallsyms only when asked to do so, or when build id matches. - // Otherwise, it is likely to use /proc/kallsyms on host for perf.data recorded on device. - if (!build_id.IsEmpty()) { - BuildId real_build_id; - if (!GetKernelBuildId(&real_build_id)) { - return false; + ReportReadElfSymbolResult(status, path_, debug_file_path_, + symbols_.empty() ? android::base::WARNING : android::base::DEBUG); + SortAndFixSymbols(symbols); + return symbols; + } + + private: + uint64_t min_vaddr_; +}; + +class KernelDso : public Dso { + public: + KernelDso(const std::string& path, const std::string& debug_file_path) + : Dso(DSO_KERNEL, path, debug_file_path) {} + + protected: + std::vector<Symbol> LoadSymbols() override { + std::vector<Symbol> symbols; + BuildId build_id = GetExpectedBuildId(); + if (!vmlinux_.empty()) { + auto symbol_callback = [&](const ElfFileSymbol& symbol) { + if (symbol.is_func) { + symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len); + } + }; + ElfStatus status = ParseSymbolsFromElfFile(vmlinux_, build_id, symbol_callback); + ReportReadElfSymbolResult(status, path_, vmlinux_); + } else if (!kallsyms_.empty()) { + symbols = ReadSymbolsFromKallsyms(kallsyms_); + } else if (read_kernel_symbols_from_proc_ || !build_id.IsEmpty()) { + // Try /proc/kallsyms only when asked to do so, or when build id matches. + // Otherwise, it is likely to use /proc/kallsyms on host for perf.data recorded on device. + bool can_read_kallsyms = true; + if (!build_id.IsEmpty()) { + BuildId real_build_id; + if (!GetKernelBuildId(&real_build_id) || build_id != real_build_id) { + LOG(DEBUG) << "failed to read symbols from /proc/kallsyms: Build id mismatch"; + can_read_kallsyms = false; + } } - bool match = (build_id == real_build_id); - if (!match) { - LOG(symbol_warning_loglevel_) << "failed to read symbols from /proc/kallsyms: Build id " - << "mismatch"; - return false; + if (can_read_kallsyms) { + std::string kallsyms; + if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) { + LOG(DEBUG) << "failed to read /proc/kallsyms"; + } else { + symbols = ReadSymbolsFromKallsyms(kallsyms); + } } } - - std::string kallsyms; - if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) { - LOG(DEBUG) << "failed to read /proc/kallsyms"; - return false; + SortAndFixSymbols(symbols); + if (!symbols.empty()) { + symbols.back().len = std::numeric_limits<uint64_t>::max() - symbols.back().addr; } - ProcessKernelSymbols(kallsyms, std::bind(&KernelSymbolCallback, - std::placeholders::_1, &symbols_)); - bool all_zero = true; - for (const auto& symbol : symbols_) { - if (symbol.addr != 0) { - all_zero = false; - break; + return symbols; + } + + private: + std::vector<Symbol> ReadSymbolsFromKallsyms(std::string& kallsyms) { + std::vector<Symbol> symbols; + auto symbol_callback = [&](const KernelSymbol& symbol) { + if (strchr("TtWw", symbol.type) && symbol.addr != 0u) { + symbols.emplace_back(symbol.name, symbol.addr, 0); } - } - if (all_zero) { - LOG(symbol_warning_loglevel_) << "Symbol addresses in /proc/kallsyms are all zero. " - "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible."; - symbols_.clear(); return false; + }; + ProcessKernelSymbols(kallsyms, symbol_callback); + if (symbols.empty()) { + LOG(WARNING) << "Symbol addresses in /proc/kallsyms on device are all zero. " + "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible."; } + return symbols; } - return true; -} - -static void ElfFileSymbolCallback(const ElfFileSymbol& elf_symbol, - bool (*filter)(const ElfFileSymbol&), - std::vector<Symbol>* symbols) { - if (filter(elf_symbol)) { - symbols->emplace_back(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len); +}; + +class KernelModuleDso : public Dso { + public: + KernelModuleDso(const std::string& path, const std::string& debug_file_path) + : Dso(DSO_KERNEL_MODULE, path, debug_file_path) {} + + protected: + std::vector<Symbol> LoadSymbols() override { + std::vector<Symbol> symbols; + BuildId build_id = GetExpectedBuildId(); + auto symbol_callback = [&](const ElfFileSymbol& symbol) { + if (symbol.is_func || symbol.is_in_text_section) { + symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len); + } + }; + ElfStatus status = ParseSymbolsFromElfFile(debug_file_path_, build_id, symbol_callback); + ReportReadElfSymbolResult(status, path_, debug_file_path_, + symbols_.empty() ? android::base::WARNING : android::base::DEBUG); + SortAndFixSymbols(symbols); + return symbols; } -} - -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); -} - -bool Dso::LoadKernelModule() { - BuildId build_id = GetExpectedBuildId(); - ElfStatus result = ParseSymbolsFromElfFile(GetDebugFilePath(), build_id, - std::bind(ElfFileSymbolCallback, std::placeholders::_1, - SymbolFilterForKernelModule, &symbols_)); - return CheckReadSymbolResult(result, GetDebugFilePath()); -} +}; -static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) { - return elf_symbol.is_func || - (elf_symbol.is_label && elf_symbol.is_in_text_section); -} - -bool Dso::LoadElfFile() { - BuildId build_id = GetExpectedBuildId(); - - if (symfs_dir_.empty()) { - // Linux host can store debug shared libraries in /usr/lib/debug. - ElfStatus result = ParseSymbolsFromElfFile( - "/usr/lib/debug" + path_, build_id, - std::bind(ElfFileSymbolCallback, std::placeholders::_1, - SymbolFilterForDso, &symbols_)); - if (result == ElfStatus::NO_ERROR) { - return CheckReadSymbolResult(result, "/usr/lib/debug" + path_); +std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path, + bool force_64bit) { + auto find_debug_file = [&]() { + // Check if file matching path_ exists in symfs directory before using it as + // debug_file_path_. + if (!symfs_dir_.empty()) { + std::string path_in_symfs = symfs_dir_ + dso_path; + std::tuple<bool, std::string, std::string> tuple = SplitUrlInApk(path_in_symfs); + std::string file_path = std::get<0>(tuple) ? std::get<1>(tuple) : path_in_symfs; + if (IsRegularFile(file_path)) { + return path_in_symfs; + } + } else if (dso_path == "[vdso]") { + if (force_64bit && !vdso_64bit_.empty()) { + return vdso_64bit_; + } else if (!force_64bit && !vdso_32bit_.empty()) { + return vdso_32bit_; + } + } else if (dso_type == DSO_ELF_FILE) { + // Linux host can store debug shared libraries in /usr/lib/debug. + std::string path = "/usr/lib/debug" + dso_path; + if (IsRegularFile(path)) { + return path; + } } - } - // TODO: load std::vector<Symbol> directly from ParseSymbolsFromElfFile - // instead of needing to call a callback function for each symbol. - ElfStatus result = ParseSymbolsFromElfFile( - GetDebugFilePath(), build_id, - std::bind(ElfFileSymbolCallback, std::placeholders::_1, - SymbolFilterForDso, &symbols_)); - return CheckReadSymbolResult(result, GetDebugFilePath()); -} + return dso_path; + }; -bool Dso::LoadEmbeddedElfFile() { - BuildId build_id = GetExpectedBuildId(); - auto tuple = SplitUrlInApk(GetDebugFilePath()); - CHECK(std::get<0>(tuple)); - ElfStatus result = ParseSymbolsFromApkFile( - std::get<1>(tuple), std::get<2>(tuple), build_id, - std::bind(ElfFileSymbolCallback, std::placeholders::_1, - SymbolFilterForDso, &symbols_)); - return CheckReadSymbolResult(result, GetDebugFilePath()); + switch (dso_type) { + case DSO_ELF_FILE: + return std::unique_ptr<Dso>(new ElfDso(dso_path, find_debug_file())); + case DSO_KERNEL: + return std::unique_ptr<Dso>(new KernelDso(dso_path, dso_path)); + case DSO_KERNEL_MODULE: + return std::unique_ptr<Dso>(new KernelModuleDso(dso_path, find_debug_file())); + case DSO_DEX_FILE: + return std::unique_ptr<Dso>(new DexFileDso(dso_path, find_debug_file())); + default: + LOG(FATAL) << "Unexpected dso_type " << static_cast<int>(dso_type); + } + return nullptr; } -void Dso::FixupSymbolLength() { - Symbol* prev_symbol = nullptr; - for (auto& symbol : symbols_) { - if (prev_symbol != nullptr && prev_symbol->len == 0) { - prev_symbol->len = symbol.addr - prev_symbol->addr; - } - prev_symbol = &symbol; +std::vector<Symbol> DexFileDso::LoadSymbols() { + std::vector<Symbol> symbols; + std::vector<DexFileSymbol> dex_file_symbols; + if (!ReadSymbolsFromDexFile(debug_file_path_, dex_file_offsets_, &dex_file_symbols)) { + android::base::LogSeverity level = symbols_.empty() ? android::base::WARNING + : android::base::DEBUG; + LOG(level) << "Failed to read symbols from " << debug_file_path_; + return symbols; } - if (prev_symbol != nullptr && prev_symbol->len == 0) { - prev_symbol->len = std::numeric_limits<uint64_t>::max() - prev_symbol->addr; + LOG(VERBOSE) << "Read symbols from " << debug_file_path_ << " successfully"; + for (auto& symbol : dex_file_symbols) { + symbols.emplace_back(symbol.name, symbol.offset, symbol.len); } + SortAndFixSymbols(symbols); + return symbols; } const char* DsoTypeToString(DsoType dso_type) { @@ -506,6 +468,8 @@ const char* DsoTypeToString(DsoType dso_type) { return "dso_kernel_module"; case DSO_ELF_FILE: return "dso_elf_file"; + case DSO_DEX_FILE: + return "dso_dex_file"; default: return "unknown"; } |