summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2022-03-10 14:16:07 -0800
committerChristopher Ferris <cferris@google.com>2022-04-20 14:13:53 -0700
commit215644709bd7bbda4c05a9db4d55e96be61bcf64 (patch)
treeda0c365d3a91eb655abe41fbe7a08c0e8833ce7a
parentd963687ae044f91eb52f39fc38b8f9edc3ad34db (diff)
downloadunwinding-215644709bd7bbda4c05a9db4d55e96be61bcf64.tar.gz
Add the AndroidUnwinder objects.
Create two different objects (AndroidLocalUnwinder and AndroidRemoteUnwinder) that encapsulate all Android related knowledge. This makes it simple for other code to create one of these objects and not need to add all of the Android specific code themselves. Add new unit tests to cover these new objects. New unit tests uncovered a small bug in the Unwinder object, namely that if you are skipping libraries, the suffixes to ignore still looks at the ignored frames. Fixed to only apply ignore suffixes to only non-ignored frames. Rewrote all of the places that attach to remote processes and created a single function to handle all of the different cases. Bug: 120606663 Test: All unit tests pass. Test: Ran the tests hundreds of times on host and target. Test: There are still some failures, but only 1 in 800 and 1 in 300. Change-Id: I9d0732e33695af20b3c3f87c625e623f6b7f9ef2
-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