diff options
Diffstat (limited to 'libunwindstack/AndroidUnwinder.cpp')
-rw-r--r-- | libunwindstack/AndroidUnwinder.cpp | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/libunwindstack/AndroidUnwinder.cpp b/libunwindstack/AndroidUnwinder.cpp new file mode 100644 index 0000000..24e991e --- /dev/null +++ b/libunwindstack/AndroidUnwinder.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2022 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 <inttypes.h> +#include <sys/types.h> +#include <unistd.h> + +#include <memory> +#include <mutex> +#include <string> +#include <vector> + +#include <android-base/stringprintf.h> +#include <android-base/threads.h> + +#include <unwindstack/AndroidUnwinder.h> +#include <unwindstack/Arch.h> +#include <unwindstack/DexFiles.h> +#include <unwindstack/Error.h> +#include <unwindstack/JitDebug.h> +#include <unwindstack/Maps.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> +#include <unwindstack/RegsGetLocal.h> +#include <unwindstack/Unwinder.h> + +#if defined(__BIONIC__) +#include <bionic/reserved_signals.h> +static constexpr int kThreadUnwindSignal = BIONIC_SIGNAL_BACKTRACE; +#else +#include <signal.h> +static int kThreadUnwindSignal = SIGRTMIN; +#endif + +// Use the demangler from libc++. +extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status); + +namespace unwindstack { + +void AndroidUnwinderData::DemangleFunctionNames() { + for (auto& frame : frames) { + char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr); + if (demangled_name != nullptr) { + frame.function_name = demangled_name; + free(demangled_name); + } + } +} + +std::string AndroidUnwinderData::GetErrorString() { + std::string error_msg(GetErrorCodeString(error.code)); + if (error.address != 0) { + error_msg += android::base::StringPrintf(" at address 0x%" PRIx64, error.address); + } + return error_msg; +} + +AndroidUnwinder* AndroidUnwinder::Create(pid_t pid) { + if (pid == getpid()) { + return new AndroidLocalUnwinder; + } else { + return new AndroidRemoteUnwinder(pid); + } +} + +bool AndroidUnwinder::Initialize(ErrorData& error) { + // Android stores the jit and dex file location only in the library + // libart.so or libartd.so. + static std::vector<std::string> search_libs [[clang::no_destroy]] = {"libart.so", "libartd.so"}; + + bool initialize = true; + std::call_once(initialize_, [this, &initialize, &error]() { + initialize = InternalInitialize(error); + if (!initialize) { + return; + } + + jit_debug_ = CreateJitDebug(arch_, process_memory_, search_libs); + +#if defined(DEXFILE_SUPPORT) + dex_files_ = CreateDexFiles(arch_, process_memory_, search_libs); +#endif + }); + + return initialize; +} + +std::string AndroidUnwinder::FormatFrame(const FrameData& frame) const { + if (arch_ == ARCH_UNKNOWN) { + return ""; + } + return Unwinder::FormatFrame(arch_, frame); +} + +bool AndroidLocalUnwinder::InternalInitialize(ErrorData& error) { + arch_ = Regs::CurrentArch(); + + maps_.reset(new LocalUpdatableMaps); + if (!maps_->Parse()) { + error.code = ERROR_MAPS_PARSE; + return false; + } + + if (process_memory_ == nullptr) { + process_memory_ = Memory::CreateProcessMemoryThreadCached(getpid()); + } + + return true; +} + +FrameData AndroidUnwinder::BuildFrameFromPcOnly(uint64_t pc) { + return Unwinder::BuildFrameFromPcOnly(pc, arch_, maps_.get(), jit_debug_.get(), process_memory_, + true); +} + +bool AndroidUnwinder::Unwind(AndroidUnwinderData& data) { + return Unwind(std::nullopt, data); +} + +bool AndroidUnwinder::Unwind(std::optional<pid_t> tid, AndroidUnwinderData& data) { + if (!Initialize(data.error)) { + return false; + } + + return InternalUnwind(tid, data); +} + +bool AndroidUnwinder::Unwind(void* ucontext, AndroidUnwinderData& data) { + if (ucontext == nullptr) { + data.error.code = ERROR_INVALID_PARAMETER; + return false; + } + std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(arch_, ucontext)); + return Unwind(regs.get(), data); +} + +bool AndroidUnwinder::Unwind(Regs* initial_regs, AndroidUnwinderData& data) { + if (initial_regs == nullptr) { + data.error.code = ERROR_INVALID_PARAMETER; + return false; + } + + if (!Initialize(data.error)) { + return false; + } + + if (arch_ != initial_regs->Arch()) { + data.error.code = ERROR_BAD_ARCH; + return false; + } + + std::unique_ptr<Regs> regs(initial_regs->Clone()); + if (data.saved_initial_regs) { + (*data.saved_initial_regs).reset(initial_regs->Clone()); + } + Unwinder unwinder(data.max_frames.value_or(max_frames_), maps_.get(), regs.get(), + process_memory_); + unwinder.SetJitDebug(jit_debug_.get()); + unwinder.SetDexFiles(dex_files_.get()); + unwinder.Unwind(data.show_all_frames ? nullptr : &initial_map_names_to_skip_, + &map_suffixes_to_ignore_); + data.frames = unwinder.ConsumeFrames(); + data.error = unwinder.LastError(); + return data.frames.size() != 0; +} + +bool AndroidLocalUnwinder::InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) { + if (!tid) { + *tid = android::base::GetThreadId(); + } + + if (static_cast<uint64_t>(*tid) == android::base::GetThreadId()) { + // Unwind current thread. + std::unique_ptr<Regs> regs(Regs::CreateFromLocal()); + RegsGetLocal(regs.get()); + return AndroidUnwinder::Unwind(regs.get(), data); + } + + ThreadUnwinder unwinder(data.max_frames.value_or(max_frames_), maps_.get(), process_memory_); + unwinder.SetJitDebug(jit_debug_.get()); + unwinder.SetDexFiles(dex_files_.get()); + std::unique_ptr<Regs>* initial_regs = nullptr; + if (data.saved_initial_regs) { + initial_regs = &data.saved_initial_regs.value(); + } + unwinder.UnwindWithSignal(kThreadUnwindSignal, *tid, initial_regs, + data.show_all_frames ? nullptr : &initial_map_names_to_skip_, + &map_suffixes_to_ignore_); + data.frames = unwinder.ConsumeFrames(); + data.error = unwinder.LastError(); + return data.frames.size() != 0; +} + +bool AndroidRemoteUnwinder::InternalInitialize(ErrorData& error) { + if (arch_ == ARCH_UNKNOWN) { + arch_ = Regs::RemoteGetArch(pid_); + } + if (arch_ == ARCH_UNKNOWN) { + error.code = ERROR_BAD_ARCH; + return false; + } + + maps_.reset(new RemoteMaps(pid_)); + if (!maps_->Parse()) { + error.code = ERROR_MAPS_PARSE; + return false; + } + + if (process_memory_ == nullptr) { + process_memory_ = Memory::CreateProcessMemoryCached(pid_); + } + + return true; +} + +bool AndroidRemoteUnwinder::InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) { + if (!tid) { + *tid = pid_; + } + + std::unique_ptr<Regs> regs(Regs::RemoteGet(*tid)); + return AndroidUnwinder::Unwind(regs.get(), data); +} + +} // namespace unwindstack |