summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2022-04-21 02:04:00 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-04-21 02:04:00 +0000
commit5ff2ac47e5169fe2a2ac34ac10353ed05c85b774 (patch)
treeda0c365d3a91eb655abe41fbe7a08c0e8833ce7a
parentf9eedfa1dfcef1ecca5a174057891a98c62b4f6e (diff)
parent1654af4eeab2540927aa5a04e674102a8ecf5a81 (diff)
downloadunwinding-5ff2ac47e5169fe2a2ac34ac10353ed05c85b774.tar.gz
Add the AndroidUnwinder objects. am: 215644709b am: 0b96208143 am: 7d76ac3305 am: 422b01c9de am: 1654af4eea
Original change: https://android-review.googlesource.com/c/platform/system/unwinding/+/2029306 Change-Id: I007bd3ea1ee28b18fc8328264846da01c01be2f9 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--libbacktrace/UnwindStack.cpp11
-rw-r--r--libunwindstack/Android.bp5
-rw-r--r--libunwindstack/AndroidUnwinder.cpp238
-rw-r--r--libunwindstack/ThreadUnwinder.cpp4
-rw-r--r--libunwindstack/Unwinder.cpp21
-rw-r--r--libunwindstack/benchmarks/local_unwind_benchmarks.cpp49
-rw-r--r--libunwindstack/benchmarks/remote_unwind_benchmarks.cpp68
-rw-r--r--libunwindstack/include/unwindstack/AndroidUnwinder.h164
-rw-r--r--libunwindstack/include/unwindstack/Error.h11
-rw-r--r--libunwindstack/include/unwindstack/Unwinder.h16
-rw-r--r--libunwindstack/tests/AndroidUnwinderTest.cpp426
-rw-r--r--libunwindstack/tests/MemoryMteTest.cpp5
-rw-r--r--libunwindstack/tests/MemoryRemoteTest.cpp37
-rw-r--r--libunwindstack/tests/RegsRemoteTest.cpp7
-rw-r--r--libunwindstack/tests/TestLocal.cpp16
-rw-r--r--libunwindstack/tests/TestUtils.cpp4
-rw-r--r--libunwindstack/tests/TestUtils.h60
-rw-r--r--libunwindstack/tests/UnwindTest.cpp61
-rw-r--r--libunwindstack/tests/UnwinderTest.cpp3
-rw-r--r--libunwindstack/utils/PidUtils.cpp130
-rw-r--r--libunwindstack/utils/PidUtils.h43
21 files changed, 1215 insertions, 164 deletions
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 95022fb..d2e65cb 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -95,10 +95,6 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
error->error_code = BACKTRACE_UNWIND_ERROR_INVALID_ELF;
break;
- case unwindstack::ERROR_SYSTEM_CALL:
- error->error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
- break;
-
case unwindstack::ERROR_THREAD_DOES_NOT_EXIST:
error->error_code = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
break;
@@ -106,6 +102,13 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
case unwindstack::ERROR_THREAD_TIMEOUT:
error->error_code = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
break;
+
+ case unwindstack::ERROR_SYSTEM_CALL:
+ case unwindstack::ERROR_MAPS_PARSE:
+ case unwindstack::ERROR_BAD_ARCH:
+ case unwindstack::ERROR_INVALID_PARAMETER:
+ error->error_code = BACKTRACE_UNWIND_ERROR_INTERNAL;
+ break;
}
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 9ceae7f..bed17e4 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -52,6 +52,7 @@ cc_defaults {
}
libunwindstack_common_src_files = [
+ "AndroidUnwinder.cpp",
"ArmExidx.cpp",
"DexFiles.cpp",
"DwarfCfa.cpp",
@@ -239,6 +240,7 @@ cc_library {
srcs: [
"utils/MemoryFake.cpp",
"utils/OfflineUnwindUtils.cpp",
+ "utils/PidUtils.cpp",
"utils/ProcessTracer.cpp",
],
}
@@ -266,6 +268,7 @@ cc_defaults {
defaults: ["libunwindstack_flags"],
srcs: [
+ "tests/AndroidUnwinderTest.cpp",
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
"tests/DexFileTest.cpp",
@@ -573,13 +576,13 @@ cc_benchmark {
static_libs: [
"libunwindstack_utils",
+ "libprocinfo",
],
target: {
android: {
static_libs: [
"libmeminfo",
- "libprocinfo",
],
},
},
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
diff --git a/libunwindstack/ThreadUnwinder.cpp b/libunwindstack/ThreadUnwinder.cpp
index 2bdb910..7b45261 100644
--- a/libunwindstack/ThreadUnwinder.cpp
+++ b/libunwindstack/ThreadUnwinder.cpp
@@ -72,6 +72,10 @@ static void SignalHandler(int, siginfo_t*, void* sigcontext) {
ThreadUnwinder::ThreadUnwinder(size_t max_frames, Maps* maps)
: UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch(), maps) {}
+ThreadUnwinder::ThreadUnwinder(size_t max_frames, Maps* maps,
+ std::shared_ptr<Memory>& process_memory)
+ : UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch(), maps, process_memory) {}
+
ThreadUnwinder::ThreadUnwinder(size_t max_frames, const ThreadUnwinder* unwinder)
: UnwinderFromPid(max_frames, getpid(), Regs::CurrentArch()) {
process_memory_ = unwinder->process_memory_;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index f6def23..facff86 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -145,6 +145,7 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
uint64_t step_pc;
uint64_t rel_pc;
Elf* elf;
+ bool ignore_frame = false;
if (map_info == nullptr) {
step_pc = regs_->pc();
rel_pc = step_pc;
@@ -155,7 +156,11 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
}
elf = nullptr;
} else {
- if (ShouldStop(map_suffixes_to_ignore, map_info->name())) {
+ ignore_frame =
+ initial_map_names_to_skip != nullptr &&
+ std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
+ android::base::Basename(map_info->name())) != initial_map_names_to_skip->end();
+ if (!ignore_frame && ShouldStop(map_suffixes_to_ignore, map_info->name())) {
break;
}
elf = map_info->GetElf(process_memory_, arch_);
@@ -186,9 +191,7 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
}
FrameData* frame = nullptr;
- if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
- std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
- android::base::Basename(map_info->name())) == initial_map_names_to_skip->end()) {
+ if (!ignore_frame) {
if (regs_->dex_pc() != 0) {
// Add a frame to represent the dex file.
FillInDexFrame();
@@ -296,8 +299,12 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
}
std::string Unwinder::FormatFrame(const FrameData& frame) const {
+ return FormatFrame(arch_, frame, display_build_id_);
+}
+
+std::string Unwinder::FormatFrame(ArchEnum arch, const FrameData& frame, bool display_build_id) {
std::string data;
- if (ArchIs32Bit(arch_)) {
+ if (ArchIs32Bit(arch)) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
} else {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
@@ -334,7 +341,7 @@ std::string Unwinder::FormatFrame(const FrameData& frame) const {
data += ')';
}
- if (map_info != nullptr && display_build_id_) {
+ if (map_info != nullptr && display_build_id) {
std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
data += " (BuildId: " + build_id + ')';
@@ -347,7 +354,7 @@ std::string Unwinder::FormatFrame(size_t frame_num) const {
if (frame_num >= frames_.size()) {
return "";
}
- return FormatFrame(frames_[frame_num]);
+ return FormatFrame(arch_, frames_[frame_num], display_build_id_);
}
void Unwinder::SetJitDebug(JitDebug* jit_debug) {
diff --git a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
index dd12ab5..78c8c13 100644
--- a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
@@ -22,6 +22,7 @@
#include <android-base/strings.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -99,6 +100,54 @@ static void BM_local_unwind_cached_process_memory(benchmark::State& state) {
}
BENCHMARK(BM_local_unwind_cached_process_memory);
+static void BM_local_android_unwind_uncached_process_memory(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+ unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+ unwindstack::ErrorData error;
+ if (!unwinder.Initialize(error)) {
+ state.SkipWithError("Failed to initialize.");
+ }
+
+ for (auto _ : state) {
+ if (LocalCall1(
+ [](void* u) -> size_t {
+ unwindstack::AndroidLocalUnwinder* unwinder =
+ reinterpret_cast<unwindstack::AndroidLocalUnwinder*>(u);
+ unwindstack::AndroidUnwinderData data;
+ unwinder->Unwind(data);
+ return data.frames.size();
+ },
+ &unwinder) < 5) {
+ state.SkipWithError("Failed to unwind.");
+ }
+ }
+}
+BENCHMARK(BM_local_android_unwind_uncached_process_memory);
+
+static void BM_local_android_unwind_cached_process_memory(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+ unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+ unwindstack::ErrorData error;
+ if (!unwinder.Initialize(error)) {
+ state.SkipWithError("Failed to initialize.");
+ }
+
+ for (auto _ : state) {
+ if (LocalCall1(
+ [](void* u) -> size_t {
+ unwindstack::AndroidLocalUnwinder* unwinder =
+ reinterpret_cast<unwindstack::AndroidLocalUnwinder*>(u);
+ unwindstack::AndroidUnwinderData data;
+ unwinder->Unwind(data);
+ return data.frames.size();
+ },
+ &unwinder) < 5) {
+ state.SkipWithError("Failed to unwind.");
+ }
+ }
+}
+BENCHMARK(BM_local_android_unwind_cached_process_memory);
+
static void BM_local_unwind_local_updatable_maps_uncached(benchmark::State& state) {
auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
unwindstack::LocalUpdatableMaps maps;
diff --git a/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp b/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp
index ef07e39..29c416d 100644
--- a/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/remote_unwind_benchmarks.cpp
@@ -24,35 +24,26 @@
#include <benchmark/benchmark.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
#include "MemoryRemote.h"
+#include "PidUtils.h"
#include "tests/TestUtils.h"
static bool WaitForRemote(pid_t pid, volatile bool* ready_ptr) {
- usleep(1000);
- for (size_t i = 0; i < 1000; i++) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
- unwindstack::TestQuiescePid(pid);
-
- unwindstack::MemoryRemote memory(pid);
- bool ready;
- uint64_t ready_addr = reinterpret_cast<uint64_t>(ready_ptr);
- if (memory.ReadFully(ready_addr, &ready, sizeof(ready)) && ready) {
- return true;
- }
- } else if (errno != ESRCH) {
- // Attach failed with unknown error.
- perror("Ptrace failed:");
- return false;
+ return unwindstack::RunWhenQuiesced(pid, true, [pid, ready_ptr]() {
+ unwindstack::MemoryRemote memory(pid);
+ bool ready;
+ uint64_t ready_addr = reinterpret_cast<uint64_t>(ready_ptr);
+ if (memory.ReadFully(ready_addr, &ready, sizeof(ready)) && ready) {
+ return unwindstack::PID_RUN_PASS;
}
- usleep(5000);
- }
- printf("Pid %d did not quiesce in a timely fashion.\n", pid);
- return false;
+ return unwindstack::PID_RUN_KEEP_GOING;
+ });
}
size_t RemoteCall6(volatile bool* ready) {
@@ -141,3 +132,42 @@ static void BM_remote_unwind_cached(benchmark::State& state) {
RemoteUnwind(state, true);
}
BENCHMARK(BM_remote_unwind_cached);
+
+static void RemoteAndroidUnwind(benchmark::State& state, bool cached) {
+ pid_t pid = StartRemoteRun();
+ if (pid == -1) {
+ state.SkipWithError("Failed to start remote process.");
+ }
+ unwindstack::TestScopedPidReaper reap(pid);
+
+ std::shared_ptr<unwindstack::Memory> process_memory;
+ if (cached) {
+ process_memory = unwindstack::Memory::CreateProcessMemoryCached(pid);
+ } else {
+ process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ }
+ unwindstack::AndroidRemoteUnwinder unwinder(pid, process_memory);
+ unwindstack::ErrorData error;
+ if (!unwinder.Initialize(error)) {
+ state.SkipWithError("Failed to initialize unwinder.");
+ }
+
+ for (auto _ : state) {
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data) || data.frames.size() < 5) {
+ state.SkipWithError("Failed to unwind properly.");
+ }
+ }
+
+ ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+
+static void BM_remote_android_unwind_uncached(benchmark::State& state) {
+ RemoteAndroidUnwind(state, true);
+}
+BENCHMARK(BM_remote_android_unwind_uncached);
+
+static void BM_remote_android_unwind_cached(benchmark::State& state) {
+ RemoteAndroidUnwind(state, true);
+}
+BENCHMARK(BM_remote_android_unwind_cached);
diff --git a/libunwindstack/include/unwindstack/AndroidUnwinder.h b/libunwindstack/include/unwindstack/AndroidUnwinder.h
new file mode 100644
index 0000000..3f82429
--- /dev/null
+++ b/libunwindstack/include/unwindstack/AndroidUnwinder.h
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_ANDROID_UNWINDER_H
+#define _LIBUNWINDSTACK_ANDROID_UNWINDER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+#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/SharedString.h>
+#include <unwindstack/Unwinder.h>
+
+namespace unwindstack {
+
+struct AndroidUnwinderData {
+ AndroidUnwinderData() = default;
+ explicit AndroidUnwinderData(const size_t max_frames) : max_frames(max_frames) {}
+ explicit AndroidUnwinderData(const bool show_all_frames) : show_all_frames(show_all_frames) {}
+
+ void DemangleFunctionNames();
+
+ std::string GetErrorString();
+
+ std::vector<FrameData> frames;
+ ErrorData error;
+ std::optional<std::unique_ptr<Regs>> saved_initial_regs;
+ const std::optional<size_t> max_frames;
+ const bool show_all_frames = false;
+};
+
+class AndroidUnwinder {
+ public:
+ AndroidUnwinder(pid_t pid) : pid_(pid) {}
+ AndroidUnwinder(pid_t pid, std::shared_ptr<Memory>& memory)
+ : pid_(pid), process_memory_(memory) {}
+ AndroidUnwinder(pid_t pid, ArchEnum arch) : pid_(pid), arch_(arch) {}
+ AndroidUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip)
+ : pid_(pid), initial_map_names_to_skip_(std::move(initial_map_names_to_skip)) {}
+ AndroidUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip,
+ const std::vector<std::string> map_suffixes_to_ignore)
+ : pid_(pid),
+ initial_map_names_to_skip_(std::move(initial_map_names_to_skip)),
+ map_suffixes_to_ignore_(std::move(map_suffixes_to_ignore)) {}
+ virtual ~AndroidUnwinder() = default;
+
+ bool Initialize(ErrorData& error);
+
+ std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
+ unwindstack::Maps* GetMaps() { return maps_.get(); }
+
+ const JitDebug& GetJitDebug() { return *jit_debug_.get(); }
+ const DexFiles& GetDexFiles() { return *dex_files_.get(); }
+
+ std::string FormatFrame(const FrameData& frame) const;
+
+ bool Unwind(AndroidUnwinderData& data);
+ bool Unwind(std::optional<pid_t> tid, AndroidUnwinderData& data);
+ bool Unwind(void* ucontext, AndroidUnwinderData& data);
+ bool Unwind(Regs* initial_regs, AndroidUnwinderData& data);
+
+ FrameData BuildFrameFromPcOnly(uint64_t pc);
+
+ static AndroidUnwinder* Create(pid_t pid);
+
+ protected:
+ virtual bool InternalInitialize(ErrorData& error) = 0;
+
+ virtual bool InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) = 0;
+
+ pid_t pid_;
+
+ size_t max_frames_ = kMaxNumFrames;
+ std::vector<std::string> initial_map_names_to_skip_;
+ std::vector<std::string> map_suffixes_to_ignore_;
+ std::once_flag initialize_;
+
+ ArchEnum arch_ = ARCH_UNKNOWN;
+
+ std::shared_ptr<Maps> maps_;
+ std::shared_ptr<Memory> process_memory_;
+ std::unique_ptr<JitDebug> jit_debug_;
+ std::unique_ptr<DexFiles> dex_files_;
+
+ static constexpr size_t kMaxNumFrames = 512;
+};
+
+class AndroidLocalUnwinder : public AndroidUnwinder {
+ public:
+ AndroidLocalUnwinder() : AndroidUnwinder(getpid()) {
+ initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+ }
+ AndroidLocalUnwinder(std::shared_ptr<Memory>& process_memory)
+ : AndroidUnwinder(getpid(), process_memory) {
+ initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+ }
+ AndroidLocalUnwinder(const std::vector<std::string>& initial_map_names_to_skip)
+ : AndroidUnwinder(getpid(), initial_map_names_to_skip) {
+ initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+ }
+ AndroidLocalUnwinder(const std::vector<std::string>& initial_map_names_to_skip,
+ const std::vector<std::string>& map_suffixes_to_ignore)
+ : AndroidUnwinder(getpid(), initial_map_names_to_skip, map_suffixes_to_ignore) {
+ initial_map_names_to_skip_.emplace_back(kUnwindstackLib);
+ }
+ virtual ~AndroidLocalUnwinder() = default;
+
+ protected:
+ static constexpr const char* kUnwindstackLib = "libunwindstack.so";
+
+ bool InternalInitialize(ErrorData& error) override;
+
+ bool InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) override;
+};
+
+class AndroidRemoteUnwinder : public AndroidUnwinder {
+ public:
+ AndroidRemoteUnwinder(pid_t pid) : AndroidUnwinder(pid) {}
+ AndroidRemoteUnwinder(pid_t pid, std::shared_ptr<Memory>& process_memory)
+ : AndroidUnwinder(pid, process_memory) {}
+ AndroidRemoteUnwinder(pid_t pid, ArchEnum arch) : AndroidUnwinder(pid, arch) {}
+ AndroidRemoteUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip)
+ : AndroidUnwinder(pid, initial_map_names_to_skip) {}
+ AndroidRemoteUnwinder(pid_t pid, const std::vector<std::string> initial_map_names_to_skip,
+ const std::vector<std::string> map_suffixes_to_ignore)
+ : AndroidUnwinder(pid, initial_map_names_to_skip, map_suffixes_to_ignore) {}
+ virtual ~AndroidRemoteUnwinder() = default;
+
+ protected:
+ bool InternalInitialize(ErrorData& error) override;
+
+ bool InternalUnwind(std::optional<pid_t> tid, AndroidUnwinderData& data) override;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_ANDROID_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 1610d55..abd59ad 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -41,7 +41,10 @@ enum ErrorCode : uint8_t {
// not exist.
ERROR_THREAD_TIMEOUT, // Timeout trying to unwind a local thread.
ERROR_SYSTEM_CALL, // System call failed while unwinding.
- ERROR_MAX = ERROR_SYSTEM_CALL,
+ ERROR_BAD_ARCH, // Arch invalid (none, or mismatched).
+ ERROR_MAPS_PARSE, // Failed to parse maps data.
+ ERROR_INVALID_PARAMETER, // Invalid parameter passed to function.
+ ERROR_MAX = ERROR_INVALID_PARAMETER,
};
static inline const char* GetErrorCodeString(ErrorCode error) {
@@ -68,6 +71,12 @@ static inline const char* GetErrorCodeString(ErrorCode error) {
return "Thread Timeout";
case ERROR_SYSTEM_CALL:
return "System Call Failed";
+ case ERROR_BAD_ARCH:
+ return "Invalid arch detected";
+ case ERROR_MAPS_PARSE:
+ return "Failed to parse maps";
+ case ERROR_INVALID_PARAMETER:
+ return "Invalid parameter";
}
}
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index de3438f..462d39b 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -83,6 +83,9 @@ class Unwinder {
std::string FormatFrame(size_t frame_num) const;
std::string FormatFrame(const FrameData& frame) const;
+ static std::string FormatFrame(ArchEnum arch, const FrameData& frame,
+ bool display_build_id = true);
+
void SetArch(ArchEnum arch) { arch_ = arch; };
void SetJitDebug(JitDebug* jit_debug);
@@ -102,6 +105,7 @@ class Unwinder {
void SetDexFiles(DexFiles* dex_files);
+ const ErrorData& LastError() { return last_error_; }
ErrorCode LastErrorCode() { return last_error_.code; }
const char* LastErrorCodeString() { return GetErrorCodeString(last_error_.code); }
uint64_t LastErrorAddress() { return last_error_.address; }
@@ -120,6 +124,8 @@ class Unwinder {
Unwinder(size_t max_frames, Maps* maps = nullptr) : max_frames_(max_frames), maps_(maps) {}
Unwinder(size_t max_frames, ArchEnum arch, Maps* maps = nullptr)
: max_frames_(max_frames), maps_(maps), arch_(arch) {}
+ Unwinder(size_t max_frames, ArchEnum arch, Maps* maps, std::shared_ptr<Memory>& process_memory)
+ : max_frames_(max_frames), maps_(maps), process_memory_(process_memory), arch_(arch) {}
void ClearErrors() {
warnings_ = WARNING_NONE;
@@ -149,14 +155,15 @@ class UnwinderFromPid : public Unwinder {
public:
UnwinderFromPid(size_t max_frames, pid_t pid, Maps* maps = nullptr)
: Unwinder(max_frames, maps), pid_(pid) {}
+ UnwinderFromPid(size_t max_frames, pid_t pid, std::shared_ptr<Memory>& process_memory)
+ : Unwinder(max_frames, nullptr, process_memory), pid_(pid) {}
UnwinderFromPid(size_t max_frames, pid_t pid, ArchEnum arch, Maps* maps = nullptr)
: Unwinder(max_frames, arch, maps), pid_(pid) {}
+ UnwinderFromPid(size_t max_frames, pid_t pid, ArchEnum arch, Maps* maps,
+ std::shared_ptr<Memory>& process_memory)
+ : Unwinder(max_frames, arch, maps, process_memory), pid_(pid) {}
virtual ~UnwinderFromPid() = default;
- void SetProcessMemory(std::shared_ptr<Memory>& process_memory) {
- process_memory_ = process_memory;
- }
-
bool Init();
void Unwind(const std::vector<std::string>* initial_map_names_to_skip = nullptr,
@@ -173,6 +180,7 @@ class UnwinderFromPid : public Unwinder {
class ThreadUnwinder : public UnwinderFromPid {
public:
ThreadUnwinder(size_t max_frames, Maps* maps = nullptr);
+ ThreadUnwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory>& process_memory);
ThreadUnwinder(size_t max_frames, const ThreadUnwinder* unwinder);
virtual ~ThreadUnwinder() = default;
diff --git a/libunwindstack/tests/AndroidUnwinderTest.cpp b/libunwindstack/tests/AndroidUnwinderTest.cpp
new file mode 100644
index 0000000..1794acc
--- /dev/null
+++ b/libunwindstack/tests/AndroidUnwinderTest.cpp
@@ -0,0 +1,426 @@
+/*
+ * 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 <dlfcn.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <android-base/threads.h>
+
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/UcontextArm.h>
+#include <unwindstack/UcontextArm64.h>
+#include <unwindstack/UcontextX86.h>
+#include <unwindstack/UcontextX86_64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "PidUtils.h"
+#include "TestUtils.h"
+
+namespace unwindstack {
+
+static std::string GetBacktrace(AndroidUnwinder& unwinder, std::vector<FrameData>& frames) {
+ std::string backtrace_str;
+ for (auto& frame : frames) {
+ backtrace_str += unwinder.FormatFrame(frame) + '\n';
+ }
+ return backtrace_str;
+}
+
+static pid_t ForkWaitForever() {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ // Do a loop that guarantees the terminating leaf frame will be in
+ // the test executable and not any other library function.
+ bool run = true;
+ while (run) {
+ DoNotOptimize(run = true);
+ }
+ exit(1);
+ }
+ return pid;
+}
+
+TEST(AndroidUnwinderDataTest, demangle_function_names) {
+ AndroidUnwinderData data;
+
+ // Add a few frames with and without demangled function names.
+ data.frames.resize(4);
+ data.frames[0].function_name = "no_demangle()";
+ data.frames[1].function_name = "_Z4fakeb";
+ data.frames[3].function_name = "_Z8demanglei";
+
+ data.DemangleFunctionNames();
+ EXPECT_EQ("no_demangle()", data.frames[0].function_name);
+ EXPECT_EQ("fake(bool)", data.frames[1].function_name);
+ EXPECT_EQ("", data.frames[2].function_name);
+ EXPECT_EQ("demangle(int)", data.frames[3].function_name);
+
+ // Make sure that this action is idempotent.
+ data.DemangleFunctionNames();
+ EXPECT_EQ("no_demangle()", data.frames[0].function_name);
+ EXPECT_EQ("fake(bool)", data.frames[1].function_name);
+ EXPECT_EQ("", data.frames[2].function_name);
+ EXPECT_EQ("demangle(int)", data.frames[3].function_name);
+}
+
+TEST(AndroidUnwinderDataTest, get_error_string) {
+ AndroidUnwinderData data;
+
+ EXPECT_EQ("None", data.GetErrorString());
+ data.error.code = ERROR_INVALID_ELF;
+ EXPECT_EQ("Invalid Elf", data.GetErrorString());
+ data.error.code = ERROR_MEMORY_INVALID;
+ EXPECT_EQ("Memory Invalid", data.GetErrorString());
+ data.error.address = 0x1000;
+ EXPECT_EQ("Memory Invalid at address 0x1000", data.GetErrorString());
+}
+
+TEST(AndroidUnwinderTest, unwind_errors) {
+ AndroidLocalUnwinder unwinder;
+
+ AndroidUnwinderData data;
+ void* ucontext = nullptr;
+ EXPECT_FALSE(unwinder.Unwind(ucontext, data));
+ EXPECT_EQ(ERROR_INVALID_PARAMETER, data.error.code);
+ std::unique_ptr<Regs> regs;
+ EXPECT_FALSE(unwinder.Unwind(regs.get(), data));
+ EXPECT_EQ(ERROR_INVALID_PARAMETER, data.error.code);
+ // Make sure that we are using a different arch from the
+ // current arch.
+ if (Regs::CurrentArch() == ARCH_ARM) {
+ regs.reset(new RegsArm64);
+ } else {
+ regs.reset(new RegsArm);
+ }
+ EXPECT_FALSE(unwinder.Unwind(regs.get(), data));
+ EXPECT_EQ(ERROR_BAD_ARCH, data.error.code);
+}
+
+TEST(AndroidUnwinderTest, create) {
+ // Verify the local unwinder object is created.
+ std::unique_ptr<AndroidUnwinder> unwinder(AndroidUnwinder::Create(getpid()));
+ AndroidUnwinderData data;
+ ASSERT_TRUE(unwinder->Unwind(data));
+
+ pid_t pid = ForkWaitForever();
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(RunWhenQuiesced(pid, false, [pid, &unwinder]() {
+ // Verify the remote unwinder object is created.
+ unwinder.reset(AndroidUnwinder::Create(pid));
+ AndroidUnwinderData data;
+ if (!unwinder->Unwind(data)) {
+ printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+ return PID_RUN_FAIL;
+ }
+ return PID_RUN_PASS;
+ }));
+}
+
+TEST(AndroidLocalUnwinderTest, initialize_before) {
+ AndroidLocalUnwinder unwinder;
+ ErrorData error;
+ ASSERT_TRUE(unwinder.Initialize(error));
+
+ AndroidUnwinderData data;
+ ASSERT_TRUE(unwinder.Unwind(data));
+}
+
+TEST(AndroidLocalUnwinderTest, suffix_ignore) {
+ AndroidLocalUnwinder unwinder(std::vector<std::string>{}, std::vector<std::string>{"so"});
+ AndroidUnwinderData data;
+ // This should work as long as the first frame is in the test executable.
+ ASSERT_TRUE(unwinder.Unwind(data));
+ // Make sure the unwind doesn't include any .so frames.
+ for (const auto& frame : data.frames) {
+ ASSERT_TRUE(frame.map_info == nullptr ||
+ !android::base::EndsWith(frame.map_info->name(), ".so"))
+ << GetBacktrace(unwinder, data.frames);
+ }
+}
+
+TEST(AndroidUnwinderTest, verify_all_unwind_functions) {
+ AndroidLocalUnwinder unwinder;
+ AndroidUnwinderData data;
+ ASSERT_TRUE(unwinder.Unwind(data));
+ ASSERT_TRUE(unwinder.Unwind(std::nullopt, data));
+ ASSERT_TRUE(unwinder.Unwind(getpid(), data));
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+
+ void* ucontext;
+ switch (regs->Arch()) {
+ case ARCH_ARM: {
+ arm_ucontext_t* arm_ucontext =
+ reinterpret_cast<arm_ucontext_t*>(malloc(sizeof(arm_ucontext_t)));
+ ucontext = arm_ucontext;
+ memcpy(&arm_ucontext->uc_mcontext.regs[0], regs->RawData(), ARM_REG_LAST * sizeof(uint32_t));
+ } break;
+ case ARCH_ARM64: {
+ arm64_ucontext_t* arm64_ucontext =
+ reinterpret_cast<arm64_ucontext_t*>(malloc(sizeof(arm64_ucontext_t)));
+ ucontext = arm64_ucontext;
+ memcpy(&arm64_ucontext->uc_mcontext.regs[0], regs->RawData(),
+ ARM64_REG_LAST * sizeof(uint64_t));
+ } break;
+ case ARCH_X86: {
+ x86_ucontext_t* x86_ucontext =
+ reinterpret_cast<x86_ucontext_t*>(malloc(sizeof(x86_ucontext_t)));
+ ucontext = x86_ucontext;
+ RegsX86* regs_x86 = static_cast<RegsX86*>(regs.get());
+
+ x86_ucontext->uc_mcontext.edi = (*regs_x86)[X86_REG_EDI];
+ x86_ucontext->uc_mcontext.esi = (*regs_x86)[X86_REG_ESI];
+ x86_ucontext->uc_mcontext.ebp = (*regs_x86)[X86_REG_EBP];
+ x86_ucontext->uc_mcontext.esp = (*regs_x86)[X86_REG_ESP];
+ x86_ucontext->uc_mcontext.ebx = (*regs_x86)[X86_REG_EBX];
+ x86_ucontext->uc_mcontext.edx = (*regs_x86)[X86_REG_EDX];
+ x86_ucontext->uc_mcontext.ecx = (*regs_x86)[X86_REG_ECX];
+ x86_ucontext->uc_mcontext.eax = (*regs_x86)[X86_REG_EAX];
+ x86_ucontext->uc_mcontext.eip = (*regs_x86)[X86_REG_EIP];
+ } break;
+ case ARCH_X86_64: {
+ x86_64_ucontext_t* x86_64_ucontext =
+ reinterpret_cast<x86_64_ucontext_t*>(malloc(sizeof(x86_64_ucontext_t)));
+ ucontext = x86_64_ucontext;
+ RegsX86_64* regs_x86_64 = static_cast<RegsX86_64*>(regs.get());
+
+ memcpy(&x86_64_ucontext->uc_mcontext.r8, &(*regs_x86_64)[X86_64_REG_R8],
+ 8 * sizeof(uint64_t));
+
+ x86_64_ucontext->uc_mcontext.rdi = (*regs_x86_64)[X86_64_REG_RDI];
+ x86_64_ucontext->uc_mcontext.rsi = (*regs_x86_64)[X86_64_REG_RSI];
+ x86_64_ucontext->uc_mcontext.rbp = (*regs_x86_64)[X86_64_REG_RBP];
+ x86_64_ucontext->uc_mcontext.rbx = (*regs_x86_64)[X86_64_REG_RBX];
+ x86_64_ucontext->uc_mcontext.rdx = (*regs_x86_64)[X86_64_REG_RDX];
+ x86_64_ucontext->uc_mcontext.rax = (*regs_x86_64)[X86_64_REG_RAX];
+ x86_64_ucontext->uc_mcontext.rcx = (*regs_x86_64)[X86_64_REG_RCX];
+ x86_64_ucontext->uc_mcontext.rsp = (*regs_x86_64)[X86_64_REG_RSP];
+ x86_64_ucontext->uc_mcontext.rip = (*regs_x86_64)[X86_64_REG_RIP];
+ } break;
+ default:
+ ucontext = nullptr;
+ break;
+ }
+ ASSERT_TRUE(ucontext != nullptr);
+ ASSERT_TRUE(unwinder.Unwind(ucontext, data));
+ free(ucontext);
+ AndroidUnwinderData reg_data;
+ ASSERT_TRUE(unwinder.Unwind(regs.get(), reg_data));
+ ASSERT_EQ(data.frames.size(), reg_data.frames.size());
+ // Make sure all of the frame data is exactly the same.
+ for (size_t i = 0; i < data.frames.size(); i++) {
+ SCOPED_TRACE("\nMismatch at Frame " + std::to_string(i) + "\nucontext trace:\n" +
+ GetBacktrace(unwinder, data.frames) + "\nregs trace:\n" +
+ GetBacktrace(unwinder, reg_data.frames));
+ const auto& frame_context = data.frames[i];
+ const auto& frame_reg = reg_data.frames[i];
+ ASSERT_EQ(frame_context.num, frame_reg.num);
+ ASSERT_EQ(frame_context.rel_pc, frame_reg.rel_pc);
+ ASSERT_EQ(frame_context.pc, frame_reg.pc);
+ ASSERT_EQ(frame_context.sp, frame_reg.sp);
+ ASSERT_STREQ(frame_context.function_name.c_str(), frame_reg.function_name.c_str());
+ ASSERT_EQ(frame_context.function_offset, frame_reg.function_offset);
+ ASSERT_EQ(frame_context.map_info.get(), frame_reg.map_info.get());
+ }
+}
+
+TEST(AndroidLocalUnwinderTest, unwind_current_thread) {
+ AndroidLocalUnwinder unwinder;
+ AndroidUnwinderData data;
+ ASSERT_TRUE(unwinder.Unwind(data));
+ // Verify that the libunwindstack.so does not appear in the first frame.
+ ASSERT_TRUE(data.frames[0].map_info == nullptr ||
+ !android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+ << "libunwindstack.so not removed properly\n"
+ << GetBacktrace(unwinder, data.frames);
+}
+
+TEST(AndroidLocalUnwinderTest, unwind_current_thread_show_all_frames) {
+ AndroidLocalUnwinder unwinder;
+ AndroidUnwinderData data(true);
+ ASSERT_TRUE(unwinder.Unwind(data));
+ // Verify that the libunwindstack.so does appear in the first frame.
+ ASSERT_TRUE(data.frames[0].map_info != nullptr &&
+ android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+ << "libunwindstack.so was removed improperly\n"
+ << GetBacktrace(unwinder, data.frames);
+}
+
+TEST(AndroidLocalUnwinderTest, unwind_different_thread) {
+ std::atomic<pid_t> tid;
+ std::atomic_bool keep_running = true;
+ std::thread thread([&tid, &keep_running] {
+ tid = android::base::GetThreadId();
+ while (keep_running) {
+ }
+ return nullptr;
+ });
+
+ while (tid == 0) {
+ }
+
+ {
+ AndroidLocalUnwinder unwinder;
+ AndroidUnwinderData data;
+ ASSERT_TRUE(unwinder.Unwind(data));
+ // Verify that the libunwindstack.so does not appear in the first frame.
+ ASSERT_TRUE(data.frames[0].map_info == nullptr ||
+ !android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+ << "libunwindstack.so not removed properly\n"
+ << GetBacktrace(unwinder, data.frames);
+ }
+
+ {
+ AndroidLocalUnwinder unwinder;
+ AndroidUnwinderData data(true);
+ ASSERT_TRUE(unwinder.Unwind(data));
+ // Verify that the libunwindstack.so does appear in the first frame.
+ ASSERT_TRUE(data.frames[0].map_info != nullptr &&
+ android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack.so"))
+ << "libunwindstack.so was removed improperly\n"
+ << GetBacktrace(unwinder, data.frames);
+ }
+
+ // Allow the thread to terminate normally.
+ keep_running = false;
+ thread.join();
+}
+
+TEST(AndroidRemoteUnwinderTest, initialize_before) {
+ pid_t pid = ForkWaitForever();
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Attach(pid));
+
+ AndroidRemoteUnwinder unwinder(pid);
+ ErrorData error;
+ ASSERT_TRUE(unwinder.Initialize(error));
+
+ AndroidUnwinderData data;
+ ASSERT_TRUE(unwinder.Unwind(data));
+
+ ASSERT_TRUE(Detach(pid));
+}
+
+static bool Verify(pid_t pid, std::function<PidRunEnum(const FrameData& frame)> fn) {
+ return RunWhenQuiesced(pid, false, [pid, &fn]() {
+ AndroidRemoteUnwinder unwinder(pid);
+ AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
+ printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+ return PID_RUN_FAIL;
+ }
+ const auto& frame = data.frames[0];
+ return fn(frame);
+ });
+}
+
+TEST(AndroidRemoteUnwinderTest, skip_libraries) {
+ void* test_lib = GetTestLibHandle();
+ ASSERT_TRUE(test_lib != nullptr);
+ int (*wait_func)() = reinterpret_cast<int (*)()>(dlsym(test_lib, "WaitForever"));
+ ASSERT_TRUE(wait_func != nullptr);
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ DoNotOptimize(wait_func());
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Verify(pid, [pid](const FrameData& frame) {
+ // Make sure that the frame is in the dlopen'd library before proceeding.
+ if (frame.map_info == nullptr ||
+ !android::base::EndsWith(frame.map_info->name(), "/libunwindstack_local.so")) {
+ return PID_RUN_KEEP_GOING;
+ }
+
+ // Do an unwind removing the libunwindstack_local.so library.
+ AndroidRemoteUnwinder unwinder(pid, std::vector<std::string>{"libunwindstack_local.so"});
+ AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
+ printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+ return PID_RUN_FAIL;
+ }
+
+ // Verify that library is properly ignored.
+ if (android::base::EndsWith(data.frames[0].map_info->name(), "/libunwindstack_local.so")) {
+ printf("Failed to strip libunwindstack_local.so\n%s\n",
+ GetBacktrace(unwinder, data.frames).c_str());
+ return PID_RUN_FAIL;
+ }
+ return PID_RUN_PASS;
+ }));
+}
+
+TEST(AndroidRemoteUnwinderTest, suffix_ignore) {
+ pid_t pid = ForkWaitForever();
+ ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
+
+ ASSERT_TRUE(Verify(pid, [pid](const FrameData& frame) {
+ // Wait until the forked process is no longer in libc.so.
+ if (frame.map_info != nullptr && android::base::EndsWith(frame.map_info->name(), ".so")) {
+ return PID_RUN_KEEP_GOING;
+ }
+
+ AndroidRemoteUnwinder unwinder(pid, std::vector<std::string>{}, std::vector<std::string>{"so"});
+ AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
+ printf("Failed to unwind %s\n", data.GetErrorString().c_str());
+
+ AndroidRemoteUnwinder normal_unwinder(pid);
+ if (normal_unwinder.Unwind(data)) {
+ printf("Full unwind %s\n", GetBacktrace(normal_unwinder, data.frames).c_str());
+ }
+ return PID_RUN_FAIL;
+ }
+
+ // Make sure the unwind doesn't include any .so frames.
+ for (const auto& frame : data.frames) {
+ if (frame.map_info != nullptr && android::base::EndsWith(frame.map_info->name(), ".so")) {
+ printf("Found unexpected .so frame\n%s\n", GetBacktrace(unwinder, data.frames).c_str());
+ return PID_RUN_FAIL;
+ }
+ }
+ return PID_RUN_PASS;
+ }));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryMteTest.cpp b/libunwindstack/tests/MemoryMteTest.cpp
index 9aab0c0..8129394 100644
--- a/libunwindstack/tests/MemoryMteTest.cpp
+++ b/libunwindstack/tests/MemoryMteTest.cpp
@@ -25,6 +25,7 @@
#include "MemoryLocal.h"
#include "MemoryRemote.h"
+#include "PidUtils.h"
#include "TestUtils.h"
namespace unwindstack {
@@ -68,14 +69,14 @@ TEST(MemoryMteTest, remote_read_tag) {
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
EXPECT_EQ(1, remote.ReadTag(mapping));
EXPECT_EQ(0, remote.ReadTag(mapping + 16));
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryMteTest, local_read_tag) {
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 2b6e682..ed58eb9 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -32,6 +32,7 @@
#include "MemoryRemote.h"
+#include "PidUtils.h"
#include "TestUtils.h"
#include "utils/MemoryFake.h"
@@ -49,7 +50,7 @@ TEST(MemoryRemoteTest, read) {
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -59,7 +60,7 @@ TEST(MemoryRemoteTest, read) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_large) {
@@ -78,7 +79,7 @@ TEST(MemoryRemoteTest, read_large) {
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -88,7 +89,7 @@ TEST(MemoryRemoteTest, read_large) {
ASSERT_EQ(i / getpagesize(), dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_partial) {
@@ -111,7 +112,7 @@ TEST(MemoryRemoteTest, read_partial) {
// Unmap from our process.
ASSERT_EQ(0, munmap(mapping, 3 * getpagesize()));
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -132,7 +133,7 @@ TEST(MemoryRemoteTest, read_partial) {
ASSERT_EQ(0x4cU, dst[i]) << "Failed at byte " << i;
}
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_fail) {
@@ -152,7 +153,7 @@ TEST(MemoryRemoteTest, read_fail) {
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -171,7 +172,7 @@ TEST(MemoryRemoteTest, read_fail) {
ASSERT_EQ(0, munmap(src, pagesize));
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_overflow) {
@@ -184,7 +185,7 @@ TEST(MemoryRemoteTest, read_overflow) {
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -192,7 +193,7 @@ TEST(MemoryRemoteTest, read_overflow) {
std::vector<uint8_t> dst(200);
ASSERT_FALSE(remote.ReadFully(UINT64_MAX - 100, dst.data(), 200));
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_illegal) {
@@ -204,7 +205,7 @@ TEST(MemoryRemoteTest, read_illegal) {
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -212,7 +213,7 @@ TEST(MemoryRemoteTest, read_illegal) {
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 1));
ASSERT_FALSE(remote.ReadFully(0, dst.data(), 100));
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_mprotect_hole) {
@@ -233,7 +234,7 @@ TEST(MemoryRemoteTest, read_mprotect_hole) {
ASSERT_EQ(0, munmap(mapping, 3 * page_size));
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -247,7 +248,7 @@ TEST(MemoryRemoteTest, read_mprotect_hole) {
ASSERT_EQ(0xCC, dst[i]);
}
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_munmap_hole) {
@@ -270,7 +271,7 @@ TEST(MemoryRemoteTest, read_munmap_hole) {
ASSERT_EQ(0, munmap(mapping, page_size));
ASSERT_EQ(0, munmap(static_cast<char*>(mapping) + 2 * page_size, page_size));
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
std::vector<uint8_t> dst(getpagesize() * 4, 0xCC);
@@ -283,7 +284,7 @@ TEST(MemoryRemoteTest, read_munmap_hole) {
ASSERT_EQ(0xCC, dst[i]);
}
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
// Verify that the memory remote object chooses a memory read function
@@ -307,7 +308,7 @@ TEST(MemoryRemoteTest, read_choose_correctly) {
ASSERT_EQ(0, munmap(mapping, 2 * page_size));
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
// We know that process_vm_readv of a mprotect'd PROT_NONE region will fail.
// Read from the PROT_NONE area first to force the choice of ptrace.
@@ -336,7 +337,7 @@ TEST(MemoryRemoteTest, read_choose_correctly) {
ASSERT_EQ(sizeof(value), bytes);
ASSERT_EQ(0xfcfcfcfcU, value);
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsRemoteTest.cpp b/libunwindstack/tests/RegsRemoteTest.cpp
index 4b5223b..2427501 100644
--- a/libunwindstack/tests/RegsRemoteTest.cpp
+++ b/libunwindstack/tests/RegsRemoteTest.cpp
@@ -26,7 +26,7 @@
#include <unwindstack/Regs.h>
-#include "TestUtils.h"
+#include "PidUtils.h"
namespace unwindstack {
@@ -40,15 +40,14 @@ class RegsRemoteTest : public ::testing::Test {
exit(1);
}
ASSERT_TRUE(pid_ != -1);
- ASSERT_TRUE(TestAttach(pid_));
- ASSERT_TRUE(TestQuiescePid(pid_));
+ ASSERT_TRUE(Attach(pid_));
}
void TearDown() override {
if (pid_ == -1) {
return;
}
- EXPECT_TRUE(TestDetach(pid_));
+ EXPECT_TRUE(Detach(pid_));
kill(pid_, SIGKILL);
waitpid(pid_, nullptr, 0);
}
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
index f8684d7..25bd42f 100644
--- a/libunwindstack/tests/TestLocal.cpp
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -17,6 +17,8 @@
#include <stdint.h>
#include <stdlib.h>
+#include "TestUtils.h"
+
// The loop in this function is only guaranteed to not be optimized away by the compiler
// if optimizations are turned off. This is partially because the compiler doesn't have
// any idea about the function since it is retrieved using dlsym.
@@ -25,7 +27,17 @@
// 1. The loop iteration variable is volatile.
// 2. A call to this function should be wrapped in TestUtils::DoNotOptimize().
extern "C" int BusyWait() {
- for (volatile size_t i = 0; i < 1000000; ++i)
- ;
+ for (size_t i = 0; i < 1000000;) {
+ unwindstack::DoNotOptimize(i++);
+ }
return 0;
}
+
+// Do a loop that guarantees the terminating leaf frame will be in
+// the this library and not a function from a different library.
+extern "C" void WaitForever() {
+ bool run = true;
+ while (run) {
+ unwindstack::DoNotOptimize(run = true);
+ }
+}
diff --git a/libunwindstack/tests/TestUtils.cpp b/libunwindstack/tests/TestUtils.cpp
index f2046f8..1f748ba 100644
--- a/libunwindstack/tests/TestUtils.cpp
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -18,8 +18,12 @@
#include <malloc.h>
#include <stdint.h>
+#include <string>
+
#include <gtest/gtest.h>
+#include "TestUtils.h"
+
namespace unwindstack {
void TestCheckForLeaks(void (*unwind_func)(void*), void* data) {
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index 44b4e75..2378fd5 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -17,12 +17,10 @@
#ifndef _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
-#include <errno.h>
#include <signal.h>
-#include <sys/ptrace.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <unistd.h>
namespace unwindstack {
@@ -38,62 +36,6 @@ class TestScopedPidReaper {
pid_t pid_;
};
-inline bool TestQuiescePid(pid_t pid) {
- siginfo_t si;
- // Wait for up to 10 seconds.
- for (size_t i = 0; i < 10000; i++) {
- if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
- return true;
- }
- if (errno != ESRCH) {
- if (errno == EINVAL) {
- // The process is in group-stop state, so try and kick the
- // process out of that state.
- if (ptrace(PTRACE_LISTEN, pid, 0, 0) == -1) {
- perror("ptrace listen failed.");
- return false;
- }
- } else {
- return false;
- }
- }
- usleep(1000);
- }
- return false;
-}
-
-inline bool TestAttach(pid_t pid) {
- // Wait up to 10 seconds to attach.
- for (size_t j = 0; j < 10000; j++) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
- break;
- }
- if (errno == ESRCH) {
- usleep(1000);
- continue;
- }
- perror("Failed to attach.");
- return false;
- }
-
- if (TestQuiescePid(pid)) {
- return true;
- }
-
- if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
- perror("Failed to detach.");
- }
- return false;
-}
-
-inline bool TestDetach(pid_t pid) {
- if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
- perror("ptrace detach failed");
- return false;
- }
- return true;
-}
-
void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
void* GetTestLibHandle();
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 2ed1b9b..9a5a96f 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -40,6 +40,7 @@
#include <unwindstack/Unwinder.h>
#include "MemoryRemote.h"
+#include "PidUtils.h"
#include "TestUtils.h"
namespace unwindstack {
@@ -236,28 +237,15 @@ TEST_F(UnwindTest, local_use_from_pid_check_for_leak) {
TestCheckForLeaks(LocalUnwind, &test_type);
}
-void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
- *completed = false;
- // Need to sleep before attempting first ptrace. Without this, on the
- // host it becomes impossible to attach and ptrace sets errno to EPERM.
- usleep(1000);
- for (size_t i = 0; i < 4000; i++) {
- ASSERT_TRUE(TestAttach(pid));
-
- MemoryRemote memory(pid);
- // Read the remote value to see if we are ready.
+static bool WaitForRemote(pid_t pid, bool leave_attached, uint64_t addr) {
+ MemoryRemote memory(pid);
+ return RunWhenQuiesced(pid, leave_attached, [addr, &memory]() {
bool value;
if (memory.ReadFully(addr, &value, sizeof(value)) && value) {
- *completed = true;
- }
- if (!*completed || !leave_attached) {
- ASSERT_TRUE(TestDetach(pid));
- }
- if (*completed) {
- break;
+ return PID_RUN_PASS;
}
- usleep(5000);
- }
+ return PID_RUN_KEEP_GOING;
+ });
}
TEST_F(UnwindTest, remote) {
@@ -269,9 +257,7 @@ TEST_F(UnwindTest, remote) {
ASSERT_NE(-1, pid);
TestScopedPidReaper reap(pid);
- bool completed;
- WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
- ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
RemoteMaps maps(pid);
ASSERT_TRUE(maps.Parse());
@@ -280,7 +266,7 @@ TEST_F(UnwindTest, remote) {
VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST_F(UnwindTest, unwind_from_pid_remote) {
@@ -292,9 +278,7 @@ TEST_F(UnwindTest, unwind_from_pid_remote) {
ASSERT_NE(-1, pid);
TestScopedPidReaper reap(pid);
- bool completed;
- WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
- ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
ASSERT_TRUE(regs.get() != nullptr);
@@ -304,7 +288,7 @@ TEST_F(UnwindTest, unwind_from_pid_remote) {
VerifyUnwind(&unwinder, kFunctionOrder);
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
@@ -316,13 +300,11 @@ static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
ASSERT_NE(-1, pid);
TestScopedPidReaper reap(pid);
- bool completed;
- WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
- ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
TestCheckForLeaks(unwind_func, &pid);
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
static void RemoteUnwind(void* data) {
@@ -368,8 +350,8 @@ TEST_F(UnwindTest, from_context) {
act.sa_sigaction = SignalHandler;
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
- // Wait for the tid to get set.
- for (size_t i = 0; i < 100; i++) {
+ // Wait 20 seconds for the tid to get set.
+ for (time_t start_time = time(nullptr); time(nullptr) - start_time < 20;) {
if (tid.load() != 0) {
break;
}
@@ -378,9 +360,9 @@ TEST_F(UnwindTest, from_context) {
ASSERT_NE(0, tid.load());
ASSERT_EQ(0, tgkill(getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno);
- // Wait for context data.
+ // Wait 20 seconds for context data.
void* ucontext;
- for (size_t i = 0; i < 2000; i++) {
+ for (time_t start_time = time(nullptr); time(nullptr) - start_time < 20;) {
ucontext = reinterpret_cast<void*>(g_ucontext.load());
if (ucontext != nullptr) {
break;
@@ -416,14 +398,11 @@ static void RemoteThroughSignal(int signal, unsigned int sa_flags) {
ASSERT_NE(-1, pid);
TestScopedPidReaper reap(pid);
- bool completed;
if (signal != SIGSEGV) {
- WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
- ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_TRUE(WaitForRemote(pid, false, reinterpret_cast<uint64_t>(&g_ready_for_remote)));
ASSERT_EQ(0, kill(pid, SIGUSR1));
}
- WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
- ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
+ ASSERT_TRUE(WaitForRemote(pid, true, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote)));
RemoteMaps maps(pid);
ASSERT_TRUE(maps.Parse());
@@ -432,7 +411,7 @@ static void RemoteThroughSignal(int signal, unsigned int sa_flags) {
VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST_F(UnwindTest, remote_through_signal) {
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 74bc516..2da3ee1 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -1761,8 +1761,7 @@ TEST_F(UnwinderTest, build_frame_pc_in_jit) {
TEST_F(UnwinderTest, unwinder_from_pid_set_process_memory) {
auto process_memory = Memory::CreateProcessMemoryCached(getpid());
- UnwinderFromPid unwinder(10, getpid());
- unwinder.SetProcessMemory(process_memory);
+ UnwinderFromPid unwinder(10, getpid(), process_memory);
unwinder.SetArch(unwindstack::Regs::CurrentArch());
ASSERT_TRUE(unwinder.Init());
ASSERT_EQ(process_memory.get(), unwinder.GetProcessMemory().get());
diff --git a/libunwindstack/utils/PidUtils.cpp b/libunwindstack/utils/PidUtils.cpp
new file mode 100644
index 0000000..4268225
--- /dev/null
+++ b/libunwindstack/utils/PidUtils.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "PidUtils.h"
+
+namespace unwindstack {
+
+static bool Exited(pid_t pid) {
+ int status;
+ pid_t wait_pid = waitpid(pid, &status, WNOHANG);
+ if (wait_pid != pid) {
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ fprintf(stderr, "%d died: Process exited with code %d\n", pid, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%d died: Process exited due to signal %d\n", pid, WTERMSIG(status));
+ } else {
+ fprintf(stderr, "%d died: Process finished for unknown reason\n", pid);
+ }
+ return true;
+}
+
+bool Quiesce(pid_t pid) {
+ siginfo_t si;
+ // Wait for up to 10 seconds.
+ for (time_t start_time = time(nullptr); time(nullptr) - start_time < 10;) {
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ return true;
+ }
+ if (errno != ESRCH) {
+ if (errno == EINVAL) {
+ // The process is in group-stop state, so try and kick the
+ // process out of that state.
+ if (ptrace(PTRACE_LISTEN, pid, 0, 0) == -1) {
+ // Cannot recover from this, so just pretend it worked and see
+ // if we can unwind.
+ return true;
+ }
+ } else {
+ perror("ptrace getsiginfo failed");
+ return false;
+ }
+ }
+ usleep(5000);
+ }
+ fprintf(stderr, "Did not quiesce in 10 seconds\n");
+ return false;
+}
+
+bool Attach(pid_t pid) {
+ // Wait up to 45 seconds to attach.
+ for (time_t start_time = time(nullptr); time(nullptr) - start_time < 45;) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ break;
+ }
+ if (errno != ESRCH) {
+ perror("Failed to attach");
+ return false;
+ }
+ usleep(5000);
+ }
+
+ if (Quiesce(pid)) {
+ return true;
+ }
+
+ if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
+ perror("Failed to detach");
+ }
+ return false;
+}
+
+bool Detach(pid_t pid) {
+ if (ptrace(PTRACE_DETACH, pid, 0, 0) == -1) {
+ perror("ptrace detach failed");
+ return false;
+ }
+ return true;
+}
+
+bool RunWhenQuiesced(pid_t pid, bool leave_attached, std::function<PidRunEnum()> fn) {
+ // Wait up to 120 seconds to run the fn.
+ PidRunEnum status = PID_RUN_KEEP_GOING;
+ for (time_t start_time = time(nullptr);
+ time(nullptr) - start_time < 120 && status == PID_RUN_KEEP_GOING;) {
+ if (Attach(pid)) {
+ status = fn();
+ if (status == PID_RUN_PASS && leave_attached) {
+ return true;
+ }
+
+ if (!Detach(pid)) {
+ return false;
+ }
+ } else if (Exited(pid)) {
+ return false;
+ }
+ usleep(5000);
+ }
+ if (status == PID_RUN_KEEP_GOING) {
+ fprintf(stderr, "Timed out waiting for pid %d to be ready\n", pid);
+ }
+ return status == PID_RUN_PASS;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/utils/PidUtils.h b/libunwindstack/utils/PidUtils.h
new file mode 100644
index 0000000..5dbd4ae
--- /dev/null
+++ b/libunwindstack/utils/PidUtils.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_UTILS_PID_UTILS_H
+#define _LIBUNWINDSTACK_UTILS_PID_UTILS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <functional>
+
+namespace unwindstack {
+
+enum PidRunEnum : uint8_t {
+ PID_RUN_KEEP_GOING,
+ PID_RUN_PASS,
+ PID_RUN_FAIL,
+};
+
+bool Quiesce(pid_t pid);
+
+bool Attach(pid_t pid);
+
+bool Detach(pid_t pid);
+
+bool RunWhenQuiesced(pid_t pid, bool leave_attached, std::function<PidRunEnum()> fn);
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UTILS_PID_UTILS_H