diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp index 14ae44564..10a4e4634 100644 --- a/libbacktrace/Android.bp +++ b/libbacktrace/Android.bp @@ -50,7 +50,6 @@ libbacktrace_sources = [ "BacktracePtrace.cpp", "thread_utils.c", "ThreadEntry.cpp", - "UnwindDexFile.cpp", "UnwindStack.cpp", "UnwindStackMap.cpp", ] @@ -110,10 +109,9 @@ cc_library { static_libs: ["libasync_safe"], }, vendor: { - cflags: ["-DNO_LIBDEXFILE"], - exclude_srcs: ["UnwindDexFile.cpp"], + cflags: ["-DNO_LIBDEXFILE_SUPPORT"], exclude_shared_libs: ["libdexfile"], - }, + } }, whole_static_libs: ["libdemangle"], } @@ -145,8 +143,6 @@ cc_test { "backtrace_test.cpp", "GetPss.cpp", "thread_utils.c", - - "unwind_dex_test.cpp", ], cflags: [ diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp index 158467ec2..7e2e6d05e 100644 --- a/libbacktrace/UnwindStack.cpp +++ b/libbacktrace/UnwindStack.cpp @@ -36,70 +36,15 @@ #include #include +#if !defined(NO_LIBDEXFILE_SUPPORT) +#include +#endif #include #include "BacktraceLog.h" -#ifndef NO_LIBDEXFILE -#include "UnwindDexFile.h" -#endif #include "UnwindStack.h" #include "UnwindStackMap.h" -static void FillInDexFrame(UnwindStackMap* stack_map, uint64_t dex_pc, - backtrace_frame_data_t* frame) { - // The DEX PC points into the .dex section within an ELF file. - // However, this is a BBS section manually mmaped to a .vdex file, - // so we need to get the following map to find the ELF data. - unwindstack::Maps* maps = stack_map->stack_maps(); - auto it = maps->begin(); - uint64_t rel_dex_pc; - unwindstack::MapInfo* info; - for (; it != maps->end(); ++it) { - auto entry = *it; - if (dex_pc >= entry->start && dex_pc < entry->end) { - info = entry; - rel_dex_pc = dex_pc - entry->start; - frame->map.start = entry->start; - frame->map.end = entry->end; - frame->map.offset = entry->offset; - frame->map.load_bias = entry->load_bias; - frame->map.flags = entry->flags; - frame->map.name = entry->name; - frame->rel_pc = rel_dex_pc; - break; - } - } - if (it == maps->end() || ++it == maps->end()) { - return; - } - - auto entry = *it; - auto process_memory = stack_map->process_memory(); - unwindstack::Elf* elf = entry->GetElf(process_memory, true); - if (!elf->valid()) { - return; - } - - // Adjust the relative dex by the offset. - rel_dex_pc += entry->elf_offset; - - uint64_t dex_offset; - if (!elf->GetFunctionName(rel_dex_pc, &frame->func_name, &dex_offset)) { - return; - } - frame->func_offset = dex_offset; - if (frame->func_name != "$dexfile") { - return; - } - -#ifndef NO_LIBDEXFILE - UnwindDexFile* dex_file = stack_map->GetDexFile(dex_pc - dex_offset, info); - if (dex_file != nullptr) { - dex_file->GetMethodInformation(dex_offset, &frame->func_name, &frame->func_offset); - } -#endif -} - bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map, std::vector* frames, size_t num_ignore_frames, std::vector* skip_names, BacktraceUnwindError* error) { @@ -110,6 +55,11 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map, if (stack_map->GetJitDebug() != nullptr) { unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch()); } +#if !defined(NO_LIBDEXFILE_SUPPORT) + if (stack_map->GetDexFiles() != nullptr) { + unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch()); + } +#endif unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore()); if (error != nullptr) { switch (unwinder.LastErrorCode()) { @@ -150,36 +100,11 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map, } auto unwinder_frames = unwinder.frames(); - // Get the real number of frames we'll need. - size_t total_frames = 0; - for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, total_frames++) { - if (unwinder_frames[i].dex_pc != 0) { - total_frames++; - } - } - frames->resize(total_frames); + frames->resize(unwinder.NumFrames() - num_ignore_frames); size_t cur_frame = 0; for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++) { auto frame = &unwinder_frames[i]; - // Inject extra 'virtual' frame that represents the dex pc data. - // The dex pc is magic register defined in the Mterp interpreter, - // and thus it will be restored/observed in the frame after it. - // Adding the dex frame first here will create something like: - // #7 pc 006b1ba1 libartd.so ExecuteMterpImpl+14625 - // #8 pc 0015fa20 core.vdex java.util.Arrays.binarySearch+8 - // #9 pc 0039a1ef libartd.so art::interpreter::Execute+719 - if (frame->dex_pc != 0) { - backtrace_frame_data_t* dex_frame = &frames->at(cur_frame); - dex_frame->num = cur_frame++; - dex_frame->pc = frame->dex_pc; - dex_frame->rel_pc = frame->dex_pc; - dex_frame->sp = frame->sp; - dex_frame->stack_size = 0; - dex_frame->func_offset = 0; - FillInDexFrame(stack_map, frame->dex_pc, dex_frame); - } - backtrace_frame_data_t* back_frame = &frames->at(cur_frame); back_frame->num = cur_frame++; diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp index 97f8d782d..1622e30d4 100644 --- a/libbacktrace/UnwindStackMap.cpp +++ b/libbacktrace/UnwindStackMap.cpp @@ -26,20 +26,11 @@ #include #include -#include "UnwindDexFile.h" #include "UnwindStackMap.h" //------------------------------------------------------------------------- UnwindStackMap::UnwindStackMap(pid_t pid) : BacktraceMap(pid) {} -UnwindStackMap::~UnwindStackMap() { -#ifndef NO_LIBDEXFILE - for (auto& entry : dex_files_) { - delete entry.second; - } -#endif -} - bool UnwindStackMap::Build() { if (pid_ == 0) { pid_ = getpid(); @@ -54,6 +45,9 @@ bool UnwindStackMap::Build() { // Create a JitDebug object for getting jit unwind information. std::vector search_libs_{"libart.so", "libartd.so"}; jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_)); +#if !defined(NO_LIBDEXFILE_SUPPORT) + dex_files_.reset(new unwindstack::DexFiles(process_memory_)); +#endif if (!stack_maps_->Parse()) { return false; @@ -127,26 +121,6 @@ std::shared_ptr UnwindStackMap::GetProcessMemory() { return process_memory_; } -#ifdef NO_LIBDEXFILE -UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t, unwindstack::MapInfo*) { - return nullptr; -} -#else -UnwindDexFile* UnwindStackMap::GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info) { - // Lock while we get the data. - std::lock_guard guard(dex_lock_); - UnwindDexFile* dex_file; - auto entry = dex_files_.find(dex_file_offset); - if (entry == dex_files_.end()) { - dex_file = UnwindDexFile::Create(dex_file_offset, process_memory_.get(), info); - dex_files_[dex_file_offset] = dex_file; - } else { - dex_file = entry->second; - } - return dex_file; -} -#endif - UnwindStackOfflineMap::UnwindStackOfflineMap(pid_t pid) : UnwindStackMap(pid) {} bool UnwindStackOfflineMap::Build() { diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h index be5c59e74..94cbfb2a0 100644 --- a/libbacktrace/UnwindStackMap.h +++ b/libbacktrace/UnwindStackMap.h @@ -27,6 +27,9 @@ #include #include +#if !defined(NO_LIBDEXFILE_SUPPORT) +#include +#endif #include #include @@ -36,7 +39,7 @@ class UnwindDexFile; class UnwindStackMap : public BacktraceMap { public: explicit UnwindStackMap(pid_t pid); - ~UnwindStackMap(); + ~UnwindStackMap() = default; bool Build() override; @@ -51,7 +54,9 @@ class UnwindStackMap : public BacktraceMap { unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); } - UnwindDexFile* GetDexFile(uint64_t dex_file_offset, unwindstack::MapInfo* info); +#if !defined(NO_LIBDEXFILE_SUPPORT) + unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); } +#endif protected: uint64_t GetLoadBias(size_t index) override; @@ -59,9 +64,8 @@ class UnwindStackMap : public BacktraceMap { std::unique_ptr stack_maps_; std::shared_ptr process_memory_; std::unique_ptr jit_debug_; -#ifndef NO_LIBDEXFILE - std::mutex dex_lock_; - std::unordered_map dex_files_; +#if !defined(NO_LIBDEXFILE_SUPPORT) + std::unique_ptr dex_files_; #endif }; diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 28d7e64c8..74dfaa500 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -80,7 +80,13 @@ cc_library { host: { cflags: ["-O0", "-g"], }, + vendor: { + cflags: ["-DNO_LIBDEXFILE_SUPPORT"], + exclude_static_libs: ["libunwindstack_dex"], + exclude_shared_libs: ["libdexfile"], + }, }, + whole_static_libs: ["libunwindstack_dex"], arch: { x86: { @@ -99,14 +105,70 @@ cc_library { shared_libs: [ "libbase", + "libdexfile", "liblog", "liblzma", ], } +// Isolate the dex file processing into a separate library. Currently, +// it is necessary to add art include directories directly, which also +// adds the art elf.h file in the include path, overriding the system one. +// Work to isolate libdexfile is b/72216369. +cc_library_static { + name: "libunwindstack_dex", + vendor_available: false, + defaults: ["libunwindstack_flags"], + + cflags: [ + "-Wexit-time-destructors", + ], + + srcs: [ + "DexFile.cpp", + "DexFiles.cpp", + ], + + shared_libs: [ + "libbase", + "libdexfile", + ], + local_include_dirs: ["include"], + allow_undefined_symbols: true, + + // libdexfile will eventually properly export headers, for now include + // these directly. + include_dirs: [ + "art/runtime", + ], +} + //------------------------------------------------------------------------- // Unit Tests //------------------------------------------------------------------------- +cc_test_library { + name: "libunwindstack_dex_test", + vendor_available: false, + defaults: ["libunwindstack_flags"], + + srcs: [ + "tests/DexFileTest.cpp", + ], + local_include_dirs: ["include"], + allow_undefined_symbols: true, + + shared_libs: [ + "libbase", + "libunwindstack", + ], + + // libdexfile will eventually properly export headers, for now include + // these directly. + include_dirs: [ + "art/runtime", + ], +} + cc_test { name: "libunwindstack_test", defaults: ["libunwindstack_flags"], @@ -168,6 +230,8 @@ cc_test { "libgmock", ], + whole_static_libs: ["libunwindstack_dex_test"], + data: [ "tests/files/elf32.xz", "tests/files/elf64.xz", diff --git a/libbacktrace/UnwindDexFile.cpp b/libunwindstack/DexFile.cpp similarity index 85% rename from libbacktrace/UnwindDexFile.cpp rename to libunwindstack/DexFile.cpp index 5780fbb9c..be6c2f71e 100644 --- a/libbacktrace/UnwindDexFile.cpp +++ b/libunwindstack/DexFile.cpp @@ -33,26 +33,27 @@ #include #include -#include "UnwindDexFile.h" +#include "DexFile.h" -UnwindDexFile* UnwindDexFile::Create(uint64_t dex_file_offset_in_memory, - unwindstack::Memory* memory, unwindstack::MapInfo* info) { +namespace unwindstack { + +DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info) { if (!info->name.empty()) { - std::unique_ptr dex_file(new UnwindDexFileFromFile); + std::unique_ptr dex_file(new DexFileFromFile); if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) { return dex_file.release(); } } - std::unique_ptr dex_file(new UnwindDexFileFromMemory); + std::unique_ptr dex_file(new DexFileFromMemory); if (dex_file->Open(dex_file_offset_in_memory, memory)) { return dex_file.release(); } return nullptr; } -void UnwindDexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name, - uint64_t* method_offset) { +void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name, + uint64_t* method_offset) { if (dex_file_ == nullptr) { return; } @@ -87,13 +88,13 @@ void UnwindDexFile::GetMethodInformation(uint64_t dex_offset, std::string* metho } } -UnwindDexFileFromFile::~UnwindDexFileFromFile() { +DexFileFromFile::~DexFileFromFile() { if (size_ != 0) { munmap(mapped_memory_, size_); } } -bool UnwindDexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) { +bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC))); if (fd == -1) { return false; @@ -137,7 +138,7 @@ bool UnwindDexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::st return dex_file_ != nullptr; } -bool UnwindDexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory) { +bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) { art::DexFile::Header header; if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) { return false; @@ -160,3 +161,5 @@ bool UnwindDexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, unwindsta dex_file_.reset(dex.release()); return dex_file_ != nullptr; } + +} // namespace unwindstack diff --git a/libbacktrace/UnwindDexFile.h b/libunwindstack/DexFile.h similarity index 61% rename from libbacktrace/UnwindDexFile.h rename to libunwindstack/DexFile.h index dd70abafd..22e98df9a 100644 --- a/libbacktrace/UnwindDexFile.h +++ b/libunwindstack/DexFile.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _LIBBACKTRACE_UNWIND_DEX_FILE_H -#define _LIBBACKTRACE_UNWIND_DEX_FILE_H +#ifndef _LIBUNWINDSTACK_DEX_FILE_H +#define _LIBUNWINDSTACK_DEX_FILE_H #include @@ -26,28 +26,24 @@ #include namespace unwindstack { -class Memory; -struct MapInfo; -} // namespace unwindstack -class UnwindDexFile { +class DexFile { public: - UnwindDexFile() = default; - virtual ~UnwindDexFile() = default; + DexFile() = default; + virtual ~DexFile() = default; void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset); - static UnwindDexFile* Create(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory, - unwindstack::MapInfo* info); + static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info); protected: std::unique_ptr dex_file_; }; -class UnwindDexFileFromFile : public UnwindDexFile { +class DexFileFromFile : public DexFile { public: - UnwindDexFileFromFile() = default; - virtual ~UnwindDexFileFromFile(); + DexFileFromFile() = default; + virtual ~DexFileFromFile(); bool Open(uint64_t dex_file_offset_in_file, const std::string& name); @@ -56,15 +52,17 @@ class UnwindDexFileFromFile : public UnwindDexFile { size_t size_ = 0; }; -class UnwindDexFileFromMemory : public UnwindDexFile { +class DexFileFromMemory : public DexFile { public: - UnwindDexFileFromMemory() = default; - virtual ~UnwindDexFileFromMemory() = default; + DexFileFromMemory() = default; + virtual ~DexFileFromMemory() = default; - bool Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory); + bool Open(uint64_t dex_file_offset_in_memory, Memory* memory); private: std::vector memory_; }; -#endif // _LIBBACKTRACE_UNWIND_DEX_FILE_H +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_DEX_FILE_H diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp new file mode 100644 index 000000000..3d67a6a5b --- /dev/null +++ b/libunwindstack/DexFiles.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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 +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "DexFile.h" + +namespace unwindstack { + +DexFiles::DexFiles(std::shared_ptr& memory) : memory_(memory) {} + +DexFiles::~DexFiles() { + for (auto& entry : files_) { + delete entry.second; + } +} + +DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) { + // Lock while processing the data. + std::lock_guard guard(files_lock_); + DexFile* dex_file; + auto entry = files_.find(dex_file_offset); + if (entry == files_.end()) { + dex_file = DexFile::Create(dex_file_offset, memory_.get(), info); + files_[dex_file_offset] = dex_file; + } else { + dex_file = entry->second; + } + return dex_file; +} + +void DexFiles::GetMethodInformation(uint64_t dex_offset, MapInfo* info, std::string* method_name, + uint64_t* method_offset) { + DexFile* dex_file = GetDexFile(dex_offset, info); + if (dex_file != nullptr) { + dex_file->GetMethodInformation(dex_offset, method_name, method_offset); + } +} + +void DexFiles::SetArch(ArchEnum) {} + +} // namespace unwindstack diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index f70ed7b2a..d22e1e88a 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -31,8 +31,74 @@ #include #include +#if !defined(NO_LIBDEXFILE_SUPPORT) +#include +#endif + namespace unwindstack { +// Inject extra 'virtual' frame that represents the dex pc data. +// The dex pc is a magic register defined in the Mterp interpreter, +// and thus it will be restored/observed in the frame after it. +// Adding the dex frame first here will create something like: +// #7 pc 0015fa20 core.vdex java.util.Arrays.binarySearch+8 +// #8 pc 006b1ba1 libartd.so ExecuteMterpImpl+14625 +// #9 pc 0039a1ef libartd.so art::interpreter::Execute+719 +void Unwinder::FillInDexFrame() { + size_t frame_num = frames_.size(); + frames_.resize(frame_num + 1); + FrameData* frame = &frames_.at(frame_num); + + uint64_t dex_pc = regs_->dex_pc(); + frame->pc = dex_pc; + frame->sp = regs_->sp(); + + auto it = maps_->begin(); + uint64_t rel_dex_pc; + MapInfo* info; + for (; it != maps_->end(); ++it) { + auto entry = *it; + if (dex_pc >= entry->start && dex_pc < entry->end) { + info = entry; + rel_dex_pc = dex_pc - entry->start; + frame->map_start = entry->start; + frame->map_end = entry->end; + frame->map_offset = entry->offset; + frame->map_load_bias = entry->load_bias; + frame->map_flags = entry->flags; + frame->map_name = entry->name; + frame->rel_pc = rel_dex_pc; + break; + } + } + + if (it == maps_->end() || ++it == maps_->end()) { + return; + } + + auto entry = *it; + unwindstack::Elf* elf = entry->GetElf(process_memory_, true); + if (!elf->valid()) { + return; + } + + // Adjust the relative dex by the offset. + rel_dex_pc += entry->elf_offset; + + uint64_t dex_offset; + if (!elf->GetFunctionName(rel_dex_pc, &frame->function_name, &dex_offset)) { + return; + } + frame->function_offset = dex_offset; + if (frame->function_name != "$dexfile") { + return; + } + +#if !defined(NO_LIBDEXFILE_SUPPORT) + dex_files_->GetMethodInformation(dex_offset, info, &frame->function_name, &frame->function_offset); +#endif +} + void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) { size_t frame_num = frames_.size(); frames_.resize(frame_num + 1); @@ -40,7 +106,6 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc frame->num = frame_num; frame->sp = regs_->sp(); frame->rel_pc = adjusted_rel_pc; - frame->dex_pc = regs_->dex_pc(); if (map_info == nullptr) { frame->pc = regs_->pc(); @@ -128,6 +193,11 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, if (map_info == nullptr || initial_map_names_to_skip == nullptr || std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(), basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) { + if (regs_->dex_pc() != 0) { + // Add a frame to represent the dex file. + FillInDexFrame(); + } + FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc); // Once a frame is added, stop skipping frames. @@ -240,4 +310,11 @@ void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) { jit_debug_ = jit_debug; } +#if !defined(NO_LIBDEXFILE_SUPPORT) +void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) { + dex_files->SetArch(arch); + dex_files_ = dex_files; +} +#endif + } // namespace unwindstack diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h new file mode 100644 index 000000000..d80e9b73a --- /dev/null +++ b/libunwindstack/include/unwindstack/DexFiles.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 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 _LIBUNWINDSTACK_DEX_FILES_H +#define _LIBUNWINDSTACK_DEX_FILES_H + +#include + +#include +#include +#include +#include + +namespace unwindstack { + +// Forward declarations. +class DexFile; +class Maps; +struct MapInfo; +class Memory; +enum ArchEnum : uint8_t; + +class DexFiles { + public: + explicit DexFiles(std::shared_ptr& memory); + ~DexFiles(); + + DexFile* GetDexFile(uint64_t dex_offset, MapInfo* info); + + void GetMethodInformation(uint64_t dex_offset, MapInfo* info, std::string* method_name, + uint64_t* method_offset); + + void SetArch(ArchEnum arch); + + private: + std::shared_ptr memory_; + std::mutex files_lock_; + std::unordered_map files_; +}; + +} // namespace unwindstack + +#endif // _LIBUNWINDSTACK_DEX_FILES_H diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index a7b57e6a7..e8af8b437 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -32,6 +32,7 @@ namespace unwindstack { // Forward declarations. +class DexFiles; class Elf; class JitDebug; enum ArchEnum : uint8_t; @@ -42,7 +43,6 @@ struct FrameData { uint64_t rel_pc; uint64_t pc; uint64_t sp; - uint64_t dex_pc; std::string function_name; uint64_t function_offset; @@ -75,10 +75,15 @@ class Unwinder { void SetJitDebug(JitDebug* jit_debug, ArchEnum arch); +#if !defined(NO_LIBDEXFILE_SUPPORT) + void SetDexFiles(DexFiles* dex_files, ArchEnum arch); +#endif + ErrorCode LastErrorCode() { return last_error_.code; } uint64_t LastErrorAddress() { return last_error_.address; } private: + void FillInDexFrame(); void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc); size_t max_frames_; @@ -87,6 +92,9 @@ class Unwinder { std::vector frames_; std::shared_ptr process_memory_; JitDebug* jit_debug_ = nullptr; +#if !defined(NO_LIBDEXFILE_SUPPORT) + DexFiles* dex_files_ = nullptr; +#endif ErrorData last_error_; }; diff --git a/libbacktrace/unwind_dex_test.cpp b/libunwindstack/tests/DexFileTest.cpp similarity index 66% rename from libbacktrace/unwind_dex_test.cpp rename to libunwindstack/tests/DexFileTest.cpp index 449e66296..d1338cba9 100644 --- a/libbacktrace/unwind_dex_test.cpp +++ b/libunwindstack/tests/DexFileTest.cpp @@ -30,46 +30,11 @@ #include -#include "UnwindDexFile.h" +#include "DexFile.h" -class MemoryFake : public unwindstack::Memory { - public: - MemoryFake() = default; - virtual ~MemoryFake() = default; +#include "MemoryFake.h" - size_t Read(uint64_t addr, void* buffer, size_t size) override; - - void SetMemory(uint64_t addr, const void* memory, size_t length); - - void Clear() { data_.clear(); } - - private: - std::unordered_map data_; -}; - -void MemoryFake::SetMemory(uint64_t addr, const void* memory, size_t length) { - const uint8_t* src = reinterpret_cast(memory); - for (size_t i = 0; i < length; i++, addr++) { - auto value = data_.find(addr); - if (value != data_.end()) { - value->second = src[i]; - } else { - data_.insert({addr, src[i]}); - } - } -} - -size_t MemoryFake::Read(uint64_t addr, void* memory, size_t size) { - uint8_t* dst = reinterpret_cast(memory); - for (size_t i = 0; i < size; i++, addr++) { - auto value = data_.find(addr); - if (value == data_.end()) { - return i; - } - dst[i] = value->second; - } - return size; -} +namespace unwindstack { // Borrowed from art/dex/dex_file_test.cc. static constexpr uint32_t kDexData[] = { @@ -92,12 +57,12 @@ static constexpr uint32_t kDexData[] = { 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c, }; -TEST(UnwindDexTest, from_file_open_non_exist) { - UnwindDexFileFromFile dex_file; +TEST(DexFileTest, from_file_open_non_exist) { + DexFileFromFile dex_file; ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist")); } -TEST(UnwindDexTest, from_file_open_too_small) { +TEST(DexFileTest, from_file_open_too_small) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); @@ -106,7 +71,7 @@ TEST(UnwindDexTest, from_file_open_too_small) { TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header)) - 2))); // Header too small. - UnwindDexFileFromFile dex_file; + DexFileFromFile dex_file; ASSERT_FALSE(dex_file.Open(0, tf.path)); // Header correct, file too small. @@ -116,18 +81,18 @@ TEST(UnwindDexTest, from_file_open_too_small) { ASSERT_FALSE(dex_file.Open(0, tf.path)); } -TEST(UnwindDexTest, from_file_open) { +TEST(DexFileTest, from_file_open) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); ASSERT_EQ(sizeof(kDexData), static_cast(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData))))); - UnwindDexFileFromFile dex_file; + DexFileFromFile dex_file; ASSERT_TRUE(dex_file.Open(0, tf.path)); } -TEST(UnwindDexTest, from_file_open_non_zero_offset) { +TEST(DexFileTest, from_file_open_non_zero_offset) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); @@ -135,38 +100,38 @@ TEST(UnwindDexTest, from_file_open_non_zero_offset) { ASSERT_EQ(sizeof(kDexData), static_cast(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData))))); - UnwindDexFileFromFile dex_file; + DexFileFromFile dex_file; ASSERT_TRUE(dex_file.Open(0x100, tf.path)); } -TEST(UnwindDexTest, from_memory_fail_too_small_for_header) { +TEST(DexFileTest, from_memory_fail_too_small_for_header) { MemoryFake memory; memory.SetMemory(0x1000, kDexData, sizeof(art::DexFile::Header) - 1); - UnwindDexFileFromMemory dex_file; + DexFileFromMemory dex_file; ASSERT_FALSE(dex_file.Open(0x1000, &memory)); } -TEST(UnwindDexTest, from_memory_fail_too_small_for_data) { +TEST(DexFileTest, from_memory_fail_too_small_for_data) { MemoryFake memory; memory.SetMemory(0x1000, kDexData, sizeof(kDexData) - 2); - UnwindDexFileFromMemory dex_file; + DexFileFromMemory dex_file; ASSERT_FALSE(dex_file.Open(0x1000, &memory)); } -TEST(UnwindDexTest, from_memory_open) { +TEST(DexFileTest, from_memory_open) { MemoryFake memory; memory.SetMemory(0x1000, kDexData, sizeof(kDexData)); - UnwindDexFileFromMemory dex_file; + DexFileFromMemory dex_file; ASSERT_TRUE(dex_file.Open(0x1000, &memory)); } -TEST(UnwindDexTest, create_using_file) { +TEST(DexFileTest, create_using_file) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); @@ -175,12 +140,12 @@ TEST(UnwindDexTest, create_using_file) { static_cast(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData))))); MemoryFake memory; - unwindstack::MapInfo info(0, 0x10000, 0, 0x5, tf.path); - std::unique_ptr dex_file(UnwindDexFile::Create(0x500, &memory, &info)); + MapInfo info(0, 0x10000, 0, 0x5, tf.path); + std::unique_ptr dex_file(DexFile::Create(0x500, &memory, &info)); ASSERT_TRUE(dex_file != nullptr); } -TEST(UnwindDexTest, create_using_file_non_zero_start) { +TEST(DexFileTest, create_using_file_non_zero_start) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); @@ -189,12 +154,12 @@ TEST(UnwindDexTest, create_using_file_non_zero_start) { static_cast(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData))))); MemoryFake memory; - unwindstack::MapInfo info(0x100, 0x10000, 0, 0x5, tf.path); - std::unique_ptr dex_file(UnwindDexFile::Create(0x600, &memory, &info)); + MapInfo info(0x100, 0x10000, 0, 0x5, tf.path); + std::unique_ptr dex_file(DexFile::Create(0x600, &memory, &info)); ASSERT_TRUE(dex_file != nullptr); } -TEST(UnwindDexTest, create_using_file_non_zero_offset) { +TEST(DexFileTest, create_using_file_non_zero_offset) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); @@ -203,28 +168,28 @@ TEST(UnwindDexTest, create_using_file_non_zero_offset) { static_cast(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData))))); MemoryFake memory; - unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path); - std::unique_ptr dex_file(UnwindDexFile::Create(0x400, &memory, &info)); + MapInfo info(0x100, 0x10000, 0x200, 0x5, tf.path); + std::unique_ptr dex_file(DexFile::Create(0x400, &memory, &info)); ASSERT_TRUE(dex_file != nullptr); } -TEST(UnwindDexTest, create_using_memory_empty_file) { +TEST(DexFileTest, create_using_memory_empty_file) { MemoryFake memory; memory.SetMemory(0x4000, kDexData, sizeof(kDexData)); - unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, ""); - std::unique_ptr dex_file(UnwindDexFile::Create(0x4000, &memory, &info)); + MapInfo info(0x100, 0x10000, 0x200, 0x5, ""); + std::unique_ptr dex_file(DexFile::Create(0x4000, &memory, &info)); ASSERT_TRUE(dex_file != nullptr); } -TEST(UnwindDexTest, create_using_memory_file_does_not_exist) { +TEST(DexFileTest, create_using_memory_file_does_not_exist) { MemoryFake memory; memory.SetMemory(0x4000, kDexData, sizeof(kDexData)); - unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist"); - std::unique_ptr dex_file(UnwindDexFile::Create(0x4000, &memory, &info)); + MapInfo info(0x100, 0x10000, 0x200, 0x5, "/does/not/exist"); + std::unique_ptr dex_file(DexFile::Create(0x4000, &memory, &info)); ASSERT_TRUE(dex_file != nullptr); } -TEST(UnwindDexTest, create_using_memory_file_is_malformed) { +TEST(DexFileTest, create_using_memory_file_is_malformed) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); @@ -233,30 +198,30 @@ TEST(UnwindDexTest, create_using_memory_file_is_malformed) { MemoryFake memory; memory.SetMemory(0x4000, kDexData, sizeof(kDexData)); - unwindstack::MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist"); - std::unique_ptr dex_file(UnwindDexFile::Create(0x4000, &memory, &info)); + MapInfo info(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist"); + std::unique_ptr dex_file(DexFile::Create(0x4000, &memory, &info)); ASSERT_TRUE(dex_file != nullptr); // Check it came from memory by clearing memory and verifying it fails. memory.Clear(); - dex_file.reset(UnwindDexFile::Create(0x4000, &memory, &info)); + dex_file.reset(DexFile::Create(0x4000, &memory, &info)); ASSERT_TRUE(dex_file == nullptr); } -TEST(UnwindDexTest, get_method_not_opened) { +TEST(DexFileTest, get_method_not_opened) { std::string method("something"); uint64_t method_offset = 100; - UnwindDexFile dex_file; + DexFile dex_file; dex_file.GetMethodInformation(0x100, &method, &method_offset); EXPECT_EQ("something", method); EXPECT_EQ(100U, method_offset); } -TEST(UnwindDexTest, get_method) { +TEST(DexFileTest, get_method) { MemoryFake memory; memory.SetMemory(0x4000, kDexData, sizeof(kDexData)); - unwindstack::MapInfo info(0x100, 0x10000, 0x200, 0x5, ""); - std::unique_ptr dex_file(UnwindDexFile::Create(0x4000, &memory, &info)); + MapInfo info(0x100, 0x10000, 0x200, 0x5, ""); + std::unique_ptr dex_file(DexFile::Create(0x4000, &memory, &info)); ASSERT_TRUE(dex_file != nullptr); std::string method; @@ -271,3 +236,5 @@ TEST(UnwindDexTest, get_method) { EXPECT_EQ("not_in_a_method", method); EXPECT_EQ(0x123U, method_offset); } + +} // namespace unwindstack diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp index 33e55274d..07e48af60 100644 --- a/libunwindstack/tools/unwind.cpp +++ b/libunwindstack/tools/unwind.cpp @@ -96,13 +96,8 @@ void DoUnwind(pid_t pid) { unwinder.Unwind(); // Print the frames. - const std::vector& frames = unwinder.frames(); for (size_t i = 0; i < unwinder.NumFrames(); i++) { printf("%s\n", unwinder.FormatFrame(i).c_str()); - const unwindstack::FrameData* frame = &frames[i]; - if (frame->dex_pc != 0) { - printf(" dex pc %" PRIx64 "\n", frame->dex_pc); - } } }