239 lines
6.7 KiB
C++
239 lines
6.7 KiB
C++
/*
|
|
* 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
|