Snap for 8570526 from f09ba56496c440750920bb51eb8a1d3ac450f8f2 to mainline-media-swcodec-release
Change-Id: I5dfee721e13b19af1c88c96a427237f56343c9da
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 38645cc..af14cde 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -56,9 +56,6 @@
"//apex_available:anyapex",
],
target: {
- linux_glibc: {
- enabled: true,
- },
linux_bionic: {
enabled: true,
},
@@ -119,6 +116,19 @@
"//apex_available:platform",
"//apex_available:anyapex",
],
+ // This library is being deprecated in favor of libunwindstack.
+ // Therefore, only allow the current set of users, and block anyone
+ // else.
+ visibility: [
+ "//art:__subpackages__",
+ "//bionic/libc/malloc_debug",
+ "//frameworks/native/opengl/libs",
+ "//packages/modules/vndk/apex",
+ "//packages/modules/Bluetooth/tools/rootcanal",
+ "//packages/modules/Bluetooth/system/gd",
+ "//system/core/init",
+ "//system/core/libutils",
+ ],
min_sdk_version: "apex_inherit",
vndk: {
enabled: true,
@@ -152,6 +162,7 @@
// Static library without DEX support to avoid dependencies on the ART APEX.
cc_library_static {
name: "libbacktrace_no_dex",
+ ramdisk_available: true,
visibility: [
"//system/core/debuggerd",
"//system/core/init",
@@ -182,7 +193,7 @@
],
target: {
- linux_glibc: {
+ host_linux: {
// This forces the creation of eh_frame with unwind information
// for host.
cflags: [
@@ -220,8 +231,6 @@
"libunwindstack",
],
- group_static_libs: true,
-
target: {
android: {
// So that the dlopen can find the libbacktrace_test.so.
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index a506575..84c7bc8 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -28,6 +28,7 @@
#include <string>
+#include <android-base/file.h>
#include <android-base/threads.h>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
@@ -97,7 +98,7 @@
bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) {
if (BacktraceMap::IsValid(frame.map)) {
- const std::string library = basename(frame.map.name.c_str());
+ const std::string library = android::base::Basename(frame.map.name);
if (library == "libunwind.so" || library == "libbacktrace.so") {
return true;
}
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 0b3bb70..d2e65cb 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -95,10 +95,6 @@
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 @@
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;
}
}
@@ -137,12 +140,20 @@
}
back_frame->func_offset = frame->function_offset;
- back_frame->map.name = frame->map_name;
- back_frame->map.start = frame->map_start;
- back_frame->map.end = frame->map_end;
- back_frame->map.offset = frame->map_elf_start_offset;
- back_frame->map.load_bias = frame->map_load_bias;
- back_frame->map.flags = frame->map_flags;
+ if (frame->map_info != nullptr) {
+ back_frame->map.name = frame->map_info->name();
+ back_frame->map.start = frame->map_info->start();
+ back_frame->map.end = frame->map_info->end();
+ back_frame->map.offset = frame->map_info->elf_start_offset();
+ back_frame->map.load_bias = frame->map_info->load_bias();
+ back_frame->map.flags = frame->map_info->flags();
+ } else {
+ back_frame->map.start = 0;
+ back_frame->map.end = 0;
+ back_frame->map.offset = 0;
+ back_frame->map.load_bias = 0;
+ back_frame->map.flags = 0;
+ }
}
return true;
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 7c15ed7..b0508d9 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -92,19 +92,24 @@
}
// Fill in the load_bias.
- unwindstack::MapInfo* map_info = stack_maps_->Find(addr);
+ std::shared_ptr<unwindstack::MapInfo> map_info = stack_maps_->Find(addr);
if (map_info == nullptr) {
return;
}
map->load_bias = map_info->GetLoadBias(process_memory_);
}
+std::string UnwindStackMap::GetBuildId(uint64_t addr) {
+ auto map_info = stack_maps_->Find(addr);
+ return map_info == nullptr ? std::string() : map_info->GetPrintableBuildID();
+}
+
uint64_t UnwindStackMap::GetLoadBias(size_t index) {
if (index >= stack_maps_->Total()) {
return 0;
}
- unwindstack::MapInfo* map_info = stack_maps_->Get(index);
+ std::shared_ptr<unwindstack::MapInfo> map_info = stack_maps_->Get(index);
if (map_info == nullptr) {
return 0;
}
@@ -116,7 +121,7 @@
unwindstack::Maps* maps = stack_maps();
// Get the map for this
- unwindstack::MapInfo* map_info = maps->Find(pc);
+ auto map_info = maps->Find(pc);
if (map_info == nullptr || map_info->flags() & PROT_DEVICE_MAP) {
return "";
}
@@ -135,7 +140,7 @@
unwindstack::SharedString name;
uint64_t func_offset;
- if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info), &name, &func_offset)) {
+ if (!elf->GetFunctionName(elf->GetRelPc(pc, map_info.get()), &name, &func_offset)) {
return "";
}
*offset = func_offset;
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index f0e7d8b..3c2591e 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -47,6 +47,8 @@
void FillIn(uint64_t addr, backtrace_map_t* map) override;
+ std::string GetBuildId(uint64_t addr) override;
+
virtual std::string GetFunctionName(uint64_t pc, uint64_t* offset) override;
virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() override final;
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index a9160d5..b978349 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -45,6 +45,7 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
+#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/test_utils.h>
@@ -281,7 +282,7 @@
// None of the frames should be in the backtrace libraries.
for (const auto& frame : *backtrace ) {
if (BacktraceMap::IsValid(frame.map)) {
- const std::string name = basename(frame.map.name.c_str());
+ const std::string name = android::base::Basename(frame.map.name);
for (const auto& lib : kBacktraceLibs) {
ASSERT_TRUE(name != lib) << DumpFrames(backtrace.get());
}
@@ -302,7 +303,7 @@
size_t first_frame_non_backtrace_lib = 0;
for (const auto& frame : *backtrace) {
if (BacktraceMap::IsValid(frame.map)) {
- const std::string name = basename(frame.map.name.c_str());
+ const std::string name = android::base::Basename(frame.map.name);
bool found = false;
for (const auto& lib : kBacktraceLibs) {
if (name == lib) {
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index e000a00..eab1328 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -117,6 +117,9 @@
// Fill in the map data structure for the given address.
virtual void FillIn(uint64_t addr, backtrace_map_t* map);
+ // Get BuildId of ELF file at the given address, or empty string on failure.
+ virtual std::string GetBuildId(uint64_t /*pc*/) { return std::string(); }
+
// Only supported with the new unwinder.
virtual std::string GetFunctionName(uint64_t /*pc*/, uint64_t* /*offset*/) { return ""; }
virtual std::shared_ptr<unwindstack::Memory> GetProcessMemory() { return nullptr; }
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 5443ce7..bed17e4 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -51,42 +51,44 @@
},
}
+libunwindstack_common_src_files = [
+ "AndroidUnwinder.cpp",
+ "ArmExidx.cpp",
+ "DexFiles.cpp",
+ "DwarfCfa.cpp",
+ "DwarfEhFrameWithHdr.cpp",
+ "DwarfMemory.cpp",
+ "DwarfOp.cpp",
+ "DwarfSection.cpp",
+ "Elf.cpp",
+ "ElfInterface.cpp",
+ "ElfInterfaceArm.cpp",
+ "Global.cpp",
+ "JitDebug.cpp",
+ "MapInfo.cpp",
+ "Maps.cpp",
+ "Memory.cpp",
+ "MemoryMte.cpp",
+ "MemoryXz.cpp",
+ "Regs.cpp",
+ "RegsArm.cpp",
+ "RegsArm64.cpp",
+ "RegsX86.cpp",
+ "RegsX86_64.cpp",
+ "RegsMips.cpp",
+ "RegsMips64.cpp",
+ "Symbols.cpp",
+ "ThreadEntry.cpp",
+ "ThreadUnwinder.cpp",
+ "Unwinder.cpp",
+]
+
cc_defaults {
name: "libunwindstack_defaults",
defaults: ["libunwindstack_flags"],
export_include_dirs: ["include"],
- srcs: [
- "ArmExidx.cpp",
- "DexFiles.cpp",
- "DwarfCfa.cpp",
- "DwarfEhFrameWithHdr.cpp",
- "DwarfMemory.cpp",
- "DwarfOp.cpp",
- "DwarfSection.cpp",
- "Elf.cpp",
- "ElfInterface.cpp",
- "ElfInterfaceArm.cpp",
- "Global.cpp",
- "JitDebug.cpp",
- "Log.cpp",
- "MapInfo.cpp",
- "Maps.cpp",
- "Memory.cpp",
- "MemoryMte.cpp",
- "LocalUnwinder.cpp",
- "Regs.cpp",
- "RegsArm.cpp",
- "RegsArm64.cpp",
- "RegsX86.cpp",
- "RegsX86_64.cpp",
- "RegsMips.cpp",
- "RegsMips64.cpp",
- "Symbols.cpp",
- "ThreadEntry.cpp",
- "ThreadUnwinder.cpp",
- "Unwinder.cpp",
- ],
+ srcs: libunwindstack_common_src_files,
cflags: [
"-Wexit-time-destructors",
@@ -140,35 +142,44 @@
support_system_process: true,
},
defaults: ["libunwindstack_defaults"],
- srcs: ["DexFile.cpp"],
+ srcs: [
+ "DexFile.cpp",
+ "LogAndroid.cpp",
+ ],
cflags: ["-DDEXFILE_SUPPORT"],
static_libs: ["libdexfile_support"],
+ runtime_libs: ["libdexfile"], // libdexfile_support dependency
target: {
vendor: {
cflags: ["-UDEXFILE_SUPPORT"],
exclude_srcs: ["DexFile.cpp"],
exclude_static_libs: ["libdexfile_support"],
+ exclude_runtime_libs: ["libdexfile"],
},
product: {
cflags: ["-UDEXFILE_SUPPORT"],
exclude_srcs: ["DexFile.cpp"],
exclude_static_libs: ["libdexfile_support"],
+ exclude_runtime_libs: ["libdexfile"],
},
recovery: {
cflags: ["-UDEXFILE_SUPPORT"],
exclude_srcs: ["DexFile.cpp"],
exclude_static_libs: ["libdexfile_support"],
+ exclude_runtime_libs: ["libdexfile"],
},
vendor_ramdisk: {
cflags: ["-UDEXFILE_SUPPORT"],
exclude_srcs: ["DexFile.cpp"],
exclude_static_libs: ["libdexfile_support"],
+ exclude_runtime_libs: ["libdexfile"],
},
native_bridge: {
cflags: ["-UDEXFILE_SUPPORT"],
exclude_srcs: ["DexFile.cpp"],
exclude_static_libs: ["libdexfile_support"],
+ exclude_runtime_libs: ["libdexfile"],
},
},
@@ -180,12 +191,23 @@
min_sdk_version: "29",
}
+// Make sure that the code can be compiled without Android Logging.
+cc_library {
+ name: "libunwindstack_stdout_log",
+ defaults: ["libunwindstack_defaults"],
+ srcs: [
+ "LogStdout.cpp",
+ ],
+}
+
// Static library without DEX support to avoid dependencies on the ART APEX.
cc_library_static {
name: "libunwindstack_no_dex",
+ ramdisk_available: true,
recovery_available: true,
vendor_ramdisk_available: true,
defaults: ["libunwindstack_defaults"],
+ srcs: ["LogAndroid.cpp"],
visibility: [
"//external/gwp_asan",
@@ -200,6 +222,30 @@
}
//-------------------------------------------------------------------------
+// Utils
+//-------------------------------------------------------------------------
+cc_library {
+ name: "libunwindstack_utils",
+ defaults: ["libunwindstack_flags"],
+ export_include_dirs: ["utils"],
+ shared_libs: [
+ "libbase",
+ "libunwindstack",
+ "libprocinfo",
+ ],
+ whole_static_libs: [
+ "libc++fs",
+ "libz",
+ ],
+ srcs: [
+ "utils/MemoryFake.cpp",
+ "utils/OfflineUnwindUtils.cpp",
+ "utils/PidUtils.cpp",
+ "utils/ProcessTracer.cpp",
+ ],
+}
+
+//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
cc_library_shared {
@@ -222,6 +268,7 @@
defaults: ["libunwindstack_flags"],
srcs: [
+ "tests/AndroidUnwinderTest.cpp",
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
"tests/DexFileTest.cpp",
@@ -243,9 +290,9 @@
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
"tests/GlobalDebugImplTest.cpp",
+ "tests/GlobalTest.cpp",
"tests/IsolatedSettings.cpp",
"tests/JitDebugTest.cpp",
- "tests/LocalUnwinderTest.cpp",
"tests/LocalUpdatableMapsTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoCreateMemoryTest.cpp",
@@ -256,7 +303,6 @@
"tests/MapsTest.cpp",
"tests/MemoryBufferTest.cpp",
"tests/MemoryCacheTest.cpp",
- "tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
"tests/MemoryOfflineBufferTest.cpp",
@@ -270,6 +316,7 @@
"tests/MemoryXzTest.cpp",
"tests/RegsInfoTest.cpp",
"tests/RegsIterateTest.cpp",
+ "tests/RegsRemoteTest.cpp",
"tests/RegsStepIfSignalHandlerTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
@@ -278,6 +325,7 @@
"tests/UnwindTest.cpp",
"tests/UnwinderTest.cpp",
"tests/VerifyBionicTerminationTest.cpp",
+ "utils/tests/ProcessTracerTest.cpp",
],
cflags: [
@@ -295,6 +343,8 @@
static_libs: [
"libdexfile_support",
"libgmock",
+ "libprocinfo",
+ "libunwindstack_utils",
],
test_suites: ["device-tests"],
@@ -306,30 +356,47 @@
"tests/files/boot_arm.oat.gnu_debugdata.xz.one-block",
"tests/files/elf32.xz",
"tests/files/elf64.xz",
- "tests/files/offline/art_quick_osr_stub_arm/*",
- "tests/files/offline/bad_eh_frame_hdr_arm64/*",
- "tests/files/offline/debug_frame_first_x86/*",
- "tests/files/offline/debug_frame_load_bias_arm/*",
- "tests/files/offline/eh_frame_bias_x86/*",
- "tests/files/offline/eh_frame_hdr_begin_x86_64/*",
- "tests/files/offline/empty_arm64/*",
- "tests/files/offline/invalid_elf_offset_arm/*",
- "tests/files/offline/jit_debug_arm/*",
- "tests/files/offline/jit_debug_x86/*",
- "tests/files/offline/jit_map_arm/*",
- "tests/files/offline/gnu_debugdata_arm/*",
- "tests/files/offline/load_bias_different_section_bias_arm64/*",
- "tests/files/offline/load_bias_ro_rx_x86_64/*",
- "tests/files/offline/offset_arm/*",
- "tests/files/offline/pauth_pc_arm64/*",
- "tests/files/offline/shared_lib_in_apk_arm64/*",
- "tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
- "tests/files/offline/shared_lib_in_apk_single_map_arm64/*",
- "tests/files/offline/signal_load_bias_arm/*",
- "tests/files/offline/signal_fde_x86/*",
- "tests/files/offline/signal_fde_x86_64/*",
- "tests/files/offline/straddle_arm/*",
- "tests/files/offline/straddle_arm64/*",
+ "offline_files/common/*",
+ "offline_files/apk_rorx_arm64/*",
+ "offline_files/apk_rorx_unreadable_arm64/*",
+ "offline_files/apk_rx_arm64/*",
+ "offline_files/apk_rx_unreadable_arm64/*",
+ "offline_files/art_quick_osr_stub_arm/*",
+ "offline_files/bad_eh_frame_hdr_arm64/*",
+ "offline_files/debug_frame_first_x86/*",
+ "offline_files/debug_frame_load_bias_arm/*",
+ "offline_files/eh_frame_bias_x86/*",
+ "offline_files/eh_frame_hdr_begin_x86_64/*",
+ "offline_files/empty_arm64/*",
+ "offline_files/invalid_elf_offset_arm/*",
+ "offline_files/jit_debug_arm/*",
+ "offline_files/jit_map_arm/*",
+ "offline_files/gnu_debugdata_arm/*",
+ "offline_files/load_bias_different_section_bias_arm64/*",
+ "offline_files/load_bias_ro_rx_x86_64/*",
+ "offline_files/offset_arm/*",
+ "offline_files/pauth_pc_arm64/*",
+ "offline_files/shared_lib_in_apk_arm64/*",
+ "offline_files/shared_lib_in_apk_memory_only_arm64/*",
+ "offline_files/shared_lib_in_apk_single_map_arm64/*",
+ "offline_files/signal_load_bias_arm/*",
+ "offline_files/signal_fde_x86/*",
+ "offline_files/signal_fde_x86_64/*",
+ "offline_files/straddle_arm/*",
+ "offline_files/jit_debug_x86/*",
+ "offline_files/straddle_arm64/*",
+ "offline_files/bluetooth_arm64/pc_1/*",
+ "offline_files/bluetooth_arm64/pc_2/*",
+ "offline_files/bluetooth_arm64/pc_3/*",
+ "offline_files/bluetooth_arm64/pc_4/*",
+ "offline_files/photos_reset_arm64/*",
+ "offline_files/youtube_compiled_arm64/*",
+ "offline_files/yt_music_arm64/*",
+ "offline_files/maps_compiled_arm64/28613_main-thread/*",
+ "offline_files/maps_compiled_arm64/28644/*",
+ "offline_files/maps_compiled_arm64/28648/*",
+ "offline_files/maps_compiled_arm64/28656_oat_odex_jar/*",
+ "offline_files/maps_compiled_arm64/28667/*",
],
target: {
@@ -371,6 +438,7 @@
],
static_libs: [
"libdexfile_support",
+ "libunwindstack_utils",
],
}
@@ -378,7 +446,6 @@
name: "libunwindstack_fuzz_unwinder",
defaults: ["libunwindstack_fuzz_defaults"],
srcs: [
- "tests/MemoryFake.cpp",
"tests/ElfFake.cpp",
"tests/fuzz/UnwinderComponentCreator.cpp",
"tests/fuzz/UnwinderFuzz.cpp",
@@ -393,7 +460,7 @@
defaults: ["libunwindstack_flags"],
shared_libs: [
- "libunwindstack",
+ "libunwindstack_stdout_log",
"libbase",
"liblzma",
],
@@ -438,6 +505,10 @@
cc_binary {
name: "unwind_for_offline",
defaults: ["libunwindstack_tools"],
+ static_libs: [
+ "libunwindstack_utils",
+ "libc++fs",
+ ],
srcs: [
"tools/unwind_for_offline.cpp",
@@ -475,10 +546,27 @@
"benchmarks/main.cpp",
"benchmarks/remote_unwind_benchmarks.cpp",
"benchmarks/thread_unwind_benchmarks.cpp",
+ "benchmarks/OfflineUnwindBenchmarks.cpp",
+ "benchmarks/EvalBenchmark.cpp",
],
data: [
"benchmarks/files/*",
+ "offline_files/common/*",
+ "offline_files/jit_debug_arm/*",
+ "offline_files/straddle_arm64/*",
+ "offline_files/bluetooth_arm64/pc_1/*",
+ "offline_files/bluetooth_arm64/pc_2/*",
+ "offline_files/bluetooth_arm64/pc_3/*",
+ "offline_files/bluetooth_arm64/pc_4/*",
+ "offline_files/photos_reset_arm64/*",
+ "offline_files/youtube_compiled_arm64/*",
+ "offline_files/yt_music_arm64/*",
+ "offline_files/maps_compiled_arm64/28613_main-thread/*",
+ "offline_files/maps_compiled_arm64/28644/*",
+ "offline_files/maps_compiled_arm64/28648/*",
+ "offline_files/maps_compiled_arm64/28656_oat_odex_jar/*",
+ "offline_files/maps_compiled_arm64/28667/*",
],
shared_libs: [
@@ -486,11 +574,15 @@
"libunwindstack",
],
+ 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/AndroidVersions.md b/libunwindstack/AndroidVersions.md
index b4e29ca..ff46829 100644
--- a/libunwindstack/AndroidVersions.md
+++ b/libunwindstack/AndroidVersions.md
@@ -114,3 +114,19 @@
and uses pc relative FDEs, the unwind will be incorrect. This tends
to truncate unwinds since the unwinder could not find the correct unwind
information for a given pc.
+
+## Android 12 ("S", API level 31)
+* Fix bug where, if a shared library is dlopen'ed from within an apk file,
+ is not readable, and the shared library only produces a single read-
+ executable map for the elf data and executable data, the offset into the
+ apk will not be displayed. Previously the line would look like:
+
+ #01 pc 000000000222675c GoogleCamera.apk
+
+ to:
+
+ #01 pc 000000000222675c GoogleCamera.apk (offset 0x269f000)
+
+ If the apk file is readable, or dlopen'ing the shared library creates
+ a read-only map of the elf data, and a read-executable map of the
+ code, the offset will be displayed properly without this fix.
diff --git a/libunwindstack/ArmExidx.cpp b/libunwindstack/ArmExidx.cpp
index 818f5d1..c737cef 100644
--- a/libunwindstack/ArmExidx.cpp
+++ b/libunwindstack/ArmExidx.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <inttypes.h>
#include <stdint.h>
#include <deque>
@@ -38,7 +39,7 @@
for (const uint8_t data : data_) {
log_str += android::base::StringPrintf(" 0x%02x", data);
}
- log(log_indent_, log_str.c_str());
+ Log::Info(log_indent_, "%s", log_str.c_str());
}
bool ArmExidx::ExtractEntryData(uint32_t entry_offset) {
@@ -67,9 +68,9 @@
status_ = ARM_STATUS_NO_UNWIND;
if (log_type_ != ARM_LOG_NONE) {
if (log_type_ == ARM_LOG_FULL) {
- log(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
+ Log::Info(log_indent_, "Raw Data: 0x00 0x00 0x00 0x01");
}
- log(log_indent_, "[cantunwind]");
+ Log::Info(log_indent_, "[cantunwind]");
}
return false;
}
@@ -195,7 +196,7 @@
if (registers == 0) {
// 10000000 00000000: Refuse to unwind
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Refuse to unwind");
+ Log::Info(log_indent_, "Refuse to unwind");
}
status_ = ARM_STATUS_NO_UNWIND;
return false;
@@ -216,7 +217,7 @@
add_comma = true;
}
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
uint32_t cfa_offset = __builtin_popcount(registers) * 4;
log_cfa_offset_ += cfa_offset;
@@ -264,7 +265,7 @@
// 10011101: Reserved as prefix for ARM register to register moves
// 10011111: Reserved as prefix for Intel Wireless MMX register to register moves
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "[Reserved]");
+ Log::Info(log_indent_, "[Reserved]");
}
status_ = ARM_STATUS_RESERVED;
return false;
@@ -272,7 +273,7 @@
// 1001nnnn: Set vsp = r[nnnn] (nnnn != 13, 15)
if (log_type_ != ARM_LOG_NONE) {
if (log_type_ == ARM_LOG_FULL) {
- log(log_indent_, "vsp = r%d", bits);
+ Log::Info(log_indent_, "vsp = r%d", bits);
} else {
log_regs_[LOG_CFA_REG] = bits;
}
@@ -300,9 +301,9 @@
msg += android::base::StringPrintf("-r%d", 4 + end_reg);
}
if (byte & 0x8) {
- log(log_indent_, "%s, r14}", msg.c_str());
+ Log::Info(log_indent_, "%s, r14}", msg.c_str());
} else {
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
}
} else {
end_reg += 4;
@@ -350,7 +351,7 @@
// 10110000: Finish
if (log_type_ != ARM_LOG_NONE) {
if (log_type_ == ARM_LOG_FULL) {
- log(log_indent_, "finish");
+ Log::Info(log_indent_, "finish");
}
if (log_skip_execution_) {
@@ -371,7 +372,7 @@
if (byte == 0) {
// 10110001 00000000: Spare
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Spare");
+ Log::Info(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
@@ -379,7 +380,7 @@
if (byte >> 4) {
// 10110001 xxxxyyyy: Spare (xxxx != 0000)
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Spare");
+ Log::Info(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
@@ -399,7 +400,7 @@
add_comma = true;
}
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
byte &= 0xf;
uint32_t cfa_offset = __builtin_popcount(byte) * 4;
@@ -456,7 +457,7 @@
if (log_type_ != ARM_LOG_NONE) {
int32_t cfa_offset = 0x204 + result;
if (log_type_ == ARM_LOG_FULL) {
- log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ Log::Info(log_indent_, "vsp = vsp + %d", cfa_offset);
} else {
log_cfa_offset_ += cfa_offset;
}
@@ -486,9 +487,9 @@
if (end_reg) {
msg += android::base::StringPrintf("-d%d", end_reg);
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported DX register display");
+ Log::Info(log_indent_, "Unsupported DX register display");
}
if (log_skip_execution_) {
@@ -502,7 +503,7 @@
inline bool ArmExidx::DecodePrefix_10_11_01nn() {
// 101101nn: Spare
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Spare");
+ Log::Info(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
@@ -519,9 +520,9 @@
if (last_reg) {
msg += android::base::StringPrintf("-d%d", last_reg + 8);
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported DX register display");
+ Log::Info(log_indent_, "Unsupported DX register display");
}
if (log_skip_execution_) {
@@ -581,9 +582,9 @@
if (end_reg) {
msg += android::base::StringPrintf("-wR%d", start_reg + end_reg);
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported wRX register display");
+ Log::Info(log_indent_, "Unsupported wRX register display");
}
if (log_skip_execution_) {
@@ -600,7 +601,7 @@
if (byte == 0) {
// 11000111 00000000: Spare
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Spare");
+ Log::Info(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
@@ -619,9 +620,9 @@
add_comma = true;
}
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported wCGR register display");
+ Log::Info(log_indent_, "Unsupported wCGR register display");
}
if (log_skip_execution_) {
@@ -633,7 +634,7 @@
} else {
// 11000111 xxxxyyyy: Spare (xxxx != 0000)
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Spare");
+ Log::Info(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
@@ -647,9 +648,9 @@
if (nnn) {
msg += android::base::StringPrintf("-wR%d", 10 + nnn);
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported wRX register display");
+ Log::Info(log_indent_, "Unsupported wRX register display");
}
if (log_skip_execution_) {
@@ -680,9 +681,9 @@
if (end_reg) {
msg += android::base::StringPrintf("-d%d", 16 + start_reg + end_reg);
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported DX register display");
+ Log::Info(log_indent_, "Unsupported DX register display");
}
if (log_skip_execution_) {
@@ -705,9 +706,9 @@
if (end_reg) {
msg += android::base::StringPrintf("-d%d", start_reg + end_reg);
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported DX register display");
+ Log::Info(log_indent_, "Unsupported DX register display");
}
if (log_skip_execution_) {
@@ -719,7 +720,7 @@
} else {
// 11001yyy: Spare (yyy != 000, 001)
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Spare");
+ Log::Info(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
@@ -738,9 +739,9 @@
if (end_reg) {
msg += android::base::StringPrintf("-d%d", 8 + end_reg);
}
- log(log_indent_, "%s}", msg.c_str());
+ Log::Info(log_indent_, "%s}", msg.c_str());
} else {
- log(log_indent_, "Unsupported DX register display");
+ Log::Info(log_indent_, "Unsupported DX register display");
}
if (log_skip_execution_) {
@@ -764,7 +765,7 @@
default:
// 11xxxyyy: Spare (xxx != 000, 001, 010)
if (log_type_ != ARM_LOG_NONE) {
- log(log_indent_, "Spare");
+ Log::Info(log_indent_, "Spare");
}
status_ = ARM_STATUS_SPARE;
return false;
@@ -784,7 +785,7 @@
if (log_type_ != ARM_LOG_NONE) {
int32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
if (log_type_ == ARM_LOG_FULL) {
- log(log_indent_, "vsp = vsp + %d", cfa_offset);
+ Log::Info(log_indent_, "vsp = vsp + %d", cfa_offset);
} else {
log_cfa_offset_ += cfa_offset;
}
@@ -801,7 +802,7 @@
if (log_type_ != ARM_LOG_NONE) {
uint32_t cfa_offset = ((byte & 0x3f) << 2) + 4;
if (log_type_ == ARM_LOG_FULL) {
- log(log_indent_, "vsp = vsp - %d", cfa_offset);
+ Log::Info(log_indent_, "vsp = vsp - %d", cfa_offset);
} else {
log_cfa_offset_ -= cfa_offset;
}
@@ -841,9 +842,9 @@
if (log_cfa_offset_ != 0) {
char sign = (log_cfa_offset_ > 0) ? '+' : '-';
- log(log_indent_, "cfa = r%zu %c %d", cfa_reg, sign, abs(log_cfa_offset_));
+ Log::Info(log_indent_, "cfa = r%" PRIu8 " %c %d", cfa_reg, sign, abs(log_cfa_offset_));
} else {
- log(log_indent_, "cfa = r%zu", cfa_reg);
+ Log::Info(log_indent_, "cfa = r%" PRIu8, cfa_reg);
}
for (const auto& entry : log_regs_) {
@@ -851,10 +852,10 @@
break;
}
if (entry.second == 0) {
- log(log_indent_, "r%zu = [cfa]", entry.first);
+ Log::Info(log_indent_, "r%" PRIu8 " = [cfa]", entry.first);
} else {
char sign = (entry.second > 0) ? '-' : '+';
- log(log_indent_, "r%zu = [cfa %c %d]", entry.first, sign, abs(entry.second));
+ Log::Info(log_indent_, "r%" PRIu8 " = [cfa %c %d]", entry.first, sign, abs(entry.second));
}
}
}
diff --git a/libunwindstack/ArmExidx.h b/libunwindstack/ArmExidx.h
index d9fc371..847613a 100644
--- a/libunwindstack/ArmExidx.h
+++ b/libunwindstack/ArmExidx.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_ARM_EXIDX_H
-#define _LIBUNWINDSTACK_ARM_EXIDX_H
+#pragma once
#include <stdint.h>
@@ -122,5 +121,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_ARM_EXIDX_H
diff --git a/libunwindstack/Check.h b/libunwindstack/Check.h
index 9643d76..102cbb1 100644
--- a/libunwindstack/Check.h
+++ b/libunwindstack/Check.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_CHECK_H
-#define _LIBUNWINDSTACK_CHECK_H
+#pragma once
#include <stdlib.h>
@@ -23,12 +22,10 @@
namespace unwindstack {
-#define CHECK(assertion) \
- if (__builtin_expect(!(assertion), false)) { \
- log(0, "%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
- abort(); \
+#define CHECK(assertion) \
+ if (__builtin_expect(!(assertion), false)) { \
+ Log::Error("%s:%d: %s\n", __FILE__, __LINE__, #assertion); \
+ abort(); \
}
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_CHECK_H
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index e576354..220ee82 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -22,12 +22,10 @@
#include <memory>
-#define LOG_TAG "unwind"
-#include <log/log.h>
-
#include <android-base/unique_fd.h>
#include <art_api/dex_file_support.h>
+#include <unwindstack/Log.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>
@@ -41,7 +39,7 @@
static bool CheckDexSupport() {
if (std::string err_msg; !art_api::dex::TryLoadLibdexfile(&err_msg)) {
- ALOGW("Failed to initialize DEX file support: %s", err_msg.c_str());
+ Log::Error("Failed to initialize DEX file support: %s", err_msg.c_str());
return false;
}
return true;
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index 6775530..3bb65da 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DEX_FILE_H
-#define _LIBUNWINDSTACK_DEX_FILE_H
+#pragma once
#include <stdint.h>
@@ -80,5 +79,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DEX_FILE_H
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index bc99012..981f158 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -29,7 +29,7 @@
template <>
bool GlobalDebugInterface<DexFile>::Load(Maps* maps, std::shared_ptr<Memory>& memory, uint64_t addr,
uint64_t size, /*out*/ std::shared_ptr<DexFile>& dex) {
- dex = DexFile::Create(addr, size, memory.get(), maps->Find(addr));
+ dex = DexFile::Create(addr, size, memory.get(), maps->Find(addr).get());
return dex.get() != nullptr;
}
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 884acf5..c167cd4 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -93,7 +93,7 @@
}
case 3: {
if (cie_loc_regs_ == nullptr) {
- log(0, "restore while processing cie");
+ Log::Error("Invalid: restore while processing cie.");
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
return false;
}
@@ -197,8 +197,8 @@
}
raw_data += android::base::StringPrintf(" 0x%02x", value);
}
- log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
- log(indent, "%s", raw_data.c_str());
+ Log::Info(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
+ Log::Info(indent, "%s", raw_data.c_str());
return true;
}
@@ -208,11 +208,11 @@
const auto* cfa = &DwarfCfaInfo::kTable[op];
if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
if (op == 0x2d) {
- log(indent, "Illegal (Only valid on aarch64)");
+ Log::Info(indent, "Illegal (Only valid on aarch64)");
} else {
- log(indent, "Illegal");
+ Log::Info(indent, "Illegal");
}
- log(indent, "Raw Data: 0x%02x", op);
+ Log::Info(indent, "Raw Data: 0x%02x", op);
return true;
}
@@ -239,7 +239,7 @@
log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
}
}
- log(indent, "%s", log_string.c_str());
+ Log::Info(indent, "%s", log_string.c_str());
// Get the raw bytes of the data.
uint64_t end_offset = memory_->cur_offset();
@@ -253,7 +253,7 @@
// Only show 10 raw bytes per line.
if ((i % 10) == 0 && i != 0) {
- log(indent, "%s", raw_data.c_str());
+ Log::Info(indent, "%s", raw_data.c_str());
raw_data.clear();
}
if (raw_data.empty()) {
@@ -262,12 +262,12 @@
raw_data += android::base::StringPrintf(" 0x%02x", value);
}
if (!raw_data.empty()) {
- log(indent, "%s", raw_data.c_str());
+ Log::Info(indent, "%s", raw_data.c_str());
}
// Log any of the expression data.
for (const auto& line : expression_lines) {
- log(indent + 1, "%s", line.c_str());
+ Log::Info(indent + 1, "%s", line.c_str());
}
return true;
}
@@ -295,8 +295,8 @@
}
break;
case 1:
- log(indent, "DW_CFA_advance_loc %d", cfa_low);
- log(indent, "Raw Data: 0x%02x", cfa_value);
+ Log::Info(indent, "DW_CFA_advance_loc %d", cfa_low);
+ Log::Info(indent, "Raw Data: 0x%02x", cfa_value);
cur_pc += cfa_low * fde_->cie->code_alignment_factor;
break;
case 2:
@@ -305,13 +305,14 @@
}
break;
case 3:
- log(indent, "DW_CFA_restore register(%d)", cfa_low);
- log(indent, "Raw Data: 0x%02x", cfa_value);
+ Log::Info(indent, "DW_CFA_restore register(%d)", cfa_low);
+ Log::Info(indent, "Raw Data: 0x%02x", cfa_value);
break;
}
if (cur_pc != old_pc) {
- log(0, "");
- log(indent, "PC 0x%" PRIx64, cur_pc);
+ // This forces a newline or empty log line.
+ Log::Info("");
+ Log::Info(indent, "PC 0x%" PRIx64, cur_pc);
}
old_pc = cur_pc;
}
@@ -324,16 +325,23 @@
return true;
}
-template <typename AddressType>
-bool DwarfCfa<AddressType>::cfa_set_loc(DwarfLocations*) {
- AddressType cur_pc = cur_pc_;
- AddressType new_pc = operands_[0];
+template <>
+bool DwarfCfa<uint32_t>::cfa_set_loc(DwarfLocations*) {
+ uint32_t cur_pc = cur_pc_;
+ uint32_t new_pc = operands_[0];
if (new_pc < cur_pc) {
- if (std::is_same<AddressType, uint32_t>::value) {
- log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
- } else {
- log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
- }
+ Log::Info("Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
+ }
+ cur_pc_ = new_pc;
+ return true;
+}
+
+template <>
+bool DwarfCfa<uint64_t>::cfa_set_loc(DwarfLocations*) {
+ uint64_t cur_pc = cur_pc_;
+ uint64_t new_pc = operands_[0];
+ if (new_pc < cur_pc) {
+ Log::Info("Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
}
cur_pc_ = new_pc;
return true;
@@ -356,7 +364,7 @@
bool DwarfCfa<AddressType>::cfa_restore(DwarfLocations* loc_regs) {
AddressType reg = operands_[0];
if (cie_loc_regs_ == nullptr) {
- log(0, "restore while processing cie");
+ Log::Error("Invalid: restore while processing cie.");
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
return false;
}
@@ -400,7 +408,7 @@
template <typename AddressType>
bool DwarfCfa<AddressType>::cfa_restore_state(DwarfLocations* loc_regs) {
if (loc_reg_state_.size() == 0) {
- log(0, "Warning: Attempt to restore without remember.");
+ Log::Info("Warning: Attempt to restore without remember.");
return true;
}
*loc_regs = loc_reg_state_.top();
@@ -418,7 +426,7 @@
bool DwarfCfa<AddressType>::cfa_def_cfa_register(DwarfLocations* loc_regs) {
auto cfa_location = loc_regs->find(CFA_REG);
if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
- log(0, "Attempt to set new register, but cfa is not already set to a register.");
+ Log::Error("Attempt to set new register, but cfa is not already set to a register.");
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
return false;
}
@@ -432,7 +440,7 @@
// Changing the offset if this is not a register is illegal.
auto cfa_location = loc_regs->find(CFA_REG);
if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
- log(0, "Attempt to set offset, but cfa is not set to a register.");
+ Log::Error("Attempt to set offset, but cfa is not set to a register.");
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
return false;
}
@@ -479,7 +487,7 @@
// Changing the offset if this is not a register is illegal.
auto cfa_location = loc_regs->find(CFA_REG);
if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
- log(0, "Attempt to set offset, but cfa is not set to a register.");
+ Log::Error("Attempt to set offset, but cfa is not set to a register.");
last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
return false;
}
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
index c4425bd..e9656e5 100644
--- a/libunwindstack/DwarfCfa.h
+++ b/libunwindstack/DwarfCfa.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_CFA_H
-#define _LIBUNWINDSTACK_DWARF_CFA_H
+#pragma once
#include <stdint.h>
@@ -270,5 +269,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h
index 635cefd..017568d 100644
--- a/libunwindstack/DwarfDebugFrame.h
+++ b/libunwindstack/DwarfDebugFrame.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
-#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
+#pragma once
#include <stdint.h>
@@ -46,5 +45,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H
diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h
index 7a41e45..93befd4 100644
--- a/libunwindstack/DwarfEhFrame.h
+++ b/libunwindstack/DwarfEhFrame.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_H
-#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H
+#pragma once
#include <stdint.h>
@@ -45,5 +44,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H
diff --git a/libunwindstack/DwarfEhFrameWithHdr.h b/libunwindstack/DwarfEhFrameWithHdr.h
index f7c010c..d074b7b 100644
--- a/libunwindstack/DwarfEhFrameWithHdr.h
+++ b/libunwindstack/DwarfEhFrameWithHdr.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
-#define _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
+#pragma once
#include <stdint.h>
@@ -82,5 +81,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_WITH_HDR_H
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
index 20db222..c9fbf24 100644
--- a/libunwindstack/DwarfEncoding.h
+++ b/libunwindstack/DwarfEncoding.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_ENCODING_H
-#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+#pragma once
#include <stdint.h>
@@ -47,5 +46,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
index ac9fd2d..2f33465 100644
--- a/libunwindstack/DwarfOp.h
+++ b/libunwindstack/DwarfOp.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_OP_H
-#define _LIBUNWINDSTACK_DWARF_OP_H
+#pragma once
#include <stdint.h>
@@ -140,5 +139,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 8a2997e..34d37b0 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -102,7 +102,8 @@
}
cie->cfa_instructions_end = memory_.cur_offset() + length64;
- cie->fde_address_encoding = DW_EH_PE_sdata8;
+ // TODO(b/192012848): This is wrong. We need to propagate pointer size here.
+ cie->fde_address_encoding = DW_EH_PE_udata8;
uint64_t cie_id;
if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
@@ -118,7 +119,8 @@
} else {
// 32 bit Cie
cie->cfa_instructions_end = memory_.cur_offset() + length32;
- cie->fde_address_encoding = DW_EH_PE_sdata4;
+ // TODO(b/192012848): This is wrong. We need to propagate pointer size here.
+ cie->fde_address_encoding = DW_EH_PE_udata4;
uint32_t cie_id;
if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
@@ -161,8 +163,13 @@
} while (aug_value != '\0');
if (cie->version == 4 || cie->version == 5) {
- // Skip the Address Size field since we only use it for validation.
- memory_.set_cur_offset(memory_.cur_offset() + 1);
+ char address_size;
+ if (!memory_.ReadBytes(&address_size, 1)) {
+ last_error_.code = DWARF_ERROR_MEMORY_INVALID;
+ last_error_.address = memory_.cur_offset();
+ return false;
+ }
+ cie->fde_address_encoding = address_size == 8 ? DW_EH_PE_udata8 : DW_EH_PE_udata4;
// Segment Size
if (!memory_.ReadBytes(&cie->segment_size, 1)) {
@@ -667,7 +674,7 @@
if (value64 == cie64_value_) {
entry_is_cie = true;
- cie_fde_encoding = DW_EH_PE_sdata8;
+ cie_fde_encoding = DW_EH_PE_udata8;
} else {
cie_offset = GetCieOffsetFromFde64(value64);
}
@@ -683,7 +690,7 @@
if (value32 == cie32_value_) {
entry_is_cie = true;
- cie_fde_encoding = DW_EH_PE_sdata4;
+ cie_fde_encoding = DW_EH_PE_udata4;
} else {
cie_offset = GetCieOffsetFromFde32(value32);
}
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 7f4142b..44db356 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -15,21 +15,26 @@
*/
#include <elf.h>
+#include <inttypes.h>
#include <string.h>
+#include <sys/mman.h>
#include <memory>
#include <mutex>
#include <string>
#include <utility>
-#define LOG_TAG "unwind"
-#include <log/log.h>
+#include <android-base/stringprintf.h>
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Log.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
+#include <unwindstack/SharedString.h>
+
+#include <android-base/stringprintf.h>
#include "ElfInterfaceArm.h"
#include "Symbols.h"
@@ -37,7 +42,7 @@
namespace unwindstack {
bool Elf::cache_enabled_;
-std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* Elf::cache_;
+std::unordered_map<std::string, std::unordered_map<uint64_t, std::shared_ptr<Elf>>>* Elf::cache_;
std::mutex* Elf::cache_lock_;
bool Elf::Init() {
@@ -302,7 +307,6 @@
interface.reset(new ElfInterface32(memory));
} else {
// Unsupported.
- ALOGI("32 bit elf that is neither arm nor x86 nor mips: e_machine = %d\n", e_machine);
return nullptr;
}
} else if (class_type_ == ELFCLASS64) {
@@ -320,8 +324,6 @@
arch_ = ARCH_MIPS64;
} else {
// Unsupported.
- ALOGI("64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = %d\n",
- e_machine);
return nullptr;
}
interface.reset(new ElfInterface64(memory));
@@ -351,7 +353,8 @@
void Elf::SetCachingEnabled(bool enable) {
if (!cache_enabled_ && enable) {
cache_enabled_ = true;
- cache_ = new std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>;
+ cache_ =
+ new std::unordered_map<std::string, std::unordered_map<uint64_t, std::shared_ptr<Elf>>>;
cache_lock_ = new std::mutex;
} else if (cache_enabled_ && !enable) {
cache_enabled_ = false;
@@ -369,58 +372,49 @@
}
void Elf::CacheAdd(MapInfo* info) {
- // If elf_offset != 0, then cache both name:offset and name.
- // The cached name is used to do lookups if multiple maps for the same
- // named elf file exist.
- // For example, if there are two maps boot.odex:1000 and boot.odex:2000
- // where each reference the entire boot.odex, the cache will properly
- // use the same cached elf object.
-
- if (info->offset() == 0 || info->elf_offset() != 0) {
- (*cache_)[info->name()] = std::make_pair(info->elf(), true);
+ if (!info->elf()->valid()) {
+ return;
}
-
- if (info->offset() != 0) {
- // The second element in the pair indicates whether elf_offset should
- // be set to offset when getting out of the cache.
- std::string key = std::string(info->name()) + ':' + std::to_string(info->offset());
- (*cache_)[key] = std::make_pair(info->elf(), info->elf_offset() != 0);
- }
-}
-
-bool Elf::CacheAfterCreateMemory(MapInfo* info) {
- if (info->name().empty() || info->offset() == 0 || info->elf_offset() == 0) {
- return false;
- }
-
- auto entry = cache_->find(info->name());
- if (entry == cache_->end()) {
- return false;
- }
-
- // In this case, the whole file is the elf, and the name has already
- // been cached. Add an entry at name:offset to get this directly out
- // of the cache next time.
- info->set_elf(entry->second.first);
- std::string key = std::string(info->name()) + ':' + std::to_string(info->offset());
- (*cache_)[key] = std::make_pair(info->elf(), true);
- return true;
+ (*cache_)[std::string(info->name())].emplace(info->elf_start_offset(), info->elf());
}
bool Elf::CacheGet(MapInfo* info) {
- std::string name(info->name());
- if (info->offset() != 0) {
- name += ':' + std::to_string(info->offset());
+ auto name_entry = cache_->find(std::string(info->name()));
+ if (name_entry == cache_->end()) {
+ return false;
}
- auto entry = cache_->find(name);
- if (entry != cache_->end()) {
- info->set_elf(entry->second.first);
- if (entry->second.second) {
- info->set_elf_offset(info->offset());
+ // First look to see if there is a zero offset entry, this indicates
+ // the whole elf is the file.
+ auto& offset_cache = name_entry->second;
+ uint64_t elf_start_offset = 0;
+ auto entry = offset_cache.find(elf_start_offset);
+ if (entry == offset_cache.end()) {
+ // Try and find using the current offset.
+ elf_start_offset = info->offset();
+ entry = offset_cache.find(elf_start_offset);
+ if (entry == offset_cache.end()) {
+ // If this is an execute map, then see if the previous read-only
+ // map is the start of the elf.
+ if (!(info->flags() & PROT_EXEC)) {
+ return false;
+ }
+ auto prev_map = info->GetPrevRealMap();
+ if (prev_map == nullptr || info->offset() <= prev_map->offset() ||
+ (prev_map->flags() != PROT_READ)) {
+ return false;
+ }
+ elf_start_offset = prev_map->offset();
+ entry = offset_cache.find(elf_start_offset);
+ if (entry == offset_cache.end()) {
+ return false;
+ }
}
- return true;
}
- return false;
+
+ info->set_elf(entry->second);
+ info->set_elf_start_offset(elf_start_offset);
+ info->set_elf_offset(info->offset() - elf_start_offset);
+ return true;
}
std::string Elf::GetBuildID(Memory* memory) {
@@ -441,4 +435,21 @@
return "";
}
+std::string Elf::GetPrintableBuildID(std::string& build_id) {
+ if (build_id.empty()) {
+ return "";
+ }
+ std::string printable_build_id;
+ for (const char& c : build_id) {
+ // Use %hhx to avoid sign extension on abis that have signed chars.
+ printable_build_id += android::base::StringPrintf("%02hhx", c);
+ }
+ return printable_build_id;
+}
+
+std::string Elf::GetPrintableBuildID() {
+ std::string build_id = GetBuildID();
+ return Elf::GetPrintableBuildID(build_id);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 8268ed7..0389198 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -21,10 +21,6 @@
#include <string>
#include <utility>
-#include <7zCrc.h>
-#include <Xz.h>
-#include <XzCrc64.h>
-
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/ElfInterface.h>
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 9bfad0b..6ee6dc9 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
-#define _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
+#pragma once
#include <elf.h>
#include <stdint.h>
@@ -94,5 +93,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_ELF_INTERFACE_ARM_H
diff --git a/libunwindstack/Global.cpp b/libunwindstack/Global.cpp
index b8adc5c..0183bd3 100644
--- a/libunwindstack/Global.cpp
+++ b/libunwindstack/Global.cpp
@@ -21,6 +21,8 @@
#include <string>
#include <vector>
+#include <android-base/file.h>
+
#include <unwindstack/Global.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
@@ -48,7 +50,7 @@
return false;
}
- const char* base_name = basename(name.c_str());
+ std::string base_name = android::base::Basename(name);
for (const std::string& lib : search_libs_) {
if (base_name == lib) {
return true;
@@ -77,8 +79,7 @@
// f3000-f4000 2000 rw- /system/lib/libc.so
MapInfo* map_zero = nullptr;
for (const auto& info : *maps) {
- if (info->offset() != 0 &&
- (info->flags() & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
+ if ((info->flags() & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE) &&
map_zero != nullptr && Searchable(info->name()) && info->name() == map_zero->name()) {
Elf* elf = map_zero->GetElf(memory_, arch());
uint64_t ptr;
diff --git a/libunwindstack/GlobalDebugImpl.h b/libunwindstack/GlobalDebugImpl.h
index db8068d..dcea0ef 100644
--- a/libunwindstack/GlobalDebugImpl.h
+++ b/libunwindstack/GlobalDebugImpl.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_GLOBAL_DEBUG_IMPL_H
-#define _LIBUNWINDSTACK_GLOBAL_DEBUG_IMPL_H
+#pragma once
#include <stdint.h>
#include <string.h>
@@ -435,5 +434,3 @@
}
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_GLOBAL_DEBUG_IMPL_H
diff --git a/libunwindstack/LocalUnwinder.cpp b/libunwindstack/LocalUnwinder.cpp
deleted file mode 100644
index 466dd68..0000000
--- a/libunwindstack/LocalUnwinder.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <pthread.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <unwindstack/Elf.h>
-#include <unwindstack/LocalUnwinder.h>
-#include <unwindstack/MapInfo.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
-#include <unwindstack/RegsGetLocal.h>
-
-namespace unwindstack {
-
-bool LocalUnwinder::Init() {
- pthread_rwlock_init(&maps_rwlock_, nullptr);
-
- // Create the maps.
- maps_.reset(new unwindstack::LocalUpdatableMaps());
- if (!maps_->Parse()) {
- maps_.reset();
- return false;
- }
-
- process_memory_ = unwindstack::Memory::CreateProcessMemoryThreadCached(getpid());
-
- return true;
-}
-
-bool LocalUnwinder::ShouldSkipLibrary(const std::string& map_name) {
- for (const std::string& skip_library : skip_libraries_) {
- if (skip_library == map_name) {
- return true;
- }
- }
- return false;
-}
-
-bool LocalUnwinder::Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames) {
- std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
- unwindstack::RegsGetLocal(regs.get());
- ArchEnum arch = regs->Arch();
-
- // Clear any cached data from previous unwinds.
- process_memory_->Clear();
-
- size_t num_frames = 0;
- bool adjust_pc = false;
- while (true) {
- uint64_t cur_pc = regs->pc();
- uint64_t cur_sp = regs->sp();
-
- MapInfo* map_info = maps_->Find(cur_pc);
- if (map_info == nullptr) {
- break;
- }
-
- Elf* elf = map_info->GetElf(process_memory_, arch);
- uint64_t rel_pc = elf->GetRelPc(cur_pc, map_info);
- uint64_t step_pc = rel_pc;
- uint64_t pc_adjustment;
- if (adjust_pc) {
- pc_adjustment = GetPcAdjustment(rel_pc, elf, arch);
- } else {
- pc_adjustment = 0;
- }
- step_pc -= pc_adjustment;
-
- bool finished = false;
- bool is_signal_frame = false;
- if (elf->StepIfSignalHandler(rel_pc, regs.get(), process_memory_.get())) {
- step_pc = rel_pc;
- } else if (!elf->Step(step_pc, regs.get(), process_memory_.get(), &finished,
- &is_signal_frame)) {
- finished = true;
- }
-
- // Skip any locations that are within this library.
- if (num_frames != 0 || !ShouldSkipLibrary(map_info->name())) {
- // Add frame information.
- SharedString func_name;
- uint64_t func_offset;
- if (elf->GetFunctionName(rel_pc, &func_name, &func_offset)) {
- frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment,
- func_name, func_offset);
- } else {
- frame_info->emplace_back(map_info, cur_pc - pc_adjustment, rel_pc - pc_adjustment, "", 0);
- }
- num_frames++;
- }
-
- if (finished || frame_info->size() == max_frames ||
- (cur_pc == regs->pc() && cur_sp == regs->sp())) {
- break;
- }
- adjust_pc = true;
- }
- return num_frames != 0;
-}
-
-} // namespace unwindstack
diff --git a/libunwindstack/Log.cpp b/libunwindstack/LogAndroid.cpp
similarity index 66%
rename from libunwindstack/Log.cpp
rename to libunwindstack/LogAndroid.cpp
index 3bf73e1..a2bfc39 100644
--- a/libunwindstack/Log.cpp
+++ b/libunwindstack/LogAndroid.cpp
@@ -32,45 +32,51 @@
namespace unwindstack {
-static bool g_print_to_stdout = false;
-
-void log_to_stdout(bool enable) {
- g_print_to_stdout = enable;
-}
+namespace Log {
// Send the data to the log.
-void log(uint8_t indent, const char* format, ...) {
+static void LogWithPriority(int priority, uint8_t indent, const char* format, va_list args) {
std::string real_format;
if (indent > 0) {
real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
} else {
real_format = format;
}
+ LOG_PRI_VA(priority, LOG_TAG, real_format.c_str(), args);
+}
+
+void Info(const char* format, ...) {
va_list args;
va_start(args, format);
- if (g_print_to_stdout) {
- real_format += '\n';
- vprintf(real_format.c_str(), args);
- } else {
- LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, real_format.c_str(), args);
- }
+ LogWithPriority(ANDROID_LOG_INFO, 0, format, args);
+ va_end(args);
+}
+
+void Info(uint8_t indent, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ LogWithPriority(ANDROID_LOG_INFO, indent, format, args);
+ va_end(args);
+}
+
+void Error(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ LogWithPriority(ANDROID_LOG_ERROR, 0, format, args);
va_end(args);
}
#if defined(__BIONIC__)
-void log_async_safe(const char* format, ...) {
- if (g_print_to_stdout) {
- // Printing to stdout is never async safe, so throw the message away.
- return;
- }
-
+void AsyncSafe(const char* format, ...) {
va_list args;
va_start(args, format);
async_safe_format_log_va_list(ANDROID_LOG_ERROR, "libunwindstack", format, args);
va_end(args);
}
#else
-void log_async_safe(const char*, ...) {}
+void AsyncSafe(const char*, ...) {}
#endif
+} // namespace Log
+
} // namespace unwindstack
diff --git a/libunwindstack/LogStdout.cpp b/libunwindstack/LogStdout.cpp
new file mode 100644
index 0000000..6ce06f8
--- /dev/null
+++ b/libunwindstack/LogStdout.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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 <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Log.h>
+
+namespace unwindstack {
+
+namespace Log {
+
+static void PrintToStdout(uint8_t indent, const char* format, va_list args) {
+ std::string real_format;
+ if (indent > 0) {
+ real_format = android::base::StringPrintf("%*s%s", 2 * indent, " ", format);
+ } else {
+ real_format = format;
+ }
+ real_format += '\n';
+
+ vprintf(real_format.c_str(), args);
+}
+
+void Info(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ PrintToStdout(0, format, args);
+ va_end(args);
+}
+
+void Info(uint8_t indent, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ PrintToStdout(indent, format, args);
+ va_end(args);
+}
+
+void Error(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ PrintToStdout(0, format, args);
+ va_end(args);
+}
+
+// Do nothing for async safe.
+void AsyncSafe(const char*, ...) {}
+
+} // namespace Log
+
+} // namespace unwindstack
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 8fb6f7e..6db1183 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -23,7 +23,7 @@
#include <mutex>
#include <string>
-#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
@@ -34,15 +34,55 @@
namespace unwindstack {
+bool MapInfo::ElfFileNotReadable() {
+ const std::string& map_name = name();
+ return memory_backed_elf() && !map_name.empty() && map_name[0] != '[' &&
+ !android::base::StartsWith(map_name, "/memfd:");
+}
+
+std::shared_ptr<MapInfo> MapInfo::GetPrevRealMap() {
+ if (name().empty()) {
+ return nullptr;
+ }
+
+ for (auto prev = prev_map(); prev != nullptr; prev = prev->prev_map()) {
+ if (!prev->IsBlank()) {
+ if (prev->name() == name()) {
+ return prev;
+ }
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+std::shared_ptr<MapInfo> MapInfo::GetNextRealMap() {
+ if (name().empty()) {
+ return nullptr;
+ }
+
+ for (auto next = next_map(); next != nullptr; next = next->next_map()) {
+ if (!next->IsBlank()) {
+ if (next->name() == name()) {
+ return next;
+ }
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
bool MapInfo::InitFileMemoryFromPreviousReadOnlyMap(MemoryFileAtOffset* memory) {
// One last attempt, see if the previous map is read-only with the
// same name and stretches across this map.
- if (prev_real_map() == nullptr || prev_real_map()->flags() != PROT_READ) {
+ auto prev_real_map = GetPrevRealMap();
+ if (prev_real_map == nullptr || prev_real_map->flags() != PROT_READ ||
+ prev_real_map->offset() >= offset()) {
return false;
}
- uint64_t map_size = end() - prev_real_map()->end();
- if (!memory->Init(name(), prev_real_map()->offset(), map_size)) {
+ uint64_t map_size = end() - prev_real_map->end();
+ if (!memory->Init(name(), prev_real_map->offset(), map_size)) {
return false;
}
@@ -51,16 +91,21 @@
return false;
}
- if (!memory->Init(name(), prev_real_map()->offset(), max_size)) {
+ if (!memory->Init(name(), prev_real_map->offset(), max_size)) {
return false;
}
- set_elf_offset(offset() - prev_real_map()->offset());
- set_elf_start_offset(prev_real_map()->offset());
+ set_elf_offset(offset() - prev_real_map->offset());
+ set_elf_start_offset(prev_real_map->offset());
return true;
}
Memory* MapInfo::GetFileMemory() {
+ // Fail on device maps.
+ if (flags() & MAPS_FLAGS_DEVICE_MAP) {
+ return nullptr;
+ }
+
std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
if (offset() == 0) {
if (memory->Init(name(), 0)) {
@@ -83,7 +128,11 @@
// and reinit to that size. This is needed because the dynamic linker
// only maps in a portion of the original elf, and never the symbol
// file data.
- uint64_t map_size = end() - start();
+ //
+ // For maps with MAPS_FLAGS_JIT_SYMFILE_MAP, the map range is for a JIT function,
+ // which can be smaller than elf header size. So make sure map_size is large enough
+ // to read elf header.
+ uint64_t map_size = std::max<uint64_t>(end() - start(), sizeof(ElfTypes64::Ehdr));
if (!memory->Init(name(), offset(), map_size)) {
return nullptr;
}
@@ -109,13 +158,6 @@
// No elf at offset, try to init as if the whole file is an elf.
if (memory->Init(name(), 0) && Elf::IsValidElf(memory.get())) {
set_elf_offset(offset());
- // Need to check how to set the elf start offset. If this map is not
- // the r-x map of a r-- map, then use the real offset value. Otherwise,
- // use 0.
- if (prev_real_map() == nullptr || prev_real_map()->offset() != 0 ||
- prev_real_map()->flags() != PROT_READ || prev_real_map()->name() != name()) {
- set_elf_start_offset(offset());
- }
return memory.release();
}
@@ -166,10 +208,13 @@
// option is used.
std::unique_ptr<MemoryRange> memory(new MemoryRange(process_memory, start(), end() - start(), 0));
if (Elf::IsValidElf(memory.get())) {
+ set_elf_start_offset(offset());
+
+ auto next_real_map = GetNextRealMap();
+
// Might need to peek at the next map to create a memory object that
// includes that map too.
- if (offset() != 0 || name().empty() || next_real_map() == nullptr ||
- offset() >= next_real_map()->offset() || next_real_map()->name() != name()) {
+ if (offset() != 0 || next_real_map == nullptr || offset() >= next_real_map->offset()) {
return memory.release();
}
@@ -179,95 +224,103 @@
// be discarded.
MemoryRanges* ranges = new MemoryRanges;
ranges->Insert(new MemoryRange(process_memory, start(), end() - start(), 0));
- ranges->Insert(new MemoryRange(process_memory, next_real_map()->start(),
- next_real_map()->end() - next_real_map()->start(),
- next_real_map()->offset() - offset()));
+ ranges->Insert(new MemoryRange(process_memory, next_real_map->start(),
+ next_real_map->end() - next_real_map->start(),
+ next_real_map->offset() - offset()));
return ranges;
}
+ auto prev_real_map = GetPrevRealMap();
+
// Find the read-only map by looking at the previous map. The linker
// doesn't guarantee that this invariant will always be true. However,
// if that changes, there is likely something else that will change and
// break something.
- if (offset() == 0 || name().empty() || prev_real_map() == nullptr ||
- prev_real_map()->name() != name() || prev_real_map()->offset() >= offset()) {
+ if (offset() == 0 || prev_real_map == nullptr || prev_real_map->offset() >= offset()) {
set_memory_backed_elf(false);
return nullptr;
}
// Make sure that relative pc values are corrected properly.
- set_elf_offset(offset() - prev_real_map()->offset());
+ set_elf_offset(offset() - prev_real_map->offset());
// Use this as the elf start offset, otherwise, you always get offsets into
// the r-x section, which is not quite the right information.
- set_elf_start_offset(prev_real_map()->offset());
+ set_elf_start_offset(prev_real_map->offset());
- MemoryRanges* ranges = new MemoryRanges;
- ranges->Insert(new MemoryRange(process_memory, prev_real_map()->start(),
- prev_real_map()->end() - prev_real_map()->start(), 0));
- ranges->Insert(new MemoryRange(process_memory, start(), end() - start(), elf_offset()));
-
- return ranges;
+ std::unique_ptr<MemoryRanges> ranges(new MemoryRanges);
+ if (!ranges->Insert(new MemoryRange(process_memory, prev_real_map->start(),
+ prev_real_map->end() - prev_real_map->start(), 0))) {
+ return nullptr;
+ }
+ if (!ranges->Insert(new MemoryRange(process_memory, start(), end() - start(), elf_offset()))) {
+ return nullptr;
+ }
+ return ranges.release();
}
-Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
- {
- // Make sure no other thread is trying to add the elf to this map.
- std::lock_guard<std::mutex> guard(elf_mutex());
+class ScopedElfCacheLock {
+ public:
+ ScopedElfCacheLock() {
+ if (Elf::CachingEnabled()) Elf::CacheLock();
+ }
+ ~ScopedElfCacheLock() {
+ if (Elf::CachingEnabled()) Elf::CacheUnlock();
+ }
+};
- if (elf().get() != nullptr) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch) {
+ // Make sure no other thread is trying to add the elf to this map.
+ std::lock_guard<std::mutex> guard(elf_mutex());
+
+ if (elf().get() != nullptr) {
+ return elf().get();
+ }
+
+ ScopedElfCacheLock elf_cache_lock;
+ if (Elf::CachingEnabled() && !name().empty()) {
+ if (Elf::CacheGet(this)) {
return elf().get();
}
+ }
- bool locked = false;
- if (Elf::CachingEnabled() && !name().empty()) {
- Elf::CacheLock();
- locked = true;
- if (Elf::CacheGet(this)) {
- Elf::CacheUnlock();
- return elf().get();
- }
- }
-
- Memory* memory = CreateMemory(process_memory);
- if (locked) {
- if (Elf::CacheAfterCreateMemory(this)) {
- delete memory;
- Elf::CacheUnlock();
- return elf().get();
- }
- }
- elf().reset(new Elf(memory));
- // If the init fails, keep the elf around as an invalid object so we
- // don't try to reinit the object.
- elf()->Init();
- if (elf()->valid() && expected_arch != elf()->arch()) {
- // Make the elf invalid, mismatch between arch and expected arch.
- elf()->Invalidate();
- }
-
- if (locked) {
- Elf::CacheAdd(this);
- Elf::CacheUnlock();
- }
+ elf().reset(new Elf(CreateMemory(process_memory)));
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ elf()->Init();
+ if (elf()->valid() && expected_arch != elf()->arch()) {
+ // Make the elf invalid, mismatch between arch and expected arch.
+ elf()->Invalidate();
}
if (!elf()->valid()) {
set_elf_start_offset(offset());
- } else if (prev_real_map() != nullptr && elf_start_offset() != offset() &&
- prev_real_map()->offset() == elf_start_offset() && prev_real_map()->name() == name()) {
+ } else if (auto prev_real_map = GetPrevRealMap(); prev_real_map != nullptr &&
+ prev_real_map->flags() == PROT_READ &&
+ prev_real_map->offset() < offset()) {
// If there is a read-only map then a read-execute map that represents the
// same elf object, make sure the previous map is using the same elf
- // object if it hasn't already been set.
- std::lock_guard<std::mutex> guard(prev_real_map()->elf_mutex());
- if (prev_real_map()->elf().get() == nullptr) {
- prev_real_map()->set_elf(elf());
- prev_real_map()->set_memory_backed_elf(memory_backed_elf());
- } else {
+ // object if it hasn't already been set. Locking this should not result
+ // in a deadlock as long as the invariant that the code only ever tries
+ // to lock the previous real map holds true.
+ std::lock_guard<std::mutex> guard(prev_real_map->elf_mutex());
+ if (prev_real_map->elf() == nullptr) {
+ // Need to verify if the map is the previous read-only map.
+ prev_real_map->set_elf(elf());
+ prev_real_map->set_memory_backed_elf(memory_backed_elf());
+ prev_real_map->set_elf_start_offset(elf_start_offset());
+ prev_real_map->set_elf_offset(prev_real_map->offset() - elf_start_offset());
+ } else if (prev_real_map->elf_start_offset() == elf_start_offset()) {
// Discard this elf, and use the elf from the previous map instead.
- set_elf(prev_real_map()->elf());
+ set_elf(prev_real_map->elf());
}
}
+
+ // Cache the elf only after all of the above checks since we might
+ // discard the original elf we created.
+ if (Elf::CachingEnabled()) {
+ Elf::CacheAdd(this);
+ }
return elf().get();
}
@@ -284,25 +337,31 @@
return elf()->GetFunctionName(addr, name, func_offset);
}
-uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
- int64_t cur_load_bias = load_bias().load();
- if (cur_load_bias != INT64_MAX) {
+uint64_t MapInfo::GetLoadBias() {
+ uint64_t cur_load_bias = load_bias().load();
+ if (cur_load_bias != UINT64_MAX) {
return cur_load_bias;
}
- {
- // Make sure no other thread is trying to add the elf to this map.
- std::lock_guard<std::mutex> guard(elf_mutex());
- if (elf() != nullptr) {
- if (elf()->valid()) {
- cur_load_bias = elf()->GetLoadBias();
- set_load_bias(cur_load_bias);
- return cur_load_bias;
- } else {
- set_load_bias(0);
- return 0;
- }
- }
+ Elf* elf_obj = GetElfObj();
+ if (elf_obj == nullptr) {
+ return UINT64_MAX;
+ }
+
+ if (elf_obj->valid()) {
+ cur_load_bias = elf_obj->GetLoadBias();
+ set_load_bias(cur_load_bias);
+ return cur_load_bias;
+ }
+
+ set_load_bias(0);
+ return 0;
+}
+
+uint64_t MapInfo::GetLoadBias(const std::shared_ptr<Memory>& process_memory) {
+ uint64_t cur_load_bias = GetLoadBias();
+ if (cur_load_bias != UINT64_MAX) {
+ return cur_load_bias;
}
// Call lightweight static function that will only read enough of the
@@ -321,6 +380,23 @@
}
}
+std::string MapInfo::GetFullName() {
+ Elf* elf_obj = GetElfObj();
+ if (elf_obj == nullptr || elf_start_offset() == 0 || name().empty()) {
+ return name();
+ }
+
+ std::string soname = elf_obj->GetSoname();
+ if (soname.empty()) {
+ return name();
+ }
+
+ std::string full_name(name());
+ full_name += '!';
+ full_name += soname;
+ return full_name;
+}
+
SharedString MapInfo::GetBuildID() {
SharedString* id = build_id().load();
if (id != nullptr) {
@@ -331,12 +407,8 @@
// time it should be detected and only one thread should win and
// save the data.
- // Now need to see if the elf object exists.
- // Make sure no other thread is trying to add the elf to this map.
- elf_mutex().lock();
- Elf* elf_obj = elf().get();
- elf_mutex().unlock();
std::string result;
+ Elf* elf_obj = GetElfObj();
if (elf_obj != nullptr) {
result = elf_obj->GetBuildID();
} else {
@@ -382,15 +454,7 @@
std::string MapInfo::GetPrintableBuildID() {
std::string raw_build_id = GetBuildID();
- if (raw_build_id.empty()) {
- return "";
- }
- std::string printable_build_id;
- for (const char& c : raw_build_id) {
- // Use %hhx to avoid sign extension on abis that have signed chars.
- printable_build_id += android::base::StringPrintf("%02hhx", c);
- }
- return printable_build_id;
+ return Elf::GetPrintableBuildID(raw_build_id);
}
} // namespace unwindstack
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 7a9e2e9..9527176 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -39,7 +39,7 @@
namespace unwindstack {
-MapInfo* Maps::Find(uint64_t pc) {
+std::shared_ptr<MapInfo> Maps::Find(uint64_t pc) {
if (maps_.empty()) {
return nullptr;
}
@@ -49,7 +49,7 @@
size_t index = (first + last) / 2;
const auto& cur = maps_[index];
if (pc >= cur->start() && pc < cur->end()) {
- return cur.get();
+ return cur;
} else if (pc < cur->start()) {
last = index;
} else {
@@ -60,8 +60,7 @@
}
bool Maps::Parse() {
- MapInfo* prev_map = nullptr;
- MapInfo* prev_real_map = nullptr;
+ std::shared_ptr<MapInfo> prev_map;
return android::procinfo::ReadMapFile(GetMapsFile(),
[&](const android::procinfo::MapInfo& mapinfo) {
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
@@ -70,52 +69,53 @@
strncmp(mapinfo.name.c_str() + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.emplace_back(new MapInfo(prev_map, prev_real_map, mapinfo.start, mapinfo.end,
- mapinfo.pgoff, flags, mapinfo.name));
- prev_map = maps_.back().get();
- if (!prev_map->IsBlank()) {
- prev_real_map = prev_map;
- }
+ maps_.emplace_back(
+ MapInfo::Create(prev_map, mapinfo.start, mapinfo.end, mapinfo.pgoff, flags, mapinfo.name));
+ prev_map = maps_.back();
});
}
void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
- const std::string& name, uint64_t load_bias) {
- MapInfo* prev_map = maps_.empty() ? nullptr : maps_.back().get();
- MapInfo* prev_real_map = prev_map;
- while (prev_real_map != nullptr && prev_real_map->IsBlank()) {
- prev_real_map = prev_real_map->prev_map();
- }
+ const std::string& name) {
+ std::shared_ptr<MapInfo> prev_map(maps_.empty() ? nullptr : maps_.back());
+ auto map_info = MapInfo::Create(prev_map, start, end, offset, flags, name);
+ maps_.emplace_back(std::move(map_info));
+}
- auto map_info =
- std::make_unique<MapInfo>(prev_map, prev_real_map, start, end, offset, flags, name);
+void Maps::Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const std::string& name, uint64_t load_bias) {
+ std::shared_ptr<MapInfo> prev_map(maps_.empty() ? nullptr : maps_.back());
+ auto map_info = MapInfo::Create(prev_map, start, end, offset, flags, name);
map_info->set_load_bias(load_bias);
maps_.emplace_back(std::move(map_info));
}
void Maps::Sort() {
+ if (maps_.empty()) {
+ return;
+ }
+
std::sort(maps_.begin(), maps_.end(),
- [](const std::unique_ptr<MapInfo>& a, const std::unique_ptr<MapInfo>& b) {
+ [](const std::shared_ptr<MapInfo>& a, const std::shared_ptr<MapInfo>& b) {
return a->start() < b->start();
});
- // Set the prev_map values on the info objects.
- MapInfo* prev_map = nullptr;
- MapInfo* prev_real_map = nullptr;
- for (const auto& map_info : maps_) {
+ // Set prev_map and next_map on the info objects.
+ std::shared_ptr<MapInfo> prev_map;
+ // Set the last next_map to nullptr.
+ maps_.back()->set_next_map(prev_map);
+ for (auto& map_info : maps_) {
map_info->set_prev_map(prev_map);
- map_info->set_prev_real_map(prev_real_map);
- prev_map = map_info.get();
- if (!prev_map->IsBlank()) {
- prev_real_map = prev_map;
+ if (prev_map) {
+ prev_map->set_next_map(map_info);
}
+ prev_map = map_info;
}
}
bool BufferMaps::Parse() {
std::string content(buffer_);
- MapInfo* prev_map = nullptr;
- MapInfo* prev_real_map = nullptr;
+ std::shared_ptr<MapInfo> prev_map;
return android::procinfo::ReadMapFileContent(
&content[0], [&](const android::procinfo::MapInfo& mapinfo) {
// Mark a device map in /dev/ and not in /dev/ashmem/ specially.
@@ -124,12 +124,9 @@
strncmp(mapinfo.name.c_str() + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
- maps_.emplace_back(new MapInfo(prev_map, prev_real_map, mapinfo.start, mapinfo.end,
- mapinfo.pgoff, flags, mapinfo.name));
- prev_map = maps_.back().get();
- if (!prev_map->IsBlank()) {
- prev_real_map = prev_map;
- }
+ maps_.emplace_back(MapInfo::Create(prev_map, mapinfo.start, mapinfo.end, mapinfo.pgoff,
+ flags, mapinfo.name));
+ prev_map = maps_.back();
});
}
@@ -145,9 +142,9 @@
pthread_rwlock_init(&maps_rwlock_, nullptr);
}
-MapInfo* LocalUpdatableMaps::Find(uint64_t pc) {
+std::shared_ptr<MapInfo> LocalUpdatableMaps::Find(uint64_t pc) {
pthread_rwlock_rdlock(&maps_rwlock_);
- MapInfo* map_info = Maps::Find(pc);
+ std::shared_ptr<MapInfo> map_info = Maps::Find(pc);
pthread_rwlock_unlock(&maps_rwlock_);
if (map_info == nullptr) {
@@ -191,13 +188,22 @@
auto& info = maps_[old_map_idx];
if (start == info->start() && end == info->end() && flags == info->flags() &&
name == info->name()) {
- // No need to check
search_map_idx = old_map_idx + 1;
- if (new_map_idx + 1 < maps_.size()) {
- maps_[new_map_idx + 1]->set_prev_map(info.get());
- maps_[new_map_idx + 1]->set_prev_real_map(info->IsBlank() ? info->prev_real_map()
- : info.get());
+ // Since we are throwing away a map from the new list, need to
+ // adjust the next/prev pointers in the old map entry.
+ auto prev = new_map_info->prev_map();
+ auto next = new_map_info->next_map();
+ info->set_prev_map(prev);
+ info->set_next_map(next);
+
+ // Fix up the pointers in the prev and next entries.
+ if (prev != nullptr) {
+ prev->set_next_map(info);
}
+ if (next != nullptr) {
+ next->set_prev_map(info);
+ }
+
maps_[new_map_idx] = nullptr;
num_deleted_new_entries++;
break;
@@ -210,7 +216,9 @@
// Never delete these maps, they may be in use. The assumption is
// that there will only every be a handful of these so waiting
// to destroy them is not too expensive.
- saved_maps_.emplace_back(std::move(info));
+ // Since these are all shared_ptrs, we can just remove the references.
+ // Any code still holding on to the pointer, will still have a
+ // valid pointer after this.
search_map_idx = old_map_idx + 1;
maps_[old_map_idx] = nullptr;
num_deleted_old_entries++;
@@ -220,9 +228,7 @@
}
}
- // Now move out any of the maps that never were found.
for (size_t i = search_map_idx; i < last_map_idx; i++) {
- saved_maps_.emplace_back(std::move(maps_[i]));
maps_[i] = nullptr;
num_deleted_old_entries++;
}
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index e1fe0e1..5cfe2f0 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -25,21 +25,17 @@
#include <sys/uio.h>
#include <unistd.h>
-#include <7zCrc.h>
-#include <Xz.h>
-#include <XzCrc64.h>
-
#include <algorithm>
#include <memory>
#include <mutex>
#include <optional>
+#include <string>
#include <android-base/unique_fd.h>
#include <unwindstack/Log.h>
#include <unwindstack/Memory.h>
-#include "Check.h"
#include "MemoryBuffer.h"
#include "MemoryCache.h"
#include "MemoryFileAtOffset.h"
@@ -48,16 +44,9 @@
#include "MemoryOfflineBuffer.h"
#include "MemoryRange.h"
#include "MemoryRemote.h"
-#include "MemoryXz.h"
namespace unwindstack {
-// Statistics (used only for optional debug log messages).
-static constexpr bool kLogMemoryXzUsage = false;
-std::atomic_size_t MemoryXz::total_used_ = 0;
-std::atomic_size_t MemoryXz::total_size_ = 0;
-std::atomic_size_t MemoryXz::total_open_ = 0;
-
static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) {
// Split up the remote read across page boundaries.
@@ -380,7 +369,7 @@
return memory_->Read(read_addr, dst, read_length);
}
-void MemoryRanges::Insert(MemoryRange* memory) {
+bool MemoryRanges::Insert(MemoryRange* memory) {
uint64_t last_addr;
if (__builtin_add_overflow(memory->offset(), memory->length(), &last_addr)) {
// This should never happen in the real world. However, it is possible
@@ -389,7 +378,12 @@
// value.
last_addr = UINT64_MAX;
}
- maps_.emplace(last_addr, memory);
+ auto entry = maps_.try_emplace(last_addr, memory);
+ if (entry.second) {
+ return true;
+ }
+ delete memory;
+ return false;
}
size_t MemoryRanges::Read(uint64_t addr, void* dst, size_t size) {
@@ -421,6 +415,16 @@
return true;
}
+bool MemoryOffline::Init(const std::string& file, uint64_t offset, uint64_t start, uint64_t size) {
+ auto memory_file = std::make_shared<MemoryFileAtOffset>();
+ if (!memory_file->Init(file, offset)) {
+ return false;
+ }
+
+ memory_ = std::make_unique<MemoryRange>(memory_file, 0, size, start);
+ return true;
+}
+
size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
if (!memory_) {
return 0;
@@ -531,7 +535,7 @@
CacheDataType* cache = reinterpret_cast<CacheDataType*>(memory);
delete cache;
}) != 0) {
- log_async_safe("Failed to create pthread key.");
+ Log::AsyncSafe("Failed to create pthread key.");
thread_cache_.reset();
}
}
@@ -559,6 +563,10 @@
}
void MemoryThreadCache::Clear() {
+ if (!thread_cache_) {
+ return;
+ }
+
CacheDataType* cache = reinterpret_cast<CacheDataType*>(pthread_getspecific(*thread_cache_));
if (cache != nullptr) {
delete cache;
@@ -566,229 +574,4 @@
}
}
-MemoryXz::MemoryXz(Memory* memory, uint64_t addr, uint64_t size, const std::string& name)
- : compressed_memory_(memory), compressed_addr_(addr), compressed_size_(size), name_(name) {
- total_open_ += 1;
-}
-
-bool MemoryXz::Init() {
- static std::once_flag crc_initialized;
- std::call_once(crc_initialized, []() {
- CrcGenerateTable();
- Crc64GenerateTable();
- });
- if (compressed_size_ >= kMaxCompressedSize) {
- return false;
- }
- if (!ReadBlocks()) {
- return false;
- }
-
- // All blocks (except the last one) must have the same power-of-2 size.
- if (blocks_.size() > 1) {
- size_t block_size_log2 = __builtin_ctz(blocks_.front().decompressed_size);
- auto correct_size = [=](XzBlock& b) { return b.decompressed_size == (1 << block_size_log2); };
- if (std::all_of(blocks_.begin(), std::prev(blocks_.end()), correct_size) &&
- blocks_.back().decompressed_size <= (1 << block_size_log2)) {
- block_size_log2_ = block_size_log2;
- } else {
- // Inconsistent block-sizes. Decompress and merge everything now.
- std::unique_ptr<uint8_t[]> data(new uint8_t[size_]);
- size_t offset = 0;
- for (XzBlock& block : blocks_) {
- if (!Decompress(&block)) {
- return false;
- }
- memcpy(data.get() + offset, block.decompressed_data.get(), block.decompressed_size);
- offset += block.decompressed_size;
- }
- blocks_.clear();
- blocks_.push_back(XzBlock{
- .decompressed_data = std::move(data),
- .decompressed_size = size_,
- });
- block_size_log2_ = 31; // Because 32 bits is too big (shift right by 32 is not allowed).
- }
- }
-
- return true;
-}
-
-MemoryXz::~MemoryXz() {
- total_used_ -= used_;
- total_size_ -= size_;
- total_open_ -= 1;
-}
-
-size_t MemoryXz::Read(uint64_t addr, void* buffer, size_t size) {
- if (addr >= size_) {
- return 0; // Read past the end.
- }
- uint8_t* dst = reinterpret_cast<uint8_t*>(buffer); // Position in the output buffer.
- for (size_t i = addr >> block_size_log2_; i < blocks_.size(); i++) {
- XzBlock* block = &blocks_[i];
- if (block->decompressed_data == nullptr) {
- if (!Decompress(block)) {
- break;
- }
- }
- size_t offset = (addr - (i << block_size_log2_)); // Start inside the block.
- size_t copy_bytes = std::min<size_t>(size, block->decompressed_size - offset);
- memcpy(dst, block->decompressed_data.get() + offset, copy_bytes);
- dst += copy_bytes;
- addr += copy_bytes;
- size -= copy_bytes;
- if (size == 0) {
- break;
- }
- }
- return dst - reinterpret_cast<uint8_t*>(buffer);
-}
-
-bool MemoryXz::ReadBlocks() {
- static ISzAlloc alloc;
- alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
- alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
-
- // Read the compressed data, so we can quickly scan through the headers.
- std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[compressed_size_]);
- if (compressed_data.get() == nullptr) {
- return false;
- }
- if (!compressed_memory_->ReadFully(compressed_addr_, compressed_data.get(), compressed_size_)) {
- return false;
- }
-
- // Implement the required interface for communication
- // (written in C so we can not use virtual methods or member functions).
- struct XzLookInStream : public ILookInStream, public ICompressProgress {
- static SRes LookImpl(const ILookInStream* p, const void** buf, size_t* size) {
- auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
- *buf = ctx->data + ctx->offset;
- *size = std::min(*size, ctx->size - ctx->offset);
- return SZ_OK;
- }
- static SRes SkipImpl(const ILookInStream* p, size_t len) {
- auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
- ctx->offset += len;
- return SZ_OK;
- }
- static SRes ReadImpl(const ILookInStream* p, void* buf, size_t* size) {
- auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
- *size = std::min(*size, ctx->size - ctx->offset);
- memcpy(buf, ctx->data + ctx->offset, *size);
- return SZ_OK;
- }
- static SRes SeekImpl(const ILookInStream* p, Int64* pos, ESzSeek origin) {
- auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
- switch (origin) {
- case SZ_SEEK_SET:
- ctx->offset = *pos;
- break;
- case SZ_SEEK_CUR:
- ctx->offset += *pos;
- break;
- case SZ_SEEK_END:
- ctx->offset = ctx->size + *pos;
- break;
- }
- *pos = ctx->offset;
- return SZ_OK;
- }
- static SRes ProgressImpl(const ICompressProgress*, UInt64, UInt64) { return SZ_OK; }
- size_t offset;
- uint8_t* data;
- size_t size;
- };
- XzLookInStream callbacks;
- callbacks.Look = &XzLookInStream::LookImpl;
- callbacks.Skip = &XzLookInStream::SkipImpl;
- callbacks.Read = &XzLookInStream::ReadImpl;
- callbacks.Seek = &XzLookInStream::SeekImpl;
- callbacks.Progress = &XzLookInStream::ProgressImpl;
- callbacks.offset = 0;
- callbacks.data = compressed_data.get();
- callbacks.size = compressed_size_;
-
- // Iterate over the internal XZ blocks without decompressing them.
- CXzs xzs;
- Xzs_Construct(&xzs);
- Int64 end_offset = compressed_size_;
- if (Xzs_ReadBackward(&xzs, &callbacks, &end_offset, &callbacks, &alloc) == SZ_OK) {
- blocks_.reserve(Xzs_GetNumBlocks(&xzs));
- size_t dst_offset = 0;
- for (int s = xzs.num - 1; s >= 0; s--) {
- const CXzStream& stream = xzs.streams[s];
- size_t src_offset = stream.startOffset + XZ_STREAM_HEADER_SIZE;
- for (size_t b = 0; b < stream.numBlocks; b++) {
- const CXzBlockSizes& block = stream.blocks[b];
- blocks_.push_back(XzBlock{
- .decompressed_data = nullptr, // Lazy allocation and decompression.
- .decompressed_size = static_cast<uint32_t>(block.unpackSize),
- .compressed_offset = static_cast<uint32_t>(src_offset),
- .compressed_size = static_cast<uint32_t>((block.totalSize + 3) & ~3u),
- .stream_flags = stream.flags,
- });
- dst_offset += blocks_.back().decompressed_size;
- src_offset += blocks_.back().compressed_size;
- }
- }
- size_ = dst_offset;
- total_size_ += dst_offset;
- }
- Xzs_Free(&xzs, &alloc);
- return !blocks_.empty();
-}
-
-bool MemoryXz::Decompress(XzBlock* block) {
- static ISzAlloc alloc;
- alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
- alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
-
- // Read the compressed data for this block.
- std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[block->compressed_size]);
- if (compressed_data.get() == nullptr) {
- return false;
- }
- if (!compressed_memory_->ReadFully(compressed_addr_ + block->compressed_offset,
- compressed_data.get(), block->compressed_size)) {
- return false;
- }
-
- // Allocate decompressed memory.
- std::unique_ptr<uint8_t[]> decompressed_data(new uint8_t[block->decompressed_size]);
- if (decompressed_data == nullptr) {
- return false;
- }
-
- // Decompress.
- CXzUnpacker state{};
- XzUnpacker_Construct(&state, &alloc);
- state.streamFlags = block->stream_flags;
- XzUnpacker_PrepareToRandomBlockDecoding(&state);
- size_t decompressed_size = block->decompressed_size;
- size_t compressed_size = block->compressed_size;
- ECoderStatus status;
- XzUnpacker_SetOutBuf(&state, decompressed_data.get(), decompressed_size);
- int return_val =
- XzUnpacker_Code(&state, /*decompressed_data=*/nullptr, &decompressed_size,
- compressed_data.get(), &compressed_size, true, CODER_FINISH_END, &status);
- XzUnpacker_Free(&state);
- if (return_val != SZ_OK || status != CODER_STATUS_FINISHED_WITH_MARK) {
- log(0, "Can not decompress \"%s\"", name_.c_str());
- return false;
- }
-
- used_ += block->decompressed_size;
- total_used_ += block->decompressed_size;
- if (kLogMemoryXzUsage) {
- log(0, "decompressed memory: %zi%% of %ziKB (%zi files), %i%% of %iKB (%s)",
- 100 * total_used_ / total_size_, total_size_ / 1024, total_open_.load(),
- 100 * used_ / size_, size_ / 1024, name_.c_str());
- }
-
- block->decompressed_data = std::move(decompressed_data);
- return true;
-}
-
} // namespace unwindstack
diff --git a/libunwindstack/MemoryBuffer.h b/libunwindstack/MemoryBuffer.h
index 24609f4..a5b5743 100644
--- a/libunwindstack/MemoryBuffer.h
+++ b/libunwindstack/MemoryBuffer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_BUFFER_H
-#define _LIBUNWINDSTACK_MEMORY_BUFFER_H
+#pragma once
#include <stdint.h>
@@ -56,5 +55,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_BUFFER_H
diff --git a/libunwindstack/MemoryCache.h b/libunwindstack/MemoryCache.h
index 523a4a1..de5e9a0 100644
--- a/libunwindstack/MemoryCache.h
+++ b/libunwindstack/MemoryCache.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_CACHE_H
-#define _LIBUNWINDSTACK_MEMORY_CACHE_H
+#pragma once
#include <pthread.h>
#include <stdint.h>
@@ -91,5 +90,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_CACHE_H
diff --git a/libunwindstack/MemoryFileAtOffset.h b/libunwindstack/MemoryFileAtOffset.h
index 9949f26..90cf00b 100644
--- a/libunwindstack/MemoryFileAtOffset.h
+++ b/libunwindstack/MemoryFileAtOffset.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
-#define _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
+#pragma once
#include <stdint.h>
@@ -45,5 +44,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_FILE_AT_OFFSET_H
diff --git a/libunwindstack/MemoryLocal.h b/libunwindstack/MemoryLocal.h
index a5c7a48..f98c9cb 100644
--- a/libunwindstack/MemoryLocal.h
+++ b/libunwindstack/MemoryLocal.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_LOCAL_H
-#define _LIBUNWINDSTACK_MEMORY_LOCAL_H
+#pragma once
#include <stdint.h>
@@ -33,5 +32,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_LOCAL_H
diff --git a/libunwindstack/MemoryMte.cpp b/libunwindstack/MemoryMte.cpp
index 679f413..3841744 100644
--- a/libunwindstack/MemoryMte.cpp
+++ b/libunwindstack/MemoryMte.cpp
@@ -17,8 +17,10 @@
#include <sys/ptrace.h>
#include <sys/uio.h>
-#ifdef __BIONIC__
+#if defined(__BIONIC__)
#include <bionic/mte.h>
+#else
+#define mte_supported() false
#endif
#include "MemoryLocal.h"
@@ -27,7 +29,7 @@
namespace unwindstack {
long MemoryRemote::ReadTag(uint64_t addr) {
-#if defined(__aarch64__)
+#if defined(PTRACE_PEEKMTETAGS) || defined(PT_PEEKMTETAGS)
char tag;
iovec iov = {&tag, 1};
if (ptrace(PTRACE_PEEKMTETAGS, pid_, reinterpret_cast<void*>(addr), &iov) != 0 ||
diff --git a/libunwindstack/MemoryOffline.h b/libunwindstack/MemoryOffline.h
index 789f1a2..024e111 100644
--- a/libunwindstack/MemoryOffline.h
+++ b/libunwindstack/MemoryOffline.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_H
-#define _LIBUNWINDSTACK_MEMORY_OFFLINE_H
+#pragma once
#include <stdint.h>
@@ -36,6 +35,8 @@
bool Init(const std::string& file, uint64_t offset);
+ bool Init(const std::string& file, uint64_t offset, uint64_t start, uint64_t size);
+
size_t Read(uint64_t addr, void* dst, size_t size) override;
private:
@@ -56,5 +57,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_H
diff --git a/libunwindstack/MemoryOfflineBuffer.h b/libunwindstack/MemoryOfflineBuffer.h
index 64c49a1..d77008e 100644
--- a/libunwindstack/MemoryOfflineBuffer.h
+++ b/libunwindstack/MemoryOfflineBuffer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
-#define _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
+#pragma once
#include <stdint.h>
@@ -39,5 +38,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_OFFLINE_BUFFER_H
diff --git a/libunwindstack/MemoryRange.h b/libunwindstack/MemoryRange.h
index 3b4ab5c..b789ee3 100644
--- a/libunwindstack/MemoryRange.h
+++ b/libunwindstack/MemoryRange.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_RANGE_H
-#define _LIBUNWINDSTACK_MEMORY_RANGE_H
+#pragma once
#include <stdint.h>
@@ -53,7 +52,7 @@
MemoryRanges() = default;
virtual ~MemoryRanges() = default;
- void Insert(MemoryRange* memory);
+ bool Insert(MemoryRange* memory);
size_t Read(uint64_t addr, void* dst, size_t size) override;
@@ -62,5 +61,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_RANGE_H
diff --git a/libunwindstack/MemoryRemote.h b/libunwindstack/MemoryRemote.h
index dd09c88..563e5b7 100644
--- a/libunwindstack/MemoryRemote.h
+++ b/libunwindstack/MemoryRemote.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_REMOTE_H
-#define _LIBUNWINDSTACK_MEMORY_REMOTE_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -42,5 +41,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_REMOTE_H
diff --git a/libunwindstack/MemoryXz.cpp b/libunwindstack/MemoryXz.cpp
new file mode 100644
index 0000000..9e91a07
--- /dev/null
+++ b/libunwindstack/MemoryXz.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2021 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+#include <7zCrc.h>
+#include <Xz.h>
+#include <XzCrc64.h>
+
+#include <unwindstack/Log.h>
+
+#include "MemoryXz.h"
+
+namespace unwindstack {
+
+// Statistics (used only for optional debug log messages).
+static constexpr bool kLogMemoryXzUsage = false;
+std::atomic_size_t MemoryXz::total_used_ = 0;
+std::atomic_size_t MemoryXz::total_size_ = 0;
+std::atomic_size_t MemoryXz::total_open_ = 0;
+
+MemoryXz::MemoryXz(Memory* memory, uint64_t addr, uint64_t size, const std::string& name)
+ : compressed_memory_(memory), compressed_addr_(addr), compressed_size_(size), name_(name) {
+ total_open_ += 1;
+}
+
+bool MemoryXz::Init() {
+ static std::once_flag crc_initialized;
+ std::call_once(crc_initialized, []() {
+ CrcGenerateTable();
+ Crc64GenerateTable();
+ });
+ if (compressed_size_ >= kMaxCompressedSize) {
+ return false;
+ }
+ if (!ReadBlocks()) {
+ return false;
+ }
+
+ // All blocks (except the last one) must have the same power-of-2 size.
+ if (blocks_.size() > 1) {
+ size_t block_size_log2 = __builtin_ctz(blocks_.front().decompressed_size);
+ auto correct_size = [=](XzBlock& b) { return b.decompressed_size == (1 << block_size_log2); };
+ if (std::all_of(blocks_.begin(), std::prev(blocks_.end()), correct_size) &&
+ blocks_.back().decompressed_size <= (1 << block_size_log2)) {
+ block_size_log2_ = block_size_log2;
+ } else {
+ // Inconsistent block-sizes. Decompress and merge everything now.
+ std::unique_ptr<uint8_t[]> data(new uint8_t[size_]);
+ size_t offset = 0;
+ for (XzBlock& block : blocks_) {
+ if (!Decompress(&block)) {
+ return false;
+ }
+ memcpy(data.get() + offset, block.decompressed_data.get(), block.decompressed_size);
+ offset += block.decompressed_size;
+ }
+ blocks_.clear();
+ blocks_.push_back(XzBlock{
+ .decompressed_data = std::move(data),
+ .decompressed_size = size_,
+ });
+ block_size_log2_ = 31; // Because 32 bits is too big (shift right by 32 is not allowed).
+ }
+ }
+
+ return true;
+}
+
+MemoryXz::~MemoryXz() {
+ total_used_ -= used_;
+ total_size_ -= size_;
+ total_open_ -= 1;
+}
+
+size_t MemoryXz::Read(uint64_t addr, void* buffer, size_t size) {
+ if (addr >= size_) {
+ return 0; // Read past the end.
+ }
+ uint8_t* dst = reinterpret_cast<uint8_t*>(buffer); // Position in the output buffer.
+ for (size_t i = addr >> block_size_log2_; i < blocks_.size(); i++) {
+ XzBlock* block = &blocks_[i];
+ if (block->decompressed_data == nullptr) {
+ if (!Decompress(block)) {
+ break;
+ }
+ }
+ size_t offset = (addr - (i << block_size_log2_)); // Start inside the block.
+ size_t copy_bytes = std::min<size_t>(size, block->decompressed_size - offset);
+ memcpy(dst, block->decompressed_data.get() + offset, copy_bytes);
+ dst += copy_bytes;
+ addr += copy_bytes;
+ size -= copy_bytes;
+ if (size == 0) {
+ break;
+ }
+ }
+ return dst - reinterpret_cast<uint8_t*>(buffer);
+}
+
+bool MemoryXz::ReadBlocks() {
+ static ISzAlloc alloc;
+ alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+ alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+
+ // Read the compressed data, so we can quickly scan through the headers.
+ std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[compressed_size_]);
+ if (compressed_data.get() == nullptr) {
+ return false;
+ }
+ if (!compressed_memory_->ReadFully(compressed_addr_, compressed_data.get(), compressed_size_)) {
+ return false;
+ }
+
+ // Implement the required interface for communication
+ // (written in C so we can not use virtual methods or member functions).
+ struct XzLookInStream : public ILookInStream, public ICompressProgress {
+ static SRes LookImpl(const ILookInStream* p, const void** buf, size_t* size) {
+ auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
+ *buf = ctx->data + ctx->offset;
+ *size = std::min(*size, ctx->size - ctx->offset);
+ return SZ_OK;
+ }
+ static SRes SkipImpl(const ILookInStream* p, size_t len) {
+ auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
+ ctx->offset += len;
+ return SZ_OK;
+ }
+ static SRes ReadImpl(const ILookInStream* p, void* buf, size_t* size) {
+ auto* ctx = reinterpret_cast<const XzLookInStream*>(p);
+ *size = std::min(*size, ctx->size - ctx->offset);
+ memcpy(buf, ctx->data + ctx->offset, *size);
+ return SZ_OK;
+ }
+ static SRes SeekImpl(const ILookInStream* p, Int64* pos, ESzSeek origin) {
+ auto* ctx = reinterpret_cast<XzLookInStream*>(const_cast<ILookInStream*>(p));
+ switch (origin) {
+ case SZ_SEEK_SET:
+ ctx->offset = *pos;
+ break;
+ case SZ_SEEK_CUR:
+ ctx->offset += *pos;
+ break;
+ case SZ_SEEK_END:
+ ctx->offset = ctx->size + *pos;
+ break;
+ }
+ *pos = ctx->offset;
+ return SZ_OK;
+ }
+ static SRes ProgressImpl(const ICompressProgress*, UInt64, UInt64) { return SZ_OK; }
+ size_t offset;
+ uint8_t* data;
+ size_t size;
+ };
+ XzLookInStream callbacks;
+ callbacks.Look = &XzLookInStream::LookImpl;
+ callbacks.Skip = &XzLookInStream::SkipImpl;
+ callbacks.Read = &XzLookInStream::ReadImpl;
+ callbacks.Seek = &XzLookInStream::SeekImpl;
+ callbacks.Progress = &XzLookInStream::ProgressImpl;
+ callbacks.offset = 0;
+ callbacks.data = compressed_data.get();
+ callbacks.size = compressed_size_;
+
+ // Iterate over the internal XZ blocks without decompressing them.
+ CXzs xzs;
+ Xzs_Construct(&xzs);
+ Int64 end_offset = compressed_size_;
+ if (Xzs_ReadBackward(&xzs, &callbacks, &end_offset, &callbacks, &alloc) == SZ_OK) {
+ blocks_.reserve(Xzs_GetNumBlocks(&xzs));
+ size_t dst_offset = 0;
+ for (int s = xzs.num - 1; s >= 0; s--) {
+ const CXzStream& stream = xzs.streams[s];
+ size_t src_offset = stream.startOffset + XZ_STREAM_HEADER_SIZE;
+ for (size_t b = 0; b < stream.numBlocks; b++) {
+ const CXzBlockSizes& block = stream.blocks[b];
+ blocks_.push_back(XzBlock{
+ .decompressed_data = nullptr, // Lazy allocation and decompression.
+ .decompressed_size = static_cast<uint32_t>(block.unpackSize),
+ .compressed_offset = static_cast<uint32_t>(src_offset),
+ .compressed_size = static_cast<uint32_t>((block.totalSize + 3) & ~3u),
+ .stream_flags = stream.flags,
+ });
+ dst_offset += blocks_.back().decompressed_size;
+ src_offset += blocks_.back().compressed_size;
+ }
+ }
+ size_ = dst_offset;
+ total_size_ += dst_offset;
+ }
+ Xzs_Free(&xzs, &alloc);
+ return !blocks_.empty();
+}
+
+bool MemoryXz::Decompress(XzBlock* block) {
+ static ISzAlloc alloc;
+ alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); };
+ alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); };
+
+ // Read the compressed data for this block.
+ std::unique_ptr<uint8_t[]> compressed_data(new (std::nothrow) uint8_t[block->compressed_size]);
+ if (compressed_data.get() == nullptr) {
+ return false;
+ }
+ if (!compressed_memory_->ReadFully(compressed_addr_ + block->compressed_offset,
+ compressed_data.get(), block->compressed_size)) {
+ return false;
+ }
+
+ // Allocate decompressed memory.
+ std::unique_ptr<uint8_t[]> decompressed_data(new uint8_t[block->decompressed_size]);
+ if (decompressed_data == nullptr) {
+ return false;
+ }
+
+ // Decompress.
+ CXzUnpacker state{};
+ XzUnpacker_Construct(&state, &alloc);
+ state.streamFlags = block->stream_flags;
+ XzUnpacker_PrepareToRandomBlockDecoding(&state);
+ size_t decompressed_size = block->decompressed_size;
+ size_t compressed_size = block->compressed_size;
+ ECoderStatus status;
+ XzUnpacker_SetOutBuf(&state, decompressed_data.get(), decompressed_size);
+ int return_val =
+ XzUnpacker_Code(&state, /*decompressed_data=*/nullptr, &decompressed_size,
+ compressed_data.get(), &compressed_size, true, CODER_FINISH_END, &status);
+ XzUnpacker_Free(&state);
+ if (return_val != SZ_OK || status != CODER_STATUS_FINISHED_WITH_MARK) {
+ Log::Error("Cannot decompress \"%s\"", name_.c_str());
+ return false;
+ }
+
+ used_ += block->decompressed_size;
+ total_used_ += block->decompressed_size;
+ if (kLogMemoryXzUsage) {
+ Log::Info("decompressed memory: %zi%% of %ziKB (%zi files), %i%% of %iKB (%s)",
+ 100 * total_used_ / total_size_, total_size_ / 1024, total_open_.load(),
+ 100 * used_ / size_, size_ / 1024, name_.c_str());
+ }
+
+ block->decompressed_data = std::move(decompressed_data);
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/MemoryXz.h b/libunwindstack/MemoryXz.h
index c72ba88..57bf6c5 100644
--- a/libunwindstack/MemoryXz.h
+++ b/libunwindstack/MemoryXz.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_XZ_H
-#define _LIBUNWINDSTACK_MEMORY_XZ_H
+#pragma once
#include <atomic>
#include <memory>
@@ -71,5 +70,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_XZ_H
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 03aa6c2..08a35ea 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -18,6 +18,7 @@
#include <sys/ptrace.h>
#include <sys/uio.h>
+#include <algorithm>
#include <vector>
#include <unwindstack/Elf.h>
@@ -25,27 +26,26 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
-#include <unwindstack/RegsMips.h>
-#include <unwindstack/RegsMips64.h>
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
#include <unwindstack/UserArm.h>
#include <unwindstack/UserArm64.h>
-#include <unwindstack/UserMips.h>
-#include <unwindstack/UserMips64.h>
#include <unwindstack/UserX86.h>
#include <unwindstack/UserX86_64.h>
namespace unwindstack {
// The largest user structure.
-constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
+// constexpr size_t MAX_USER_REGS_SIZE = sizeof(mips64_user_regs) + 10;
+static constexpr size_t kMaxUserRegsSize = std::max(
+ sizeof(arm_user_regs),
+ std::max(sizeof(arm64_user_regs), std::max(sizeof(x86_user_regs), sizeof(x86_64_user_regs))));
// This function assumes that reg_data is already aligned to a 64 bit value.
// If not this could crash with an unaligned access.
Regs* Regs::RemoteGet(pid_t pid) {
// Make the buffer large enough to contain the largest registers type.
- std::vector<uint64_t> buffer(MAX_USER_REGS_SIZE / sizeof(uint64_t));
+ std::vector<uint64_t> buffer(kMaxUserRegsSize / sizeof(uint64_t));
struct iovec io;
io.iov_base = buffer.data();
io.iov_len = buffer.size() * sizeof(uint64_t);
@@ -54,6 +54,7 @@
return nullptr;
}
+ // Infer the process architecture from the size of its register structure.
switch (io.iov_len) {
case sizeof(x86_user_regs):
return RegsX86::Read(buffer.data());
@@ -63,14 +64,35 @@
return RegsArm::Read(buffer.data());
case sizeof(arm64_user_regs):
return RegsArm64::Read(buffer.data());
- case sizeof(mips_user_regs):
- return RegsMips::Read(buffer.data());
- case sizeof(mips64_user_regs):
- return RegsMips64::Read(buffer.data());
}
return nullptr;
}
+ArchEnum Regs::RemoteGetArch(pid_t pid) {
+ // Make the buffer large enough to contain the largest registers type.
+ std::vector<uint64_t> buffer(kMaxUserRegsSize / sizeof(uint64_t));
+ struct iovec io;
+ io.iov_base = buffer.data();
+ io.iov_len = buffer.size() * sizeof(uint64_t);
+
+ if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, reinterpret_cast<void*>(&io)) == -1) {
+ return ARCH_UNKNOWN;
+ }
+
+ // Infer the process architecture from the size of its register structure.
+ switch (io.iov_len) {
+ case sizeof(x86_user_regs):
+ return ARCH_X86;
+ case sizeof(x86_64_user_regs):
+ return ARCH_X86_64;
+ case sizeof(arm_user_regs):
+ return ARCH_ARM;
+ case sizeof(arm64_user_regs):
+ return ARCH_ARM64;
+ }
+ return ARCH_UNKNOWN;
+}
+
Regs* Regs::CreateFromUcontext(ArchEnum arch, void* ucontext) {
switch (arch) {
case ARCH_X86:
@@ -81,11 +103,6 @@
return RegsArm::CreateFromUcontext(ucontext);
case ARCH_ARM64:
return RegsArm64::CreateFromUcontext(ucontext);
- case ARCH_MIPS:
- return RegsMips::CreateFromUcontext(ucontext);
- case ARCH_MIPS64:
- return RegsMips64::CreateFromUcontext(ucontext);
- case ARCH_UNKNOWN:
default:
return nullptr;
}
diff --git a/libunwindstack/RegsInfo.h b/libunwindstack/RegsInfo.h
index e445a91..28b297e 100644
--- a/libunwindstack/RegsInfo.h
+++ b/libunwindstack/RegsInfo.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_INFO_H
-#define _LIBUNWINDSTACK_REGS_INFO_H
+#pragma once
#include <stdint.h>
@@ -64,5 +63,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_INFO_H
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index c1967b0..0b46751 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_SYMBOLS_H
-#define _LIBUNWINDSTACK_SYMBOLS_H
+#pragma once
#include <stdint.h>
@@ -75,5 +74,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/TEST_MAPPING b/libunwindstack/TEST_MAPPING
index 0c73ff9..2ebaeac 100644
--- a/libunwindstack/TEST_MAPPING
+++ b/libunwindstack/TEST_MAPPING
@@ -12,5 +12,11 @@
{
"name": "CtsPerfettoTestCases"
}
+ ],
+
+ "hwasan-postsubmit": [
+ {
+ "name": "libunwindstack_unit_test"
+ }
]
}
diff --git a/libunwindstack/ThreadEntry.cpp b/libunwindstack/ThreadEntry.cpp
index 86912a2..db34df0 100644
--- a/libunwindstack/ThreadEntry.cpp
+++ b/libunwindstack/ThreadEntry.cpp
@@ -75,12 +75,12 @@
}
bool ThreadEntry::Wait(WaitType type) {
- static const std::chrono::duration wait_time(std::chrono::seconds(5));
+ static const std::chrono::duration wait_time(std::chrono::seconds(10));
std::unique_lock<std::mutex> lock(wait_mutex_);
if (wait_cond_.wait_for(lock, wait_time, [this, type] { return wait_value_ == type; })) {
return true;
} else {
- log_async_safe("pthread_cond_timedwait for value %d failed", type);
+ Log::AsyncSafe("pthread_cond_timedwait for value %d failed", type);
return false;
}
}
diff --git a/libunwindstack/ThreadEntry.h b/libunwindstack/ThreadEntry.h
index 1caba83..2f8e88d 100644
--- a/libunwindstack/ThreadEntry.h
+++ b/libunwindstack/ThreadEntry.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_THREAD_ENTRY_H
-#define _LIBUNWINDSTACK_THREAD_ENTRY_H
+#pragma once
#include <pthread.h>
#include <sys/types.h>
@@ -74,5 +73,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_THREAD_ENTRY_H
diff --git a/libunwindstack/ThreadUnwinder.cpp b/libunwindstack/ThreadUnwinder.cpp
index a04a997..7b45261 100644
--- a/libunwindstack/ThreadUnwinder.cpp
+++ b/libunwindstack/ThreadUnwinder.cpp
@@ -39,7 +39,7 @@
static void SignalLogOnly(int, siginfo_t*, void*) {
android::base::ErrnoRestorer restore;
- log_async_safe("pid %d, tid %d: Received a spurious thread signal\n", getpid(),
+ Log::AsyncSafe("pid %d, tid %d: Received a spurious thread signal\n", getpid(),
static_cast<int>(android::base::GetThreadId()));
}
@@ -65,13 +65,17 @@
entry->Wake();
} else {
// At this point, it is possible that entry has been freed, so just exit.
- log_async_safe("Timed out waiting for unwind thread to indicate it completed.");
+ Log::AsyncSafe("Timed out waiting for unwind thread to indicate it completed.");
}
}
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_;
@@ -92,7 +96,7 @@
struct sigaction old_action = {};
sigemptyset(&new_action.sa_mask);
if (sigaction(signal, &new_action, &old_action) != 0) {
- log_async_safe("sigaction failed: %s", strerror(errno));
+ Log::AsyncSafe("sigaction failed: %s", strerror(errno));
ThreadEntry::Remove(entry);
last_error_.code = ERROR_SYSTEM_CALL;
return nullptr;
@@ -137,7 +141,7 @@
last_error_.code = ERROR_THREAD_DOES_NOT_EXIST;
} else {
last_error_.code = ERROR_THREAD_TIMEOUT;
- log_async_safe("Timed out waiting for signal handler to get ucontext data.");
+ Log::AsyncSafe("Timed out waiting for signal handler to get ucontext data.");
}
ThreadEntry::Remove(entry);
@@ -145,11 +149,11 @@
return nullptr;
}
-void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid,
+void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid, std::unique_ptr<Regs>* initial_regs,
const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
ClearErrors();
- if (tid == pid_) {
+ if (tid == static_cast<pid_t>(android::base::GetThreadId())) {
last_error_.code = ERROR_UNSUPPORTED;
return;
}
@@ -164,6 +168,9 @@
}
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentArch(), entry->GetUcontext()));
+ if (initial_regs != nullptr) {
+ initial_regs->reset(regs->Clone());
+ }
SetRegs(regs.get());
UnwinderFromPid::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore);
@@ -173,7 +180,7 @@
// Wait for the thread to indicate it is done with the ThreadEntry.
if (!entry->Wait(WAIT_FOR_THREAD_TO_RESTART)) {
// Send a warning, but do not mark as a failure to unwind.
- log_async_safe("Timed out waiting for signal handler to indicate it finished.");
+ Log::AsyncSafe("Timed out waiting for signal handler to indicate it finished.");
}
ThreadEntry::Remove(entry);
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index bd34465..facff86 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -24,9 +24,10 @@
#include <unistd.h>
#include <algorithm>
+#include <memory>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
#include <unwindstack/DexFiles.h>
#include <unwindstack/Elf.h>
@@ -60,21 +61,12 @@
frame->pc = dex_pc;
frame->sp = regs_->sp();
- MapInfo* info = maps_->Find(dex_pc);
- if (info != nullptr) {
- frame->map_start = info->start();
- frame->map_end = info->end();
- // Since this is a dex file frame, the elf_start_offset is not set
- // by any of the normal code paths. Use the offset of the map since
- // that matches the actual offset.
- frame->map_elf_start_offset = info->offset();
- frame->map_exact_offset = info->offset();
- frame->map_load_bias = info->load_bias();
- frame->map_flags = info->flags();
- if (resolve_names_) {
- frame->map_name = info->name();
- }
- frame->rel_pc = dex_pc - info->start();
+ frame->map_info = maps_->Find(dex_pc);
+ if (frame->map_info != nullptr) {
+ frame->rel_pc = dex_pc - frame->map_info->start();
+ // Initialize the load bias for this map so subsequent calls
+ // to GetLoadBias() will always return data.
+ frame->map_info->set_load_bias(0);
} else {
frame->rel_pc = dex_pc;
warnings_ |= WARNING_DEX_PC_NOT_IN_MAP;
@@ -94,7 +86,7 @@
#endif
}
-FrameData* Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc,
+FrameData* Unwinder::FillInFrame(std::shared_ptr<MapInfo>& map_info, Elf* /*elf*/, uint64_t rel_pc,
uint64_t pc_adjustment) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
@@ -109,25 +101,8 @@
return nullptr;
}
- if (resolve_names_) {
- frame->map_name = map_info->name();
- if (embedded_soname_ && map_info->elf_start_offset() != 0 && !frame->map_name.empty()) {
- std::string soname = elf->GetSoname();
- if (!soname.empty()) {
- std::string map_with_soname;
- map_with_soname += frame->map_name;
- map_with_soname += '!';
- map_with_soname += soname;
- frame->map_name = SharedString(std::move(map_with_soname));
- }
- }
- }
- frame->map_elf_start_offset = map_info->elf_start_offset();
- frame->map_exact_offset = map_info->offset();
- frame->map_start = map_info->start();
- frame->map_end = map_info->end();
- frame->map_flags = map_info->flags();
- frame->map_load_bias = elf->GetLoadBias();
+ frame->map_info = map_info;
+
return frame;
}
@@ -151,7 +126,6 @@
ClearErrors();
frames_.clear();
- elf_from_memory_not_file_ = false;
// Clear any cached data from previous unwinds.
process_memory_->Clear();
@@ -166,30 +140,32 @@
uint64_t cur_pc = regs_->pc();
uint64_t cur_sp = regs_->sp();
- MapInfo* map_info = maps_->Find(regs_->pc());
+ std::shared_ptr<MapInfo> map_info = maps_->Find(regs_->pc());
uint64_t pc_adjustment = 0;
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;
- last_error_.code = ERROR_INVALID_MAP;
+ // If we get invalid map via return_address_attempt, don't hide error for the previous frame.
+ if (!return_address_attempt || last_error_.code == ERROR_NONE) {
+ last_error_.code = ERROR_INVALID_MAP;
+ last_error_.address = step_pc;
+ }
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_);
- // If this elf is memory backed, and there is a valid file, then set
- // an indicator that we couldn't open the file.
- const std::string& map_name = map_info->name();
- if (!elf_from_memory_not_file_ && map_info->memory_backed_elf() && !map_name.empty() &&
- map_name[0] != '[' && !android::base::StartsWith(map_name, "/memfd:")) {
- elf_from_memory_not_file_ = true;
- }
step_pc = regs_->pc();
- rel_pc = elf->GetRelPc(step_pc, map_info);
+ rel_pc = elf->GetRelPc(step_pc, map_info.get());
// Everyone except elf data in gdb jit debug maps uses the relative pc.
if (!(map_info->flags() & MAPS_FLAGS_JIT_SYMFILE_MAP)) {
step_pc = rel_pc;
@@ -215,9 +191,7 @@
}
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(),
- basename(map_info->name().c_str())) == initial_map_names_to_skip->end()) {
+ if (!ignore_frame) {
if (regs_->dex_pc() != 0) {
// Add a frame to represent the dex file.
FillInDexFrame();
@@ -248,7 +222,7 @@
// some of the speculative frames.
in_device_map = true;
} else {
- MapInfo* sp_info = maps_->Find(regs_->sp());
+ auto sp_info = maps_->Find(regs_->sp());
if (sp_info != nullptr && sp_info->flags() & MAPS_FLAGS_DEVICE_MAP) {
// Do not stop here, fall through in case we are
// in the speculative unwind path and need to remove
@@ -325,25 +299,30 @@
}
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);
}
- if (frame.map_start == frame.map_end) {
+ auto map_info = frame.map_info;
+ if (map_info == nullptr) {
// No valid map associated with this frame.
data += " <unknown>";
- } else if (!frame.map_name.empty()) {
+ } else if (!map_info->name().empty()) {
data += " ";
- data += frame.map_name;
+ data += map_info->GetFullName();
} else {
- data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
+ data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", map_info->start());
}
- if (frame.map_elf_start_offset != 0) {
- data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_elf_start_offset);
+ if (map_info != nullptr && map_info->elf_start_offset() != 0) {
+ data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", map_info->elf_start_offset());
}
if (!frame.function_name.empty()) {
@@ -362,8 +341,7 @@
data += ')';
}
- MapInfo* map_info = maps_->Find(frame.map_start);
- 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 + ')';
@@ -376,7 +354,7 @@
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) {
@@ -446,7 +424,7 @@
bool resolve_names) {
FrameData frame;
- MapInfo* map_info = maps->Find(pc);
+ std::shared_ptr<MapInfo> map_info = maps->Find(pc);
if (map_info == nullptr || arch == ARCH_UNKNOWN) {
frame.pc = pc;
frame.rel_pc = pc;
@@ -455,7 +433,7 @@
Elf* elf = map_info->GetElf(process_memory, arch);
- uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+ uint64_t relative_pc = elf->GetRelPc(pc, map_info.get());
uint64_t pc_adjustment = GetPcAdjustment(relative_pc, elf, arch);
relative_pc -= pc_adjustment;
@@ -475,13 +453,7 @@
// Copy all the things we need into the frame for symbolization.
frame.rel_pc = relative_pc;
frame.pc = pc - pc_adjustment;
- frame.map_name = map_info->name();
- frame.map_elf_start_offset = map_info->elf_start_offset();
- frame.map_exact_offset = map_info->offset();
- frame.map_start = map_info->start();
- frame.map_end = map_info->end();
- frame.map_flags = map_info->flags();
- frame.map_load_bias = elf->GetLoadBias();
+ frame.map_info = map_info;
if (!resolve_names ||
!elf->GetFunctionName(debug_pc, &frame.function_name, &frame.function_offset)) {
diff --git a/libunwindstack/benchmarks/ElfBenchmark.cpp b/libunwindstack/benchmarks/ElfBenchmark.cpp
index 3e1e21f..7bfd9db 100644
--- a/libunwindstack/benchmarks/ElfBenchmark.cpp
+++ b/libunwindstack/benchmarks/ElfBenchmark.cpp
@@ -47,7 +47,7 @@
unwindstack::Elf elf(file_memory.release());
if (!elf.Init() || !elf.valid()) {
- errx(1, "Internal Error: Cannot open elf.");
+ errx(1, "Internal Error: Cannot open elf: %s", elf_file.c_str());
}
state.PauseTiming();
@@ -73,10 +73,15 @@
}
BENCHMARK(BM_elf_create);
-void BM_elf_create_compressed(benchmark::State& state) {
- BenchmarkElfCreate(state, GetCompressedElfFile());
+void BM_elf_create_large_compressed(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetLargeCompressedFrameElfFile());
}
-BENCHMARK(BM_elf_create_compressed);
+BENCHMARK(BM_elf_create_large_compressed);
+
+void BM_elf_create_large_eh_frame(benchmark::State& state) {
+ BenchmarkElfCreate(state, GetLargeEhFrameElfFile());
+}
+BENCHMARK(BM_elf_create_large_eh_frame);
static void InitializeBuildId(benchmark::State& state, unwindstack::Maps& maps,
unwindstack::MapInfo** build_id_map_info) {
diff --git a/libunwindstack/benchmarks/EvalBenchmark.cpp b/libunwindstack/benchmarks/EvalBenchmark.cpp
new file mode 100644
index 0000000..9b3d1d8
--- /dev/null
+++ b/libunwindstack/benchmarks/EvalBenchmark.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2021 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 <cstdint>
+#include <ios>
+#include <sstream>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/DwarfLocation.h>
+#include <unwindstack/DwarfSection.h>
+
+#include "Utils.h"
+#include "utils/DwarfSectionImplFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/RegsFake.h"
+
+namespace unwindstack {
+namespace {
+
+// This collection of benchmarks exercises the DwarfSectionImpl::Eval function with a set of
+// artificial unwind data. The number of registers and register evaluation method are varied
+// for each individual benchmark.
+
+constexpr int kReturnAddressReg = 5;
+
+template <typename AddresssType>
+class EvalBenchmark : public benchmark::Fixture {
+ public:
+ EvalBenchmark() {
+ memory_.Clear();
+ section_ = std::make_unique<DwarfSectionImplFake<AddresssType>>(&memory_);
+ }
+
+ void TearDown(benchmark::State& state) override { mem_tracker_.SetBenchmarkCounters(state); }
+
+ // Benchmarks DwarfSectionImpl::Eval given the DwarfLocation object, loc_regs, initialized in each
+ // individual benchmark macro/function.
+ //
+ // This method initializes the fake register object and the DwarfCie object the same regardless
+ // of the benchmark. So the initialization of loc_regs is carefully crafted in each benchmark
+ // macro so that the evaluated PC and SP match the expected values after each call to Eval in the
+ // benchmarking loop.
+ //
+ // In addition to the Eval call, register value assertion is included in the benchmarking loop
+ // to ensure that we always capture the actual register evaluation
+ // (DwarfSectionImpl::EvalRegister). For example, if Eval is modified to lazily evaluate register
+ // values, we will still capture the register evaluation for the PC and SP (common case) in the
+ // register value assertion.
+ void RunBenchmark(benchmark::State& state, DwarfLocations& loc_regs) {
+ DwarfCie cie{.return_address_register = kReturnAddressReg};
+ bool finished;
+ RegsImplFake<AddresssType> regs(64);
+ regs.set_pc(0x1000);
+ regs.set_sp(0x3500);
+ regs[0] = 0x10000000;
+ mem_tracker_.StartTrackingAllocations();
+ for (auto _ : state) {
+ std::stringstream err_stream;
+ if (!section_->Eval(&cie, &memory_, loc_regs, ®s, &finished)) {
+ err_stream << "Eval() failed at address " << section_->LastErrorAddress();
+ state.SkipWithError(err_stream.str().c_str());
+ return;
+ }
+ if (finished || regs.pc() != 0x60000000U || regs.sp() != 0x10000000U) {
+ err_stream
+ << "Eval() finished successfully but registers were not evaluated correctly."
+ << "\nExpected: finished == false, regs.pc() == 0x60000000, regs.sp() == 0x10000000."
+ << "\nActual: finished == " << std::boolalpha << finished << std::hex
+ << ", regs.pc() == 0x" << regs.pc() << ", regs.sp() == 0x" << regs.sp();
+ state.SkipWithError(err_stream.str().c_str());
+ return;
+ }
+ }
+ mem_tracker_.StopTrackingAllocations();
+ }
+
+ protected:
+ MemoryFake memory_;
+ std::unique_ptr<DwarfSectionImplFake<AddresssType>> section_;
+ MemoryTracker mem_tracker_;
+};
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_REGISTER evaluation method.
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_few_regs, uint64_t)(benchmark::State& state) {
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0x50000000}};
+ RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_register_many_regs, uint64_t)(benchmark::State& state) {
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ for (uint64_t i = 0; i < 64; i++) {
+ loc_regs[i] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, i * 0x10000000}};
+ }
+ RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_VAL_OFFSET evaluation method.
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_few_regs, uint64_t)
+(benchmark::State& state) {
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x50000000, 0}};
+ RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_offset_many_regs, uint64_t)
+(benchmark::State& state) {
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ for (uint64_t i = 0; i < 64; i++) {
+ loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {i * 0x10000000, 0}};
+ }
+ RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_OFFSET evaluation method.
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_few_regs, uint64_t)
+(benchmark::State& state) {
+ memory_.SetData64(0x20000000, 0x60000000);
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
+ RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_offset_many_regs, uint64_t)
+(benchmark::State& state) {
+ memory_.SetData64(0x20000000, 0x60000000);
+ memory_.SetData64(0x30000000, 0x10000000);
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ for (uint64_t i = 1; i < 64; i++) {
+ loc_regs[i] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x10000000, 0}};
+ }
+ // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
+ // across multiple calls to Eval.
+ loc_regs[0] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x20000000, 0}};
+ RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_EXPRESSION evaluation method.
+// The dwarf op-code used for the expression benchmarks are OP_const4u (see DwarfOp::Eval).
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_few_regs, uint64_t)
+(benchmark::State& state) {
+ memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ uint64_t pc_value = 0x60000000;
+ memory_.SetMemory(0x80000000, &pc_value, sizeof(pc_value));
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
+ RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_expression_many_regs, uint64_t)
+(benchmark::State& state) {
+ memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
+ uint64_t pc_value = 0x60000000;
+ memory_.SetMemory(0x80000000, &pc_value, sizeof(pc_value));
+
+ memory_.SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x90});
+ uint64_t sp_value = 0x10000000;
+ memory_.SetMemory(0x90000000, &sp_value, sizeof(sp_value));
+
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ for (uint64_t i = 1; i < 64; i++) {
+ loc_regs[i] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
+ }
+ // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
+ // across multiple calls to Eval.
+ loc_regs[0] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x6004}};
+ RunBenchmark(state, loc_regs);
+}
+
+// Benchmarks exercising Eval with the DWARF_LOCATION_VAL_EXPRESSION evaluation method.
+// The dwarf op-code used for the value expression benchmarks are OP_const4u (see DwarfOp::Eval).
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_few_regs, uint64_t)
+(benchmark::State& state) {
+ memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ loc_regs[kReturnAddressReg] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+ RunBenchmark(state, loc_regs);
+}
+
+BENCHMARK_TEMPLATE_F(EvalBenchmark, BM_eval_val_expression_many_regs, uint64_t)
+(benchmark::State& state) {
+ memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x60});
+ memory_.SetMemory(0x6000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x10});
+ DwarfLocations loc_regs;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 0}};
+ for (uint64_t i = 1; i < 64; i++) {
+ loc_regs[i] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5004}};
+ }
+ // Read from different place in memory for reg 0 so reg 0 maintains value of 0x10000000
+ // across multiple calls to Eval.
+ loc_regs[0] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x6004}};
+ RunBenchmark(state, loc_regs);
+}
+
+} // namespace
+} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/MemoryLocalUnsafe.h b/libunwindstack/benchmarks/MemoryLocalUnsafe.h
new file mode 100644
index 0000000..8edf473
--- /dev/null
+++ b/libunwindstack/benchmarks/MemoryLocalUnsafe.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstring>
+
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+// MemoryLocalUnsafe is a prototype class to compare the performance of MemoryLocal::Read
+// to an "unsafe" read that assumes the local memory address provided is valid (i.e. memory is
+// not corrupted and address range lies within the stack).
+class MemoryLocalUnsafe : public Memory {
+ public:
+ MemoryLocalUnsafe() = default;
+ virtual ~MemoryLocalUnsafe() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override {
+ memcpy(dst, reinterpret_cast<void*>(addr), size);
+ return size;
+ }
+};
+
+} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp b/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp
new file mode 100644
index 0000000..bcb0bec
--- /dev/null
+++ b/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2021 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 <cstddef>
+#include <cstdint>
+#include <filesystem>
+#include <memory>
+#include <sstream>
+#include <unordered_map>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Arch.h>
+#include <unwindstack/Unwinder.h>
+
+#include "Utils.h"
+#include "utils/OfflineUnwindUtils.h"
+
+// This collection of benchmarks exercises Unwinder::Unwind for offline unwinds.
+//
+// See `libunwindstack/utils/OfflineUnwindUtils.h` for more info on offline unwinds
+// and b/192012600 for additional information regarding these benchmarks.
+namespace unwindstack {
+namespace {
+
+static constexpr char kStartup[] = "startup_case";
+static constexpr char kSteadyState[] = "steady_state_case";
+
+class OfflineUnwindBenchmark : public benchmark::Fixture {
+ public:
+ void SetUp(benchmark::State& state) override {
+ // Ensure each benchmarks has a fresh ELF cache at the start.
+ unwind_case_ = state.range(0) ? kSteadyState : kStartup;
+ resolve_names_ = state.range(1);
+ Elf::SetCachingEnabled(false);
+ }
+
+ void TearDown(benchmark::State& state) override {
+ offline_utils_.ReturnToCurrentWorkingDirectory();
+ mem_tracker_.SetBenchmarkCounters(state);
+ }
+
+ void SingleUnwindBenchmark(benchmark::State& state, const UnwindSampleInfo& sample_info) {
+ std::string error_msg;
+ if (!offline_utils_.Init(sample_info, &error_msg)) {
+ state.SkipWithError(error_msg.c_str());
+ return;
+ }
+ BenchmarkOfflineUnwindMultipleSamples(state, std::vector<UnwindSampleInfo>{sample_info});
+ }
+
+ void ConsecutiveUnwindBenchmark(benchmark::State& state,
+ const std::vector<UnwindSampleInfo>& sample_infos) {
+ std::string error_msg;
+ if (!offline_utils_.Init(sample_infos, &error_msg)) {
+ state.SkipWithError(error_msg.c_str());
+ return;
+ }
+ BenchmarkOfflineUnwindMultipleSamples(state, sample_infos);
+ }
+
+ private:
+ void BenchmarkOfflineUnwindMultipleSamples(benchmark::State& state,
+ const std::vector<UnwindSampleInfo>& sample_infos) {
+ std::string error_msg;
+ auto offline_unwind_multiple_samples = [&](bool benchmarking_unwind) {
+ // The benchmark should only measure the time / memory usage for the creation of
+ // each Unwinder object and the corresponding unwind as close as possible.
+ if (benchmarking_unwind) state.PauseTiming();
+
+ std::unordered_map<std::string_view, std::unique_ptr<Regs>> regs_copies;
+ for (const auto& sample_info : sample_infos) {
+ const std::string& sample_name = sample_info.offline_files_dir;
+
+ // Need to init unwinder with new copy of regs each iteration because unwinding changes
+ // the attributes of the regs object.
+ regs_copies.emplace(sample_name,
+ std::unique_ptr<Regs>(offline_utils_.GetRegs(sample_name)->Clone()));
+
+ // The Maps object will still hold the parsed maps from the previous unwinds. So reset them
+ // unless we want to assume all Maps are cached.
+ if (!sample_info.create_maps) {
+ if (!offline_utils_.CreateMaps(&error_msg, sample_name)) {
+ state.SkipWithError(error_msg.c_str());
+ return;
+ }
+
+ // Since this maps object will be cached, need to make sure that
+ // all of the names are fully qualified paths. This allows the
+ // caching mechanism to properly cache elf files that are
+ // actually the same.
+ if (!offline_utils_.ChangeToSampleDirectory(&error_msg, sample_name)) {
+ state.SkipWithError(error_msg.c_str());
+ return;
+ }
+ for (auto& map_info : *offline_utils_.GetMaps(sample_name)) {
+ auto& name = map_info->name();
+ if (!name.empty()) {
+ std::filesystem::path path;
+ if (std::filesystem::is_symlink(name.c_str())) {
+ path = std::filesystem::read_symlink(name.c_str());
+ } else {
+ path = std::filesystem::current_path();
+ path /= name.c_str();
+ }
+ name = path.lexically_normal().c_str();
+ }
+ }
+ }
+ }
+
+ if (benchmarking_unwind) mem_tracker_.StartTrackingAllocations();
+ for (const auto& sample_info : sample_infos) {
+ const std::string& sample_name = sample_info.offline_files_dir;
+ // Need to change to sample directory for Unwinder to properly init ELF objects.
+ // See more info at OfflineUnwindUtils::ChangeToSampleDirectory.
+ if (!offline_utils_.ChangeToSampleDirectory(&error_msg, sample_name)) {
+ state.SkipWithError(error_msg.c_str());
+ return;
+ }
+ if (benchmarking_unwind) state.ResumeTiming();
+
+ Unwinder unwinder(128, offline_utils_.GetMaps(sample_name),
+ regs_copies.at(sample_name).get(),
+ offline_utils_.GetProcessMemory(sample_name));
+ if (sample_info.memory_flag == ProcessMemoryFlag::kIncludeJitMemory) {
+ unwinder.SetJitDebug(offline_utils_.GetJitDebug(sample_name));
+ }
+ unwinder.SetResolveNames(resolve_names_);
+ unwinder.Unwind();
+
+ if (benchmarking_unwind) state.PauseTiming();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg, sample_name)) {
+ state.SkipWithError(error_msg.c_str());
+ return;
+ }
+ if (unwinder.NumFrames() != expected_num_frames) {
+ std::stringstream err_stream;
+ err_stream << "Failed to unwind sample " << sample_name << " properly.Expected "
+ << expected_num_frames << " frames, but unwinder contained "
+ << unwinder.NumFrames() << " frames. Unwind:\n"
+ << DumpFrames(unwinder);
+ state.SkipWithError(err_stream.str().c_str());
+ return;
+ }
+ }
+ if (benchmarking_unwind) mem_tracker_.StopTrackingAllocations();
+ };
+
+ if (unwind_case_ == kSteadyState) {
+ WarmUpUnwindCaches(offline_unwind_multiple_samples);
+ }
+
+ for (auto _ : state) {
+ offline_unwind_multiple_samples(/*benchmarking_unwind=*/true);
+ }
+ }
+
+ // This functions main purpose is to enable ELF caching for the steady state unwind case
+ // and then perform one unwind to warm up the cache for subsequent unwinds.
+ //
+ // Another reason for pulling this functionality out of the main benchmarking function is
+ // to add an additional call stack frame in between the cache warm-up unwinds and
+ // BenchmarkOfflineUnwindMultipleSamples so that it is easy to filter this set of unwinds out
+ // when profiling.
+ void WarmUpUnwindCaches(const std::function<void(bool)>& offline_unwind_multiple_samples) {
+ Elf::SetCachingEnabled(true);
+ offline_unwind_multiple_samples(/*benchmarking_unwind=*/false);
+ }
+
+ std::string unwind_case_;
+ bool resolve_names_;
+ MemoryTracker mem_tracker_;
+ OfflineUnwindUtils offline_utils_;
+};
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64)(benchmark::State& state) {
+ SingleUnwindBenchmark(
+ state, {.offline_files_dir = "straddle_arm64/", .arch = ARCH_ARM64, .create_maps = false});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64)
+ ->ArgNames({"is_steady_state_case", "resolve_names"})
+ ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64_cached_maps)
+(benchmark::State& state) {
+ SingleUnwindBenchmark(state, {.offline_files_dir = "straddle_arm64/", .arch = ARCH_ARM64});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64_cached_maps)
+ ->ArgNames({"is_steady_state_case", "resolve_names"})
+ ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_jit_debug_arm)(benchmark::State& state) {
+ SingleUnwindBenchmark(state, {.offline_files_dir = "jit_debug_arm/",
+ .arch = ARCH_ARM,
+ .memory_flag = ProcessMemoryFlag::kIncludeJitMemory,
+ .create_maps = false});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_jit_debug_arm)
+ ->ArgNames({"is_steady_state_case", "resolve_names"})
+ ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_profiler_like_multi_process)
+(benchmark::State& state) {
+ ConsecutiveUnwindBenchmark(
+ state,
+ std::vector<UnwindSampleInfo>{
+ {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64, .create_maps = false},
+ {.offline_files_dir = "jit_debug_arm/",
+ .arch = ARCH_ARM,
+ .memory_flag = ProcessMemoryFlag::kIncludeJitMemory,
+ .create_maps = false},
+ {.offline_files_dir = "photos_reset_arm64/", .arch = ARCH_ARM64, .create_maps = false},
+ {.offline_files_dir = "youtube_compiled_arm64/",
+ .arch = ARCH_ARM64,
+ .create_maps = false},
+ {.offline_files_dir = "yt_music_arm64/", .arch = ARCH_ARM64, .create_maps = false},
+ {.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/",
+ .arch = ARCH_ARM64,
+ .create_maps = false}});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_profiler_like_multi_process)
+ ->ArgNames({"is_steady_state_case", "resolve_names"})
+ ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_process_multi_thread)
+(benchmark::State& state) {
+ ConsecutiveUnwindBenchmark(
+ state,
+ std::vector<UnwindSampleInfo>{{.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/",
+ .arch = ARCH_ARM64,
+ .create_maps = false},
+ {.offline_files_dir = "maps_compiled_arm64/28613_main-thread/",
+ .arch = ARCH_ARM64,
+ .create_maps = false},
+ {.offline_files_dir = "maps_compiled_arm64/28644/",
+ .arch = ARCH_ARM64,
+ .create_maps = false},
+ {.offline_files_dir = "maps_compiled_arm64/28648/",
+ .arch = ARCH_ARM64,
+ .create_maps = false},
+ {.offline_files_dir = "maps_compiled_arm64/28667/",
+ .arch = ARCH_ARM64,
+ .create_maps = false}});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_process_multi_thread)
+ ->ArgNames({"is_steady_state_case", "resolve_names"})
+ ->Ranges({{false, true}, {false, true}});
+
+BENCHMARK_DEFINE_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_thread_diverse_pcs)
+(benchmark::State& state) {
+ ConsecutiveUnwindBenchmark(
+ state,
+ std::vector<UnwindSampleInfo>{
+ {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64, .create_maps = false},
+ {.offline_files_dir = "bluetooth_arm64/pc_2/", .arch = ARCH_ARM64, .create_maps = false},
+ {.offline_files_dir = "bluetooth_arm64/pc_3/", .arch = ARCH_ARM64, .create_maps = false},
+ {.offline_files_dir = "bluetooth_arm64/pc_4/",
+ .arch = ARCH_ARM64,
+ .create_maps = false}});
+}
+BENCHMARK_REGISTER_F(OfflineUnwindBenchmark, BM_offline_profiler_like_single_thread_diverse_pcs)
+ ->ArgNames({"is_steady_state_case", "resolve_names"})
+ ->Ranges({{false, true}, {false, true}});
+
+} // namespace
+} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/SymbolBenchmark.cpp b/libunwindstack/benchmarks/SymbolBenchmark.cpp
index 8b65b15..f181b57 100644
--- a/libunwindstack/benchmarks/SymbolBenchmark.cpp
+++ b/libunwindstack/benchmarks/SymbolBenchmark.cpp
@@ -47,7 +47,7 @@
unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(elf_file, 0).release());
if (!elf.Init() || !elf.valid()) {
- errx(1, "Internal Error: Cannot open elf.");
+ errx(1, "Internal Error: Cannot open elf: %s", elf_file.c_str());
}
unwindstack::SharedString name;
@@ -129,3 +129,25 @@
GetSymbolSortedElfFile(), true);
}
BENCHMARK(BM_elf_and_symbol_find_multiple_from_sorted);
+
+void BM_elf_and_symbol_not_present_from_large_compressed_frame(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0, GetLargeCompressedFrameElfFile(), false);
+}
+BENCHMARK(BM_elf_and_symbol_not_present_from_large_compressed_frame);
+
+void BM_elf_and_symbol_find_single_from_large_compressed_frame(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0x202aec, GetLargeCompressedFrameElfFile(), true);
+}
+BENCHMARK(BM_elf_and_symbol_find_single_from_large_compressed_frame);
+
+void BM_elf_and_symbol_find_single_many_times_from_large_compressed_frame(benchmark::State& state) {
+ BenchmarkSymbolLookup(state, 0x202aec, GetLargeCompressedFrameElfFile(), true, 4096);
+}
+BENCHMARK(BM_elf_and_symbol_find_single_many_times_from_large_compressed_frame);
+
+void BM_elf_and_symbol_find_multiple_from_large_compressed_frame(benchmark::State& state) {
+ BenchmarkSymbolLookup(state,
+ std::vector<uint64_t>{0x202aec, 0x23e74c, 0xd000c, 0x201b10, 0x183060},
+ GetLargeCompressedFrameElfFile(), true);
+}
+BENCHMARK(BM_elf_and_symbol_find_multiple_from_large_compressed_frame);
diff --git a/libunwindstack/benchmarks/Utils.cpp b/libunwindstack/benchmarks/Utils.cpp
index c92f109..3750168 100644
--- a/libunwindstack/benchmarks/Utils.cpp
+++ b/libunwindstack/benchmarks/Utils.cpp
@@ -17,6 +17,9 @@
#include <err.h>
#include <stdint.h>
+#include <benchmark/benchmark.h>
+#include <malloc.h>
+
#include <string>
#include <vector>
@@ -27,17 +30,30 @@
#include <unwindstack/Elf.h>
#include <unwindstack/Memory.h>
+#include "utils/OfflineUnwindUtils.h"
+
+#include "Utils.h"
+
+std::string GetBenchmarkFilesDirectory() {
+ std::string path = android::base::GetExecutableDirectory() + "/benchmarks/files/";
+ unwindstack::DecompressFiles(path);
+ return path;
+}
+
std::string GetElfFile() {
- return android::base::GetExecutableDirectory() + "/benchmarks/files/libart_arm.so";
+ return GetBenchmarkFilesDirectory() + "libart_arm.so";
}
std::string GetSymbolSortedElfFile() {
- return android::base::GetExecutableDirectory() + "/benchmarks/files/boot_arm.oat";
+ return GetBenchmarkFilesDirectory() + "boot_arm.oat";
}
-std::string GetCompressedElfFile() {
- // Both are the same right now.
- return GetSymbolSortedElfFile();
+std::string GetLargeCompressedFrameElfFile() {
+ return GetBenchmarkFilesDirectory() + "libpac.so";
+}
+
+std::string GetLargeEhFrameElfFile() {
+ return GetBenchmarkFilesDirectory() + "libLLVM_android.so";
}
#if defined(__BIONIC__)
@@ -60,3 +76,42 @@
}
}
#endif
+
+void MemoryTracker::SetBenchmarkCounters(benchmark::State& state) {
+ total_iterations_ += state.iterations();
+#if defined(__BIONIC__)
+ state.counters["MEAN_RSS_BYTES"] = total_rss_bytes_ / static_cast<double>(total_iterations_);
+ state.counters["MAX_RSS_BYTES"] = max_rss_bytes_;
+ state.counters["MIN_RSS_BYTES"] = min_rss_bytes_;
+#endif
+ state.counters["MEAN_ALLOCATED_BYTES"] =
+ total_alloc_bytes_ / static_cast<double>(total_iterations_);
+ state.counters["MAX_ALLOCATED_BYTES"] = max_alloc_bytes_;
+ state.counters["MIN_ALLOCATED_BYTES"] = min_alloc_bytes_;
+}
+
+void MemoryTracker::StartTrackingAllocations() {
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+ rss_bytes_before_ = 0;
+ GatherRss(&rss_bytes_before_);
+#endif
+ alloc_bytes_before_ = mallinfo().uordblks;
+}
+
+void MemoryTracker::StopTrackingAllocations() {
+#if defined(__BIONIC__)
+ mallopt(M_PURGE, 0);
+#endif
+ uint64_t bytes_alloced = mallinfo().uordblks - alloc_bytes_before_;
+ total_alloc_bytes_ += bytes_alloced;
+ if (bytes_alloced > max_alloc_bytes_) max_alloc_bytes_ = bytes_alloced;
+ if (bytes_alloced < min_alloc_bytes_) min_alloc_bytes_ = bytes_alloced;
+#if defined(__BIONIC__)
+ uint64_t rss_bytes = 0;
+ GatherRss(&rss_bytes);
+ total_rss_bytes_ += rss_bytes - rss_bytes_before_;
+ if (rss_bytes > max_rss_bytes_) max_rss_bytes_ = rss_bytes;
+ if (rss_bytes < min_rss_bytes_) min_rss_bytes_ = rss_bytes;
+#endif
+}
diff --git a/libunwindstack/benchmarks/Utils.h b/libunwindstack/benchmarks/Utils.h
index bee6efc..2d05af9 100644
--- a/libunwindstack/benchmarks/Utils.h
+++ b/libunwindstack/benchmarks/Utils.h
@@ -14,18 +14,25 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_UTILS_H
-#define _LIBUNWINDSTACK_UTILS_H
+#pragma once
+#include <benchmark/benchmark.h>
#include <stdint.h>
#include <string>
+std::string GetBenchmarkFilesDirectory();
+
std::string GetElfFile();
std::string GetSymbolSortedElfFile();
-std::string GetCompressedElfFile();
+// GetLargeCompressedFrameElfFile and GetLargeEhFrameElfFile were added to provide larger
+// ELF files for more representative benchmarks. Theses ELF files will enable validation
+// of optimizations to the unwindstack::Elf.
+std::string GetLargeCompressedFrameElfFile();
+
+std::string GetLargeEhFrameElfFile();
#if defined(__BIONIC__)
@@ -36,4 +43,25 @@
#endif
-#endif // _LIBUNWINDSTACK_UTILS_h
+class MemoryTracker {
+ public:
+ void StartTrackingAllocations();
+ void StopTrackingAllocations();
+ void SetBenchmarkCounters(benchmark::State& state);
+
+ private:
+#if defined(__BIONIC__)
+ uint64_t total_rss_bytes_ = 0;
+ uint64_t min_rss_bytes_ = 0;
+ uint64_t max_rss_bytes_ = 0;
+ uint64_t rss_bytes_before_;
+#endif
+ uint64_t total_alloc_bytes_ = 0;
+ uint64_t min_alloc_bytes_ = std::numeric_limits<uint64_t>::max();
+ uint64_t max_alloc_bytes_ = 0;
+ uint64_t alloc_bytes_before_;
+ // Benchmarks may run multiple times (the whole benchmark not just what is in the ranged based
+ // for loop) but this instance is not destructed and re-constructed each time. So this holds the
+ // total number of iterations of the ranged for loop across all runs of a single benchmark.
+ size_t total_iterations_ = 0;
+};
diff --git a/libunwindstack/benchmarks/files/boot_arm.oat b/libunwindstack/benchmarks/files/boot_arm.oat
deleted file mode 100644
index 51188eb..0000000
--- a/libunwindstack/benchmarks/files/boot_arm.oat
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/benchmarks/files/boot_arm.oat.gz b/libunwindstack/benchmarks/files/boot_arm.oat.gz
new file mode 100644
index 0000000..4b236b8
--- /dev/null
+++ b/libunwindstack/benchmarks/files/boot_arm.oat.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libLLVM_android.so.gz b/libunwindstack/benchmarks/files/libLLVM_android.so.gz
new file mode 100644
index 0000000..5edc266
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libLLVM_android.so.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libart_arm.so b/libunwindstack/benchmarks/files/libart_arm.so
deleted file mode 100644
index 2201faf..0000000
--- a/libunwindstack/benchmarks/files/libart_arm.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libart_arm.so.gz b/libunwindstack/benchmarks/files/libart_arm.so.gz
new file mode 100644
index 0000000..963f453
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libart_arm.so.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/files/libpac.so.gz b/libunwindstack/benchmarks/files/libpac.so.gz
new file mode 100644
index 0000000..a5df2be
--- /dev/null
+++ b/libunwindstack/benchmarks/files/libpac.so.gz
Binary files differ
diff --git a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
index 69c6091..78c8c13 100644
--- a/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
+++ b/libunwindstack/benchmarks/local_unwind_benchmarks.cpp
@@ -22,12 +22,13 @@
#include <android-base/strings.h>
-#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
#include <unwindstack/Unwinder.h>
+#include "MemoryLocalUnsafe.h"
constexpr size_t kMaxFrames = 32;
@@ -75,13 +76,6 @@
return unwinder.NumFrames();
}
-static size_t LocalUnwind(void* unwind_ptr) {
- unwindstack::LocalUnwinder* unwinder = reinterpret_cast<unwindstack::LocalUnwinder*>(unwind_ptr);
- std::vector<unwindstack::LocalFrameData> frame_info;
- unwinder->Unwind(&frame_info, kMaxFrames);
- return frame_info.size();
-}
-
static void BM_local_unwind_uncached_process_memory(benchmark::State& state) {
auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
unwindstack::LocalMaps maps;
@@ -106,6 +100,54 @@
}
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;
@@ -142,16 +184,6 @@
}
BENCHMARK(BM_local_unwind_local_updatable_maps_thread_cached);
-static void BM_local_unwind_local_unwinder(benchmark::State& state) {
- unwindstack::LocalUnwinder unwinder;
- if (!unwinder.Init()) {
- state.SkipWithError("Failed to init local unwinder.");
- }
-
- Run(state, LocalUnwind, &unwinder);
-}
-BENCHMARK(BM_local_unwind_local_unwinder);
-
static void BM_local_unwind_uncached_process_memory_no_func_names(benchmark::State& state) {
auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
unwindstack::LocalMaps maps;
@@ -199,3 +231,15 @@
Run(state, Unwind, &data);
}
BENCHMARK(BM_local_unwind_local_updatable_maps_cached_no_func_names);
+
+static void BM_local_unwind_uncached_process_memory_unsafe_reads(benchmark::State& state) {
+ std::shared_ptr<unwindstack::Memory> process_memory(new unwindstack::MemoryLocalUnsafe());
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ }
+
+ UnwindData data = {.process_memory = process_memory, .maps = &maps, .resolve_names = true};
+ Run(state, Unwind, &data);
+}
+BENCHMARK(BM_local_unwind_uncached_process_memory_unsafe_reads);
diff --git a/libunwindstack/benchmarks/main.cpp b/libunwindstack/benchmarks/main.cpp
index 02b2fdf..3fc1db9 100644
--- a/libunwindstack/benchmarks/main.cpp
+++ b/libunwindstack/benchmarks/main.cpp
@@ -16,4 +16,80 @@
#include <benchmark/benchmark.h>
-BENCHMARK_MAIN();
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <android-base/strings.h>
+
+#include <string>
+#include <vector>
+
+#if defined(__APPLE__)
+
+// Darwin doesn't support this, so do nothing.
+bool LockToCPU(int) {
+ return false;
+}
+
+#else
+
+#include <errno.h>
+#include <sched.h>
+
+bool LockToCPU(int lock_cpu) {
+ cpu_set_t cpuset;
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(lock_cpu, &cpuset);
+ if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
+ if (errno == EINVAL) {
+ printf("Invalid cpu %d\n", lock_cpu);
+ } else {
+ perror("sched_setaffinity failed");
+ }
+ return false;
+ }
+
+ printf("Locked to cpu %d\n", lock_cpu);
+ return true;
+}
+
+#endif
+
+int main(int argc, char** argv) {
+#if defined(__BIONIC__)
+ // Enable decay time option to allow frees to run faster at the cost of slightly increasing RSS.
+ // All applications on Android run with this option enabled.
+ mallopt(M_DECAY_TIME, 1);
+#endif
+ std::vector<char*> new_argv;
+ // The first argument is not an option, so add it as is.
+ new_argv.push_back(argv[0]);
+
+ // Look for the special option --benchmark_lock_cpu.
+ int lock_cpu = -1;
+ for (int i = 1; i < argc; i++) {
+ if (android::base::StartsWith(argv[i], "--benchmark_cpu=")) {
+ char* endptr;
+ long cpu = strtol(&argv[i][16], &endptr, 10);
+ if (endptr == nullptr || *endptr != '\0' || cpu > INT_MAX || cpu < 0) {
+ printf("Malformed value for --benchmark_cpu, requires a valid positive number.\n");
+ return 1;
+ }
+ lock_cpu = cpu;
+ } else {
+ new_argv.push_back(argv[i]);
+ }
+ }
+ new_argv.push_back(nullptr);
+
+ if (lock_cpu != -1 && !LockToCPU(lock_cpu)) {
+ return 1;
+ }
+
+ int new_argc = new_argv.size() - 1;
+ ::benchmark::Initialize(&new_argc, new_argv.data());
+ if (::benchmark::ReportUnrecognizedArguments(new_argc, new_argv.data())) return 1;
+ ::benchmark::RunSpecifiedBenchmarks();
+}
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 @@
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/GlobalDebugInterface.h b/libunwindstack/include/GlobalDebugInterface.h
index 45eec63..b1e9106 100644
--- a/libunwindstack/include/GlobalDebugInterface.h
+++ b/libunwindstack/include/GlobalDebugInterface.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_GLOBAL_DEBUG_INTERFACE_H
-#define _LIBUNWINDSTACK_GLOBAL_DEBUG_INTERFACE_H
+#pragma once
#include <stdint.h>
#include <memory>
@@ -41,5 +40,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_GLOBAL_DEBUG_INTERFACE_H
diff --git a/libunwindstack/include/unwindstack/AndroidUnwinder.h b/libunwindstack/include/unwindstack/AndroidUnwinder.h
new file mode 100644
index 0000000..de5579d
--- /dev/null
+++ b/libunwindstack/include/unwindstack/AndroidUnwinder.h
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#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
diff --git a/libunwindstack/include/unwindstack/Arch.h b/libunwindstack/include/unwindstack/Arch.h
index 7060004..42180b4 100644
--- a/libunwindstack/include/unwindstack/Arch.h
+++ b/libunwindstack/include/unwindstack/Arch.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_ARCH_H
-#define _LIBUNWINDSTACK_ARCH_H
+#pragma once
#include <stddef.h>
@@ -43,5 +42,3 @@
}
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_ARCH_H
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
index 2a1b770..101d772 100644
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ b/libunwindstack/include/unwindstack/DexFiles.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DEX_FILES_H
-#define _LIBUNWINDSTACK_DEX_FILES_H
+#pragma once
#include <stdint.h>
@@ -36,5 +35,3 @@
std::vector<std::string> search_libs = {});
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DEX_FILES_H
diff --git a/libunwindstack/include/unwindstack/DwarfError.h b/libunwindstack/include/unwindstack/DwarfError.h
index 763e2cb..6143523 100644
--- a/libunwindstack/include/unwindstack/DwarfError.h
+++ b/libunwindstack/include/unwindstack/DwarfError.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
-#define _LIBUNWINDSTACK_DWARF_ERROR_H
+#pragma once
#include <stdint.h>
@@ -40,5 +39,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 22ed47a..9726f15 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_LOCATION_H
-#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+#pragma once
#include <stdint.h>
@@ -49,5 +48,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/include/unwindstack/DwarfMemory.h b/libunwindstack/include/unwindstack/DwarfMemory.h
index c45699a..2ef3b30 100644
--- a/libunwindstack/include/unwindstack/DwarfMemory.h
+++ b/libunwindstack/include/unwindstack/DwarfMemory.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_MEMORY_H
-#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+#pragma once
#include <stdint.h>
@@ -72,5 +71,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 71ca62e..33435b2 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_SECTION_H
-#define _LIBUNWINDSTACK_DWARF_SECTION_H
+#pragma once
#include <stdint.h>
@@ -179,5 +178,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_SECTION_H
diff --git a/libunwindstack/include/unwindstack/DwarfStructs.h b/libunwindstack/include/unwindstack/DwarfStructs.h
index 3d8c2db..4c05360 100644
--- a/libunwindstack/include/unwindstack/DwarfStructs.h
+++ b/libunwindstack/include/unwindstack/DwarfStructs.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DWARF_STRUCTS_H
-#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+#pragma once
#include <stdint.h>
@@ -51,5 +50,3 @@
constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index 01d9af9..0985042 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_ELF_H
-#define _LIBUNWINDSTACK_ELF_H
+#pragma once
#include <stddef.h>
@@ -68,6 +67,8 @@
std::string GetBuildID();
+ std::string GetPrintableBuildID();
+
int64_t GetLoadBias() { return load_bias_; }
bool IsValidPc(uint64_t pc);
@@ -100,14 +101,20 @@
static std::string GetBuildID(Memory* memory);
+ // Caching cannot be enabled/disabled while unwinding. It is assumed
+ // that once enabled, it remains enabled while all unwinds are running.
+ // If the state of the caching changes while unwinding is occurring,
+ // it could cause crashes.
static void SetCachingEnabled(bool enable);
+
static bool CachingEnabled() { return cache_enabled_; }
static void CacheLock();
static void CacheUnlock();
static void CacheAdd(MapInfo* info);
static bool CacheGet(MapInfo* info);
- static bool CacheAfterCreateMemory(MapInfo* info);
+
+ static std::string GetPrintableBuildID(std::string& build_id);
protected:
bool valid_ = false;
@@ -124,10 +131,9 @@
std::unique_ptr<ElfInterface> gnu_debugdata_interface_;
static bool cache_enabled_;
- static std::unordered_map<std::string, std::pair<std::shared_ptr<Elf>, bool>>* cache_;
+ static std::unordered_map<std::string, std::unordered_map<uint64_t, std::shared_ptr<Elf>>>*
+ cache_;
static std::mutex* cache_lock_;
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_ELF_H
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 23b7256..a192450 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_ELF_INTERFACE_H
-#define _LIBUNWINDSTACK_ELF_INTERFACE_H
+#pragma once
#include <elf.h>
#include <stdint.h>
@@ -225,5 +224,3 @@
using ElfInterface64 = ElfInterfaceImpl<ElfTypes64>;
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_ELF_INTERFACE_H
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 1610d55..3a3c646 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_ERROR_H
-#define _LIBUNWINDSTACK_ERROR_H
+#pragma once
#include <stdint.h>
@@ -41,7 +40,10 @@
// 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 +70,12 @@
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";
}
}
@@ -78,5 +86,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_ERROR_H
diff --git a/libunwindstack/include/unwindstack/Global.h b/libunwindstack/include/unwindstack/Global.h
index b790832..d12ecb1 100644
--- a/libunwindstack/include/unwindstack/Global.h
+++ b/libunwindstack/include/unwindstack/Global.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_GLOBAL_H
-#define _LIBUNWINDSTACK_GLOBAL_H
+#pragma once
#include <stdint.h>
@@ -59,5 +58,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_GLOBAL_H
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index cfa8f7e..8653f94 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_JIT_DEBUG_H
-#define _LIBUNWINDSTACK_JIT_DEBUG_H
+#pragma once
#include <stdint.h>
@@ -36,5 +35,3 @@
std::vector<std::string> search_libs = {});
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_JIT_DEBUG_H
diff --git a/libunwindstack/include/unwindstack/LocalUnwinder.h b/libunwindstack/include/unwindstack/LocalUnwinder.h
deleted file mode 100644
index b9123b3..0000000
--- a/libunwindstack/include/unwindstack/LocalUnwinder.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBUNWINDSTACK_LOCAL_UNWINDER_H
-#define _LIBUNWINDSTACK_LOCAL_UNWINDER_H
-
-#include <pthread.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <unwindstack/Error.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
-
-namespace unwindstack {
-
-// Forward declarations.
-class Elf;
-class MapInfo;
-
-struct LocalFrameData {
- LocalFrameData(MapInfo* map_info, uint64_t pc, uint64_t rel_pc, const std::string& function_name,
- uint64_t function_offset)
- : map_info(map_info),
- pc(pc),
- rel_pc(rel_pc),
- function_name(function_name),
- function_offset(function_offset) {}
-
- MapInfo* map_info;
- uint64_t pc;
- uint64_t rel_pc;
- std::string function_name;
- uint64_t function_offset;
-};
-
-// This is a specialized class that should only be used for doing local unwinds.
-// The Unwind call can be made as multiple times on the same object, and it can
-// be called by multiple threads at the same time.
-// It is designed to be used in debugging circumstances to get a stack trace
-// as fast as possible.
-class LocalUnwinder {
- public:
- LocalUnwinder() = default;
- LocalUnwinder(const std::vector<std::string>& skip_libraries) : skip_libraries_(skip_libraries) {}
- ~LocalUnwinder() = default;
-
- bool Init();
-
- bool Unwind(std::vector<LocalFrameData>* frame_info, size_t max_frames);
-
- bool ShouldSkipLibrary(const std::string& map_name);
-
- MapInfo* GetMapInfo(uint64_t pc);
-
- ErrorCode LastErrorCode() { return last_error_.code; }
- uint64_t LastErrorAddress() { return last_error_.address; }
-
- private:
- pthread_rwlock_t maps_rwlock_;
- std::unique_ptr<LocalUpdatableMaps> maps_ = nullptr;
- std::shared_ptr<Memory> process_memory_;
- std::vector<std::string> skip_libraries_;
- ErrorData last_error_;
-};
-
-} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_LOCAL_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/Log.h b/libunwindstack/include/unwindstack/Log.h
index a4f763d..34eb218 100644
--- a/libunwindstack/include/unwindstack/Log.h
+++ b/libunwindstack/include/unwindstack/Log.h
@@ -14,17 +14,24 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_LOG_H
-#define _LIBUNWINDSTACK_LOG_H
+#pragma once
+#include <stdarg.h>
#include <stdint.h>
+#if !defined(__printflike)
+#define __printflike(x, y) __attribute__((__format__(printf, x, y)))
+#endif
+
namespace unwindstack {
-void log_to_stdout(bool enable);
-void log(uint8_t indent, const char* format, ...);
-void log_async_safe(const char* format, ...);
+namespace Log {
+
+void Error(const char* format, ...) __printflike(1, 2);
+void Info(const char* format, ...) __printflike(1, 2);
+void Info(uint8_t indent, const char* format, ...) __printflike(2, 3);
+void AsyncSafe(const char* format, ...) __printflike(1, 2);
+
+} // namespace Log
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_LOG_H
diff --git a/libunwindstack/include/unwindstack/MachineArm.h b/libunwindstack/include/unwindstack/MachineArm.h
index 3f902b1..6b8198e 100644
--- a/libunwindstack/include/unwindstack/MachineArm.h
+++ b/libunwindstack/include/unwindstack/MachineArm.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MACHINE_ARM_H
-#define _LIBUNWINDSTACK_MACHINE_ARM_H
+#pragma once
#include <stdint.h>
@@ -46,5 +45,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MACHINE_ARM_H
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index 358e3d9..f1b7c1d 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MACHINE_ARM64_H
-#define _LIBUNWINDSTACK_MACHINE_ARM64_H
+#pragma once
#include <stdint.h>
@@ -70,5 +69,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MACHINE_ARM64_H
diff --git a/libunwindstack/include/unwindstack/MachineMips.h b/libunwindstack/include/unwindstack/MachineMips.h
index 2dfb1e9..6a96536 100644
--- a/libunwindstack/include/unwindstack/MachineMips.h
+++ b/libunwindstack/include/unwindstack/MachineMips.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MACHINE_MIPS_H
-#define _LIBUNWINDSTACK_MACHINE_MIPS_H
+#pragma once
#include <stdint.h>
@@ -62,5 +61,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MACHINE_MIPS_H
\ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineMips64.h b/libunwindstack/include/unwindstack/MachineMips64.h
index 34addf2..e148efc 100644
--- a/libunwindstack/include/unwindstack/MachineMips64.h
+++ b/libunwindstack/include/unwindstack/MachineMips64.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MACHINE_MIPS64_H
-#define _LIBUNWINDSTACK_MACHINE_MIPS64_H
+#pragma once
#include <stdint.h>
@@ -62,5 +61,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MACHINE_MIPS64_H
\ No newline at end of file
diff --git a/libunwindstack/include/unwindstack/MachineX86.h b/libunwindstack/include/unwindstack/MachineX86.h
index 02adb98..ff4fd4b 100644
--- a/libunwindstack/include/unwindstack/MachineX86.h
+++ b/libunwindstack/include/unwindstack/MachineX86.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MACHINE_X86_H
-#define _LIBUNWINDSTACK_MACHINE_X86_H
+#pragma once
#include <stdint.h>
@@ -47,5 +46,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MACHINE_X86_H
diff --git a/libunwindstack/include/unwindstack/MachineX86_64.h b/libunwindstack/include/unwindstack/MachineX86_64.h
index af33fea..66670e3 100644
--- a/libunwindstack/include/unwindstack/MachineX86_64.h
+++ b/libunwindstack/include/unwindstack/MachineX86_64.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MACHINE_X86_64_H
-#define _LIBUNWINDSTACK_MACHINE_X86_64_H
+#pragma once
#include <stdint.h>
@@ -48,5 +47,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MACHINE_X86_64_H
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 727dc68..12711f5 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MAP_INFO_H
-#define _LIBUNWINDSTACK_MAP_INFO_H
+#pragma once
#include <stdint.h>
@@ -38,7 +37,7 @@
// (for example, 400 process * 400 maps * 128 bytes = 20 MB + string data).
class MapInfo {
public:
- MapInfo(MapInfo* prev_map, MapInfo* prev_real_map, uint64_t start, uint64_t end, uint64_t offset,
+ MapInfo(std::shared_ptr<MapInfo>& prev_map, uint64_t start, uint64_t end, uint64_t offset,
uint64_t flags, SharedString name)
: start_(start),
end_(end),
@@ -46,16 +45,35 @@
flags_(flags),
name_(name),
elf_fields_(nullptr),
- prev_map_(prev_map),
- prev_real_map_(prev_real_map) {
- if (prev_real_map != nullptr) prev_real_map->next_real_map_ = this;
+ prev_map_(prev_map) {}
+ MapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, SharedString name)
+ : start_(start),
+ end_(end),
+ offset_(offset),
+ flags_(flags),
+ name_(name),
+ elf_fields_(nullptr) {}
+
+ static inline std::shared_ptr<MapInfo> Create(std::shared_ptr<MapInfo>& prev_map,
+ uint64_t start, uint64_t end, uint64_t offset,
+ uint64_t flags, SharedString name) {
+ auto map_info = std::make_shared<MapInfo>(prev_map, start, end, offset, flags, name);
+ if (prev_map) {
+ prev_map->next_map_ = map_info;
+ }
+ return map_info;
}
+ static inline std::shared_ptr<MapInfo> Create(uint64_t start, uint64_t end, uint64_t offset,
+ uint64_t flags, SharedString name) {
+ return std::make_shared<MapInfo>(start, end, offset, flags, name);
+ }
+
~MapInfo();
// Cached data for mapped ELF files.
// We allocate this structure lazily since there are much fewer ELFs than maps.
struct ElfFields {
- ElfFields() : load_bias_(INT64_MAX), build_id_(0) {}
+ ElfFields() : load_bias_(UINT64_MAX), build_id_(0) {}
std::shared_ptr<Elf> elf_;
// The offset of the beginning of this mapping to the beginning of the
@@ -69,7 +87,7 @@
// shared libraries into a read-only and read-execute map.
uint64_t elf_start_offset_ = 0;
- std::atomic_int64_t load_bias_;
+ std::atomic_uint64_t load_bias_;
// This is a pointer to a new'd std::string.
// Using an atomic value means that we don't need to lock and will
@@ -83,6 +101,38 @@
std::mutex elf_mutex_;
};
+ // True if the file named by this map is not actually readable and the
+ // elf is using the data in memory.
+ bool ElfFileNotReadable();
+
+ // This is the previous map with the same name that is not empty and with
+ // a 0 offset. For example, this set of maps:
+ // 1000-2000 r--p 000000 00:00 0 libc.so
+ // 2000-3000 ---p 000000 00:00 0
+ // 3000-4000 r-xp 003000 00:00 0 libc.so
+ // The last map's prev_map would point to the 2000-3000 map, while
+ // GetPrevRealMap() would point to the 1000-2000 map.
+ // NOTE: If a map is encountered that has a non-zero offset, or has a
+ // a name different from the current map, then GetPrevRealMap()
+ // returns nullptr.
+ std::shared_ptr<MapInfo> GetPrevRealMap();
+ // This is the next map with the same name that is not empty and with
+ // a 0 offset. For the example above, the first map's GetNextRealMap()
+ // would be the 3000-4000 map.
+ // NOTE: If a map is encountered that has a non-zero offset, or has a
+ // a name different from the current map, then GetNextRealMap()
+ // returns nullptr.
+ std::shared_ptr<MapInfo> GetNextRealMap();
+
+ // This is guaranteed to give out the Elf object associated with the
+ // object. The invariant is that once the Elf object is set under the
+ // lock in a MapInfo object it never changes and is not freed until
+ // the MapInfo object is destructed.
+ inline Elf* GetElfObj() {
+ std::lock_guard<std::mutex> guard(elf_mutex());
+ return elf().get();
+ }
+
inline uint64_t start() const { return start_; }
inline void set_start(uint64_t value) { start_ = value; }
@@ -109,8 +159,8 @@
inline uint64_t elf_start_offset() { return GetElfFields().elf_start_offset_; }
inline void set_elf_start_offset(uint64_t value) { GetElfFields().elf_start_offset_ = value; }
- inline std::atomic_int64_t& load_bias() { return GetElfFields().load_bias_; }
- inline void set_load_bias(int64_t value) { GetElfFields().load_bias_ = value; }
+ inline std::atomic_uint64_t& load_bias() { return GetElfFields().load_bias_; }
+ inline void set_load_bias(uint64_t value) { GetElfFields().load_bias_ = value; }
inline std::atomic<SharedString*>& build_id() { return GetElfFields().build_id_; }
inline void set_build_id(SharedString* value) { GetElfFields().build_id_ = value; }
@@ -118,20 +168,28 @@
inline bool memory_backed_elf() { return GetElfFields().memory_backed_elf_; }
inline void set_memory_backed_elf(bool value) { GetElfFields().memory_backed_elf_ = value; }
- inline MapInfo* prev_map() const { return prev_map_; }
- inline void set_prev_map(MapInfo* value) { prev_map_ = value; }
+ inline std::shared_ptr<MapInfo> prev_map() const { return prev_map_.lock(); }
+ inline void set_prev_map(std::shared_ptr<MapInfo>& value) { prev_map_ = value; }
- inline MapInfo* prev_real_map() const { return prev_real_map_; }
- inline void set_prev_real_map(MapInfo* value) { prev_real_map_ = value; }
-
- inline MapInfo* next_real_map() const { return next_real_map_; }
- inline void set_next_real_map(MapInfo* value) { next_real_map_ = value; }
+ inline std::shared_ptr<MapInfo> next_map() const { return next_map_.lock(); }
+ inline void set_next_map(std::shared_ptr<MapInfo>& value) { next_map_ = value; }
// This function guarantees it will never return nullptr.
Elf* GetElf(const std::shared_ptr<Memory>& process_memory, ArchEnum expected_arch);
+ // Guaranteed to return the proper value if GetElf() has been called.
+ uint64_t GetLoadBias();
+
+ // Will get the proper value even if GetElf() hasn't been called.
uint64_t GetLoadBias(const std::shared_ptr<Memory>& process_memory);
+ // This returns the name of the map plus the soname if this particular
+ // map represents an elf file that is contained inside of another file.
+ // The format of this soname embedded name is:
+ // file.apk!libutils.so
+ // Otherwise, this function only returns the name of the map.
+ std::string GetFullName();
+
Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
bool GetFunctionName(uint64_t addr, SharedString* name, uint64_t* func_offset);
@@ -168,20 +226,8 @@
std::atomic<ElfFields*> elf_fields_;
- MapInfo* prev_map_ = nullptr;
- // This is the previous map that is not empty with a 0 offset. For
- // example, this set of maps:
- // 1000-2000 r--p 000000 00:00 0 libc.so
- // 2000-3000 ---p 000000 00:00 0 libc.so
- // 3000-4000 r-xp 003000 00:00 0 libc.so
- // The last map's prev_map would point to the 2000-3000 map, while the
- // prev_real_map would point to the 1000-2000 map.
- MapInfo* prev_real_map_ = nullptr;
-
- // Same as above but set to point to the next map.
- MapInfo* next_real_map_ = nullptr;
+ std::weak_ptr<MapInfo> prev_map_;
+ std::weak_ptr<MapInfo> next_map_;
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index be013a4..d9e1383 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MAPS_H
-#define _LIBUNWINDSTACK_MAPS_H
+#pragma once
#include <pthread.h>
#include <sys/types.h>
@@ -50,34 +49,35 @@
Maps(Maps&&) = default;
Maps& operator=(Maps&&) = default;
- virtual MapInfo* Find(uint64_t pc);
+ virtual std::shared_ptr<MapInfo> Find(uint64_t pc);
virtual bool Parse();
virtual const std::string GetMapsFile() const { return ""; }
+ void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name);
void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
uint64_t load_bias);
void Sort();
- typedef std::vector<std::unique_ptr<MapInfo>>::iterator iterator;
+ typedef std::vector<std::shared_ptr<MapInfo>>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
- typedef std::vector<std::unique_ptr<MapInfo>>::const_iterator const_iterator;
+ typedef std::vector<std::shared_ptr<MapInfo>>::const_iterator const_iterator;
const_iterator begin() const { return maps_.begin(); }
const_iterator end() const { return maps_.end(); }
size_t Total() { return maps_.size(); }
- MapInfo* Get(size_t index) {
+ std::shared_ptr<MapInfo> Get(size_t index) {
if (index >= maps_.size()) return nullptr;
- return maps_[index].get();
+ return maps_[index];
}
protected:
- std::vector<std::unique_ptr<MapInfo>> maps_;
+ std::vector<std::shared_ptr<MapInfo>> maps_;
};
class RemoteMaps : public Maps {
@@ -102,7 +102,7 @@
LocalUpdatableMaps();
virtual ~LocalUpdatableMaps() = default;
- MapInfo* Find(uint64_t pc) override;
+ std::shared_ptr<MapInfo> Find(uint64_t pc) override;
bool Parse() override;
@@ -110,9 +110,7 @@
bool Reparse(/*out*/ bool* any_changed = nullptr);
- protected:
- std::vector<std::unique_ptr<MapInfo>> saved_maps_;
-
+ private:
pthread_rwlock_t maps_rwlock_;
};
@@ -139,5 +137,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MAPS_H
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index b7bede6..d6ca29e 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_MEMORY_H
-#define _LIBUNWINDSTACK_MEMORY_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -65,5 +64,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 45e96df..e49370d 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_H
-#define _LIBUNWINDSTACK_REGS_H
+#pragma once
#include <stdint.h>
#include <unistd.h>
@@ -82,6 +81,7 @@
virtual Regs* Clone() = 0;
static ArchEnum CurrentArch();
+ static ArchEnum RemoteGetArch(pid_t pid);
static Regs* RemoteGet(pid_t pid);
static Regs* CreateFromUcontext(ArchEnum arch, void* ucontext);
static Regs* CreateFromLocal();
@@ -116,5 +116,3 @@
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf, ArchEnum arch);
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index fbb34e7..5596605 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_ARM_H
-#define _LIBUNWINDSTACK_REGS_ARM_H
+#pragma once
#include <stdint.h>
@@ -56,5 +55,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_ARM_H
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 508366e..d12a043 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_ARM64_H
-#define _LIBUNWINDSTACK_REGS_ARM64_H
+#pragma once
#include <stdint.h>
@@ -73,5 +72,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_ARM64_H
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index ac171b9..bf3f767 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
-#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+#pragma once
namespace unwindstack {
@@ -91,7 +90,4 @@
AsmGetRegs(regs->RawData());
}
-
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index dc09b83..61e4dbf 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_MIPS_H
-#define _LIBUNWINDSTACK_REGS_MIPS_H
+#pragma once
#include <stdint.h>
@@ -56,5 +55,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_MIPS_H
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 64a80dc..012a8f0 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_MIPS64_H
-#define _LIBUNWINDSTACK_REGS_MIPS64_H
+#pragma once
#include <stdint.h>
@@ -56,5 +55,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index cfbdda6..d8245ee 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_X86_H
-#define _LIBUNWINDSTACK_REGS_X86_H
+#pragma once
#include <stdint.h>
@@ -59,5 +58,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_X86_H
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index a11aef0..90fee93 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_REGS_X86_64_H
-#define _LIBUNWINDSTACK_REGS_X86_64_H
+#pragma once
#include <stdint.h>
@@ -59,5 +58,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_REGS_X86_64_H
diff --git a/libunwindstack/include/unwindstack/SharedString.h b/libunwindstack/include/unwindstack/SharedString.h
index a82c4fe..1eb4e21 100644
--- a/libunwindstack/include/unwindstack/SharedString.h
+++ b/libunwindstack/include/unwindstack/SharedString.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_SHAREDSTRING_H
-#define _LIBUNWINDSTACK_SHAREDSTRING_H
+#pragma once
#include <memory>
#include <string>
@@ -73,4 +72,3 @@
}
} // namespace unwindstack
-#endif // _LIBUNWINDSTACK_SHAREDSTRING_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm.h b/libunwindstack/include/unwindstack/UcontextArm.h
index 7d1ec3b..0d7f90d 100644
--- a/libunwindstack/include/unwindstack/UcontextArm.h
+++ b/libunwindstack/include/unwindstack/UcontextArm.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM_H
-#define _LIBUNWINDSTACK_UCONTEXT_ARM_H
+#pragma once
#include <stdint.h>
@@ -59,5 +58,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_UCONTEXT_ARM_H
diff --git a/libunwindstack/include/unwindstack/UcontextArm64.h b/libunwindstack/include/unwindstack/UcontextArm64.h
index a68be3b..49278b3 100644
--- a/libunwindstack/include/unwindstack/UcontextArm64.h
+++ b/libunwindstack/include/unwindstack/UcontextArm64.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_UCONTEXT_ARM64_H
-#define _LIBUNWINDSTACK_UCONTEXT_ARM64_H
+#pragma once
#include <stdint.h>
@@ -65,5 +64,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_UCONTEXT_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips.h b/libunwindstack/include/unwindstack/UcontextMips.h
index 02e33b6..c75e469 100644
--- a/libunwindstack/include/unwindstack/UcontextMips.h
+++ b/libunwindstack/include/unwindstack/UcontextMips.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS_H
-#define _LIBUNWINDSTACK_UCONTEXT_MIPS_H
+#pragma once
#include <stdint.h>
@@ -58,5 +57,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UcontextMips64.h b/libunwindstack/include/unwindstack/UcontextMips64.h
index 5b92a55..c63218f 100644
--- a/libunwindstack/include/unwindstack/UcontextMips64.h
+++ b/libunwindstack/include/unwindstack/UcontextMips64.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
-#define _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
+#pragma once
#include <stdint.h>
@@ -65,5 +64,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_UCONTEXT_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86.h b/libunwindstack/include/unwindstack/UcontextX86.h
index c96ebb7..9dfdf2a 100644
--- a/libunwindstack/include/unwindstack/UcontextX86.h
+++ b/libunwindstack/include/unwindstack/UcontextX86.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_H
-#define _LIBUNWINDSTACK_UCONTEXT_X86_H
+#pragma once
#include <stdint.h>
@@ -73,5 +72,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_UCONTEXT_X86_H
diff --git a/libunwindstack/include/unwindstack/UcontextX86_64.h b/libunwindstack/include/unwindstack/UcontextX86_64.h
index 4e163e5..2801217 100644
--- a/libunwindstack/include/unwindstack/UcontextX86_64.h
+++ b/libunwindstack/include/unwindstack/UcontextX86_64.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_UCONTEXT_X86_64_H
-#define _LIBUNWINDSTACK_UCONTEXT_X86_64_H
+#pragma once
#include <stdint.h>
@@ -78,5 +77,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_UCONTEXT_X86_64_H
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index bcd0d42..d78da5c 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_UNWINDER_H
-#define _LIBUNWINDSTACK_UNWINDER_H
+#pragma once
#include <stdint.h>
#include <sys/types.h>
@@ -49,18 +48,7 @@
SharedString function_name;
uint64_t function_offset = 0;
- SharedString map_name;
- // The offset from the first map representing the frame. When there are
- // two maps (read-only and read-execute) this will be the offset from
- // the read-only map. When there is only one map, this will be the
- // same as the actual offset of the map and match map_exact_offset.
- uint64_t map_elf_start_offset = 0;
- // The actual offset from the map where the pc lies.
- uint64_t map_exact_offset = 0;
- uint64_t map_start = 0;
- uint64_t map_end = 0;
- uint64_t map_load_bias = 0;
- int map_flags = 0;
+ std::shared_ptr<MapInfo> map_info;
};
class Unwinder {
@@ -94,6 +82,9 @@
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);
@@ -109,17 +100,11 @@
// set to an empty string and the function offset being set to zero.
void SetResolveNames(bool resolve) { resolve_names_ = resolve; }
- // Enable/disable soname printing the soname for a map name if the elf is
- // embedded in a file. This is enabled by default.
- // NOTE: This does nothing unless resolving names is enabled.
- void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; }
-
void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
void SetDexFiles(DexFiles* dex_files);
- bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
-
+ 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; }
@@ -138,6 +123,8 @@
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;
@@ -146,21 +133,18 @@
}
void FillInDexFrame();
- FrameData* FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t pc_adjustment);
+ FrameData* FillInFrame(std::shared_ptr<MapInfo>& map_info, Elf* elf, uint64_t rel_pc,
+ uint64_t pc_adjustment);
size_t max_frames_;
- Maps* maps_;
+ Maps* maps_ = nullptr;
Regs* regs_;
std::vector<FrameData> frames_;
std::shared_ptr<Memory> process_memory_;
JitDebug* jit_debug_ = nullptr;
DexFiles* dex_files_ = nullptr;
bool resolve_names_ = true;
- bool embedded_soname_ = true;
bool display_build_id_ = false;
- // True if at least one elf file is coming from memory and not the related
- // file. This is only true if there is an actual file backing up the elf.
- bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
uint64_t warnings_;
ArchEnum arch_ = ARCH_UNKNOWN;
@@ -170,14 +154,15 @@
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,
@@ -194,6 +179,7 @@
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;
@@ -201,7 +187,7 @@
void Unwind(const std::vector<std::string>*, const std::vector<std::string>*) override {}
- void UnwindWithSignal(int signal, pid_t tid,
+ void UnwindWithSignal(int signal, pid_t tid, std::unique_ptr<Regs>* initial_regs = nullptr,
const std::vector<std::string>* initial_map_names_to_skip = nullptr,
const std::vector<std::string>* map_suffixes_to_ignore = nullptr);
@@ -210,5 +196,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_UNWINDER_H
diff --git a/libunwindstack/include/unwindstack/UserArm.h b/libunwindstack/include/unwindstack/UserArm.h
index 7388c03..725a35b 100644
--- a/libunwindstack/include/unwindstack/UserArm.h
+++ b/libunwindstack/include/unwindstack/UserArm.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_USER_ARM_H
-#define _LIBUNWINDSTACK_USER_ARM_H
+#pragma once
namespace unwindstack {
@@ -36,5 +35,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_USER_ARM_H
diff --git a/libunwindstack/include/unwindstack/UserArm64.h b/libunwindstack/include/unwindstack/UserArm64.h
index d74983f..0e16cd6 100644
--- a/libunwindstack/include/unwindstack/UserArm64.h
+++ b/libunwindstack/include/unwindstack/UserArm64.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_USER_ARM64_H
-#define _LIBUNWINDSTACK_USER_ARM64_H
+#pragma once
namespace unwindstack {
@@ -39,5 +38,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_USER_ARM64_H
diff --git a/libunwindstack/include/unwindstack/UserMips.h b/libunwindstack/include/unwindstack/UserMips.h
index 184be4f..148ef93 100644
--- a/libunwindstack/include/unwindstack/UserMips.h
+++ b/libunwindstack/include/unwindstack/UserMips.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_USER_MIPS_H
-#define _LIBUNWINDSTACK_USER_MIPS_H
+#pragma once
namespace unwindstack {
@@ -41,5 +40,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_USER_MIPS_H
diff --git a/libunwindstack/include/unwindstack/UserMips64.h b/libunwindstack/include/unwindstack/UserMips64.h
index c46befd..9844307 100644
--- a/libunwindstack/include/unwindstack/UserMips64.h
+++ b/libunwindstack/include/unwindstack/UserMips64.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_USER_MIPS64_H
-#define _LIBUNWINDSTACK_USER_MIPS64_H
+#pragma once
namespace unwindstack {
@@ -41,5 +40,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_USER_MIPS64_H
diff --git a/libunwindstack/include/unwindstack/UserX86.h b/libunwindstack/include/unwindstack/UserX86.h
index a040560..9508010 100644
--- a/libunwindstack/include/unwindstack/UserX86.h
+++ b/libunwindstack/include/unwindstack/UserX86.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_USER_X86_H
-#define _LIBUNWINDSTACK_USER_X86_H
+#pragma once
namespace unwindstack {
@@ -52,5 +51,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_USER_X86_H
diff --git a/libunwindstack/include/unwindstack/UserX86_64.h b/libunwindstack/include/unwindstack/UserX86_64.h
index b80d201..d7ff2e2 100644
--- a/libunwindstack/include/unwindstack/UserX86_64.h
+++ b/libunwindstack/include/unwindstack/UserX86_64.h
@@ -26,8 +26,7 @@
* SUCH DAMAGE.
*/
-#ifndef _LIBUNWINDSTACK_USER_X86_64_H
-#define _LIBUNWINDSTACK_USER_X86_64_H
+#pragma once
namespace unwindstack {
@@ -62,5 +61,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_USER_X86_64_H
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rorx_arm64/fake.apk
similarity index 66%
copy from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
copy to libunwindstack/offline_files/apk_rorx_arm64/fake.apk
index 35a6bc5..9bd1f85 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rorx_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/links.txt b/libunwindstack/offline_files/apk_rorx_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/maps.txt b/libunwindstack/offline_files/apk_rorx_arm64/maps.txt
new file mode 100644
index 0000000..863c13e
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/maps.txt
@@ -0,0 +1,6 @@
+603b0c4000-603b0c5000 r--p 0 00:00 0 run
+603b0c5000-603b0c6000 r-xp 1000 00:00 0 run
+7426d2d000-7426d2e000 r--p 4000 00:00 0 fake.apk
+7426d2e000-7426d2f000 r-xp 5000 00:00 0 fake.apk
+76b6da8000-76b6de4000 r--p 0 00:00 0 libc.so
+76b6de4000-76b6e65000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/output.txt b/libunwindstack/offline_files/apk_rorx_arm64/output.txt
new file mode 100644
index 0000000..0636f7f
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/output.txt
@@ -0,0 +1,6 @@
+ #00 pc 0000000000001030 fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel4+28)
+ #01 pc 000000000000108c fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel3+28)
+ #02 pc 00000000000010b8 fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel2+28)
+ #03 pc 00000000000010e4 fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel1+28)
+ #04 pc 0000000000001154 run (main+256)
+ #05 pc 0000000000048b10 libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/regs.txt b/libunwindstack/offline_files/apk_rorx_arm64/regs.txt
new file mode 100644
index 0000000..da2ceb5
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 76bc49ee88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: e41
+x10: e41
+x11: 10
+x12: 76bc385f80
+x13: f66dca96235a549b
+x14: a83626c39758e9a
+x15: fabaf3feaa5334a
+x16: 7426d2f1e0
+x17: 7426d2e014
+x18: 76bb4bc000
+x19: 7fe740cd61
+x20: 3
+x21: 7fe740ce28
+x22: 76bb091000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe740ccc0
+lr: 7426d2e090
+sp: 7fe740cc90
+pc: 7426d2e030
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/run b/libunwindstack/offline_files/apk_rorx_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_arm64/stack.data b/libunwindstack/offline_files/apk_rorx_arm64/stack.data
new file mode 100644
index 0000000..5a609c7
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/fake.apk
similarity index 66%
copy from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
copy to libunwindstack/offline_files/apk_rorx_unreadable_arm64/fake.apk
index 35a6bc5..9bd1f85 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/links.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/maps.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/maps.txt
new file mode 100644
index 0000000..d0fd187
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/maps.txt
@@ -0,0 +1,6 @@
+603b0c4000-603b0c5000 r--p 0 00:00 0 run
+603b0c5000-603b0c6000 r-xp 1000 00:00 0 run
+7426d2d000-7426d2e000 r--p 4000 00:00 0 unreadable.apk
+7426d2e000-7426d2f000 r-xp 5000 00:00 0 unreadable.apk
+76b6da8000-76b6de4000 r--p 0 00:00 0 libc.so
+76b6de4000-76b6e65000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/output.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/output.txt
new file mode 100644
index 0000000..dddf4da
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/output.txt
@@ -0,0 +1,6 @@
+ #00 pc 0000000000001030 unreadable.apk (offset 0x4000)
+ #01 pc 000000000000108c unreadable.apk (offset 0x4000)
+ #02 pc 00000000000010b8 unreadable.apk (offset 0x4000)
+ #03 pc 00000000000010e4 unreadable.apk (offset 0x4000)
+ #04 pc 0000000000001154 run (main+256)
+ #05 pc 0000000000048b10 libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/regs.txt b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/regs.txt
new file mode 100644
index 0000000..da2ceb5
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 76bc49ee88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: e41
+x10: e41
+x11: 10
+x12: 76bc385f80
+x13: f66dca96235a549b
+x14: a83626c39758e9a
+x15: fabaf3feaa5334a
+x16: 7426d2f1e0
+x17: 7426d2e014
+x18: 76bb4bc000
+x19: 7fe740cd61
+x20: 3
+x21: 7fe740ce28
+x22: 76bb091000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe740ccc0
+lr: 7426d2e090
+sp: 7fe740cc90
+pc: 7426d2e030
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/run b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rorx_unreadable_arm64/stack.data b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/stack.data
new file mode 100644
index 0000000..5a609c7
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rorx_unreadable_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rx_arm64/fake.apk
similarity index 78%
rename from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
rename to libunwindstack/offline_files/apk_rx_arm64/fake.apk
index 35a6bc5..1f45187 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rx_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_arm64/links.txt b/libunwindstack/offline_files/apk_rx_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rx_arm64/maps.txt b/libunwindstack/offline_files/apk_rx_arm64/maps.txt
new file mode 100644
index 0000000..7e23cb0
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/maps.txt
@@ -0,0 +1,5 @@
+5e004ef000-5e004f0000 r--p 0 00:00 0 run
+5e004f0000-5e004f1000 r-xp 1000 00:00 0 run
+7cb0e62000-7cb0e63000 r-xp 4000 00:00 0 fake.apk
+7f410dc000-7f41118000 r--p 0 00:00 0 libc.so
+7f41118000-7f41199000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/apk_rx_arm64/output.txt b/libunwindstack/offline_files/apk_rx_arm64/output.txt
new file mode 100644
index 0000000..c033076
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/output.txt
@@ -0,0 +1,6 @@
+ #00 pc 000000000000066c fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel4+36)
+ #01 pc 00000000000006c0 fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel3+28)
+ #02 pc 00000000000006ec fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel2+28)
+ #03 pc 0000000000000718 fake.apk!libunwindstack_local.so (offset 0x4000) (TestlibLevel1+28)
+ #04 pc 0000000000001154 run (main+256)
+ #05 pc 0000000000048b10 libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rx_arm64/regs.txt b/libunwindstack/offline_files/apk_rx_arm64/regs.txt
new file mode 100644
index 0000000..1f6e4c6
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 7f42f67e88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: 1014
+x10: 1014
+x11: 10
+x12: 7f42e4ef80
+x13: 2f75fd4dd1b37121
+x14: 7
+x15: fabaf3feaa5334a
+x16: 7cb0e631e0
+x17: 7cb0e62648
+x18: 7f426ea000
+x19: 7fe563bf61
+x20: 3
+x21: 7fe563c028
+x22: 7f41b5a000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe563bec0
+lr: 7cb0e626c4
+sp: 7fe563be90
+pc: 7cb0e6266c
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rx_arm64/run b/libunwindstack/offline_files/apk_rx_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_arm64/stack.data b/libunwindstack/offline_files/apk_rx_arm64/stack.data
new file mode 100644
index 0000000..5ce83d4
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex b/libunwindstack/offline_files/apk_rx_unreadable_arm64/fake.apk
similarity index 78%
copy from libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
copy to libunwindstack/offline_files/apk_rx_unreadable_arm64/fake.apk
index 35a6bc5..1f45187 100644
--- a/libunwindstack/tests/files/offline/jit_debug_arm/137-cfi.odex
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/fake.apk
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/links.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/links.txt
new file mode 100644
index 0000000..42da529
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/links.txt
@@ -0,0 +1 @@
+../common/libc.so_3106fb6eab1b2dc6704157e908a50eba libc.so
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/maps.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/maps.txt
new file mode 100644
index 0000000..696c397
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/maps.txt
@@ -0,0 +1,5 @@
+5e004ef000-5e004f0000 r--p 0 00:00 0 run
+5e004f0000-5e004f1000 r-xp 1000 00:00 0 run
+7cb0e62000-7cb0e63000 r-xp 4000 00:00 0 unreadable.apk
+7f410dc000-7f41118000 r--p 0 00:00 0 libc.so
+7f41118000-7f41199000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/output.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/output.txt
new file mode 100644
index 0000000..c984b58
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/output.txt
@@ -0,0 +1,6 @@
+ #00 pc 000000000000066c unreadable.apk (offset 0x4000)
+ #01 pc 00000000000006c0 unreadable.apk (offset 0x4000)
+ #02 pc 00000000000006ec unreadable.apk (offset 0x4000)
+ #03 pc 0000000000000718 unreadable.apk (offset 0x4000)
+ #04 pc 0000000000001154 run (main+256)
+ #05 pc 0000000000048b10 libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/regs.txt b/libunwindstack/offline_files/apk_rx_unreadable_arm64/regs.txt
new file mode 100644
index 0000000..1f6e4c6
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 0
+x2: 7f42f67e88
+x3: 11b030000
+x4: 800000
+x5: 5462727542000000
+x6: 804275726254
+x7: 5678727440ff306b
+x8: 0
+x9: 1014
+x10: 1014
+x11: 10
+x12: 7f42e4ef80
+x13: 2f75fd4dd1b37121
+x14: 7
+x15: fabaf3feaa5334a
+x16: 7cb0e631e0
+x17: 7cb0e62648
+x18: 7f426ea000
+x19: 7fe563bf61
+x20: 3
+x21: 7fe563c028
+x22: 7f41b5a000
+x23: 0
+x24: 0
+x25: 0
+x26: 0
+x27: 0
+x28: 0
+x29: 7fe563bec0
+lr: 7cb0e626c4
+sp: 7fe563be90
+pc: 7cb0e6266c
+pst: 80001000
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/run b/libunwindstack/offline_files/apk_rx_unreadable_arm64/run
new file mode 100644
index 0000000..b984d44
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/run
Binary files differ
diff --git a/libunwindstack/offline_files/apk_rx_unreadable_arm64/stack.data b/libunwindstack/offline_files/apk_rx_unreadable_arm64/stack.data
new file mode 100644
index 0000000..5ce83d4
--- /dev/null
+++ b/libunwindstack/offline_files/apk_rx_unreadable_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/offline_files/art_quick_osr_stub_arm/libart.so.gz b/libunwindstack/offline_files/art_quick_osr_stub_arm/libart.so.gz
new file mode 100644
index 0000000..f0d1e92
--- /dev/null
+++ b/libunwindstack/offline_files/art_quick_osr_stub_arm/libart.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/art_quick_osr_stub_arm/libc.so.gz b/libunwindstack/offline_files/art_quick_osr_stub_arm/libc.so.gz
new file mode 100644
index 0000000..a7e4170
--- /dev/null
+++ b/libunwindstack/offline_files/art_quick_osr_stub_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/offline_files/art_quick_osr_stub_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/maps.txt
diff --git a/libunwindstack/offline_files/art_quick_osr_stub_arm/output.txt b/libunwindstack/offline_files/art_quick_osr_stub_arm/output.txt
new file mode 100644
index 0000000..76b288f
--- /dev/null
+++ b/libunwindstack/offline_files/art_quick_osr_stub_arm/output.txt
@@ -0,0 +1,25 @@
+ #00 pc 0000c788 <anonymous:d0250000> (com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)
+ #01 pc 0000cdd5 <anonymous:d0250000> (com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)
+ #02 pc 004135bb libart.so (art_quick_osr_stub+42)
+ #03 pc 002657a5 libart.so (art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, art::JValue*)+876)
+ #04 pc 004021a7 libart.so (MterpMaybeDoOnStackReplacement+86)
+ #05 pc 00412474 libart.so (ExecuteMterpImpl+66164)
+ #06 pc cd8365b0 <unknown>
+ #07 pc 001d7f1b libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+374)
+ #08 pc 001dc593 libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)
+ #09 pc 001f4d01 libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+732)
+ #10 pc 003fe427 libart.so (MterpInvokeInterface+1354)
+ #11 pc 00405b94 libart.so (ExecuteMterpImpl+14740)
+ #12 pc 7004873e <unknown>
+ #13 pc 001d7f1b libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+374)
+ #14 pc 001dc4d5 libart.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+92)
+ #15 pc 003f25ab libart.so (artQuickToInterpreterBridge+970)
+ #16 pc 00417aff libart.so (art_quick_to_interpreter_bridge+30)
+ #17 pc 00413575 libart.so (art_quick_invoke_stub_internal+68)
+ #18 pc 00418531 libart.so (art_quick_invoke_stub+236)
+ #19 pc 000b468d libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+136)
+ #20 pc 00362f49 libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+52)
+ #21 pc 00363cd9 libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue*)+332)
+ #22 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)
+ #23 pc 00062925 libc.so (__pthread_start(void*)+22)
+ #24 pc 0001de39 libc.so (__start_thread+24)
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt b/libunwindstack/offline_files/art_quick_osr_stub_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data b/libunwindstack/offline_files/art_quick_osr_stub_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
rename to libunwindstack/offline_files/art_quick_osr_stub_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/libc.so.gz b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/libc.so.gz
new file mode 100644
index 0000000..d9c3705
--- /dev/null
+++ b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/maps.txt
rename to libunwindstack/offline_files/bad_eh_frame_hdr_arm64/maps.txt
diff --git a/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/output.txt b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/output.txt
new file mode 100644
index 0000000..cdec0e4
--- /dev/null
+++ b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/output.txt
@@ -0,0 +1,5 @@
+ #00 pc 0000000000000550 waiter64
+ #01 pc 0000000000000568 waiter64
+ #02 pc 000000000000057c waiter64
+ #03 pc 0000000000000590 waiter64
+ #04 pc 00000000000a8e98 libc.so (__libc_init+88)
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/regs.txt
rename to libunwindstack/offline_files/bad_eh_frame_hdr_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/stack.data
rename to libunwindstack/offline_files/bad_eh_frame_hdr_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/waiter64.gz b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/waiter64.gz
new file mode 100644
index 0000000..7039b60
--- /dev/null
+++ b/libunwindstack/offline_files/bad_eh_frame_hdr_arm64/waiter64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/maps.txt
new file mode 100644
index 0000000..60ce7c7
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/maps.txt
@@ -0,0 +1,4 @@
+7b5e40e000-7b5e5d5000 r--p 0 00:00 0 libbluetooth.so
+7b5e5d5000-7b5ec43000 r-xp 1c7000 00:00 0 libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/output.txt
new file mode 100644
index 0000000..50852d6
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/output.txt
@@ -0,0 +1,13 @@
+ #00 pc 00000000007cbbdc libbluetooth.so (bluetooth::packet::Iterator<true>::operator*() const+368)
+ #01 pc 0000000000756ab0 libbluetooth.so (bluetooth::hci::LeExtendedAdvertisingReport::Parse(bluetooth::hci::LeExtendedAdvertisingReport*, bluetooth::packet::Iterator<true>)+1228)
+ #02 pc 0000000000756344 libbluetooth.so (bluetooth::hci::LeExtendedAdvertisingReportView::GetAdvertisingReports()+872)
+ #03 pc 0000000000750ec8 libbluetooth.so (bluetooth::hci::LeScanningManager::impl::handle_extended_advertising_report(bluetooth::hci::LeExtendedAdvertisingReportView)+116)
+ #04 pc 000000000074f48c libbluetooth.so (bluetooth::hci::LeScanningManager::impl::handle_scan_results(bluetooth::hci::LeMetaEventView)+760)
+ #05 pc 0000000000750284 libbluetooth.so (base::internal::Invoker<base::internal::BindState<void (bluetooth::hci::LeScanningManager::impl::*)(bluetooth::hci::LeMetaEventView), base::internal::UnretainedWrapper<bluetooth::hci::LeScanningManager::impl> >, void (bluetooth::hci::LeMetaEventView)>::Run(base::internal::BindStateBase*, bluetooth::hci::LeMetaEventView&&)+312)
+ #06 pc 00000000006f9908 libbluetooth.so (void base::internal::FunctorTraits<base::RepeatingCallback<void (bluetooth::hci::LeMetaEventView)>, void>::Invoke<base::RepeatingCallback<void (bluetooth::hci::LeMetaEventView)>, bluetooth::hci::LeMetaEventView>(base::RepeatingCallback<void (bluetooth::hci::LeMetaEventView)>&&, bluetooth::hci::LeMetaEventView&&)+296)
+ #07 pc 000000000081ac88 libbluetooth.so (bluetooth::os::Handler::handle_next_event()+244)
+ #08 pc 000000000081b580 libbluetooth.so (bluetooth::os::Reactor::Run()+608)
+ #09 pc 000000000081bec0 libbluetooth.so (bluetooth::os::Thread::run(bluetooth::os::Thread::Priority)+172)
+ #10 pc 000000000081c028 libbluetooth.so (void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (bluetooth::os::Thread::*)(bluetooth::os::Thread::Priority), bluetooth::os::Thread*, bluetooth::os::Thread::Priority> >(void*)+64)
+ #11 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #12 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_1/regs.txt
new file mode 100644
index 0000000..31d33d6
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/regs.txt
@@ -0,0 +1,34 @@
+x0: 1
+x1: d
+x2: 7c0f6d5110
+x3: 7b5ae05f58
+x4: 4
+x5: 33
+x6: 4025000fe2c160c
+x7: eb0a02e0114040c1
+x8: 87fc4d8789a28136
+x9: 87fc4d8789a28136
+x10: a
+x11: 0
+x12: 8
+x13: 106b9c07
+x14: 0
+x15: 0
+x16: 7e831617d0
+x17: 7e86850b40
+x18: 7b59516000
+x19: 7b5ae06138
+x20: 7b5ae06160
+x21: 7b5ae06210
+x22: 2f
+x23: 0
+x24: 0
+x25: 7b5ae07000
+x26: 7b5ec4ad78
+x27: 7b5ae05f88
+x28: 7b5ec4ad98
+x29: 7b5ae05fb0
+lr: 7b5ebd9b2c
+sp: 7b5ae05f70
+pc: 7b5ebd9bdc
+pst: 60000000
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_1/stack.data b/libunwindstack/offline_files/bluetooth_arm64/pc_1/stack.data
new file mode 100644
index 0000000..7395be3
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_1/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/maps.txt
new file mode 100644
index 0000000..88dffe8
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/maps.txt
@@ -0,0 +1,4 @@
+7b6021f000-7b603e6000 r--p 0 00:00 0 libbluetooth.so
+7b603e6000-7b60a54000 r-xp 1c7000 00:00 0 libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/output.txt
new file mode 100644
index 0000000..76f4825
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/output.txt
@@ -0,0 +1,7 @@
+ #00 pc 0000000000832188 libbluetooth.so
+ #01 pc 000000000081aca4 libbluetooth.so (bluetooth::os::Handler::handle_next_event()+272)
+ #02 pc 000000000081b580 libbluetooth.so (bluetooth::os::Reactor::Run()+608)
+ #03 pc 000000000081bec0 libbluetooth.so (bluetooth::os::Thread::run(bluetooth::os::Thread::Priority)+172)
+ #04 pc 000000000081c028 libbluetooth.so (void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (bluetooth::os::Thread::*)(bluetooth::os::Thread::Priority), bluetooth::os::Thread*, bluetooth::os::Thread::Priority> >(void*)+64)
+ #05 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #06 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_2/regs.txt
new file mode 100644
index 0000000..364f927
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/regs.txt
@@ -0,0 +1,34 @@
+x0: 7b5cb42718
+x1: 7e868e244c
+x2: 7c2f7039d0
+x3: 7b5cb426d8
+x4: 48
+x5: b
+x6: 7cbf6a4290
+x7: 7b5cb41ff9
+x8: 0
+x9: 0
+x10: 7c2f69d000
+x11: 60
+x12: 11
+x13: 106b9c07
+x14: 70742a85
+x15: 20d43150fc510e
+x16: 7b60a8a000
+x17: 7b63298604
+x18: 7b5c1d6000
+x19: 7c2f6aaf58
+x20: 7c0f6cd810
+x21: 7b5cb43000
+x22: 1
+x23: 0
+x24: ffffffff
+x25: 7c2f6ab4d0
+x26: 7c2f6ab4ec
+x27: 1
+x28: 7c7f6d7a60
+x29: 7b5cb42730
+lr: 7b60a39ca8
+sp: 7b5cb42710
+pc: 7b60a51188
+pst: 60000000
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_2/stack.data b/libunwindstack/offline_files/bluetooth_arm64/pc_2/stack.data
new file mode 100644
index 0000000..9bea330
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_2/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/maps.txt
new file mode 100644
index 0000000..3acc483
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/maps.txt
@@ -0,0 +1,4 @@
+7b56f20000-7b570e7000 r--p 0 00:00 0 libbluetooth.so
+7b570e7000-7b57755000 r-xp 1c7000 00:00 0 libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/output.txt
new file mode 100644
index 0000000..e9d42c9
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/output.txt
@@ -0,0 +1,11 @@
+ #00 pc 00000000003f0698 libbluetooth.so (allocation_tracker_notify_free(unsigned char, void*)+592)
+ #01 pc 00000000003f0d40 libbluetooth.so (osi_free(void*)+16)
+ #02 pc 00000000003f4598 libbluetooth.so (list_free_node_(list_t*, list_node_t*)+84)
+ #03 pc 00000000003f44e4 libbluetooth.so (list_remove(list_t*, void*)+356)
+ #04 pc 00000000003ef738 libbluetooth.so (callback_dispatch(void*)+188)
+ #05 pc 00000000003f6a70 libbluetooth.so (work_queue_read_cb(void*)+92)
+ #06 pc 00000000003f4cc4 libbluetooth.so (run_reactor(reactor_t*, int)+380)
+ #07 pc 00000000003f4b1c libbluetooth.so (reactor_start(reactor_t*)+84)
+ #08 pc 00000000003f65dc libbluetooth.so (run_thread(void*)+196)
+ #09 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #10 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_3/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_3/regs.txt
new file mode 100644
index 0000000..e8b9437
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/regs.txt
@@ -0,0 +1,34 @@
+x0: 0
+x1: 7bff6daf18
+x2: 15f199c2
+x3: 0
+x4: 7db0c8a
+x5: 0
+x6: 0
+x7: 7f7f7f7f7f7f7f7f
+x8: 10
+x9: 124d6
+x10: 20
+x11: 7b577e6000
+x12: 1
+x13: 7bff6daf18
+x14: 341555ac
+x15: 18
+x16: 7e83161730
+x17: 7e868c6698
+x18: 7b415a2000
+x19: 7bff6daf18
+x20: 7bff6d8ae0
+x21: 7bff6daf10
+x22: 7b56fc8897
+x23: 7b56f8dcc7
+x24: 7b56f7870a
+x25: 7bff6daf28
+x26: 766310a6006816c5
+x27: 7b577e6758
+x28: 0
+x29: 7b41ab2f90
+lr: 7b57310488
+sp: 7b41ab2e50
+pc: 7b57310698
+pst: 20000000
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex b/libunwindstack/offline_files/bluetooth_arm64/pc_3/stack.data
similarity index 61%
rename from libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
rename to libunwindstack/offline_files/bluetooth_arm64/pc_3/stack.data
index 870ac0a..cfc8a86 100644
--- a/libunwindstack/tests/files/offline/jit_debug_x86/137-cfi.odex
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_3/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/links.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/links.txt
new file mode 100644
index 0000000..29aa2e4
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/links.txt
@@ -0,0 +1,2 @@
+../../common/libbluetooth.so_41de80f38623dac3c221b849566fb858 libbluetooth.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/maps.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/maps.txt
new file mode 100644
index 0000000..3acc483
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/maps.txt
@@ -0,0 +1,4 @@
+7b56f20000-7b570e7000 r--p 0 00:00 0 libbluetooth.so
+7b570e7000-7b57755000 r-xp 1c7000 00:00 0 libbluetooth.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/output.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/output.txt
new file mode 100644
index 0000000..9dc4dd9
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/output.txt
@@ -0,0 +1,4 @@
+ #00 pc 00000000003f5b24 libbluetooth.so (semaphore_post(semaphore_t*))
+ #01 pc 000000000005c3b8 libc.so (__timer_thread_start(void*)+136)
+ #02 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #03 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/regs.txt b/libunwindstack/offline_files/bluetooth_arm64/pc_4/regs.txt
new file mode 100644
index 0000000..1d080e6
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/regs.txt
@@ -0,0 +1,34 @@
+x0: 7bff6f4a78
+x1: 7b43cafba0
+x2: 0
+x3: 8
+x4: ffffffff
+x5: 0
+x6: 36313031
+x7: 33363130
+x8: 7b577e6000
+x9: 87fc4d8789a28136
+x10: 0
+x11: 0
+x12: 1
+x13: 100000000
+x14: 0
+x15: 30
+x16: 7e868d3f28
+x17: 7e86863adc
+x18: 7b4330e000
+x19: 7c0f6c0750
+x20: 7b43cb0000
+x21: 7b43cafcb0
+x22: 2777
+x23: 2794
+x24: 7b43cafcb0
+x25: 7b43cafcb0
+x26: 7b43cafff8
+x27: fc000
+x28: 7b43bb7000
+x29: 7b43cafc30
+lr: 7e868703bc
+sp: 7b43cafba0
+pc: 7b57315b24
+pst: 60000000
diff --git a/libunwindstack/offline_files/bluetooth_arm64/pc_4/stack.data b/libunwindstack/offline_files/bluetooth_arm64/pc_4/stack.data
new file mode 100644
index 0000000..96d17cc
--- /dev/null
+++ b/libunwindstack/offline_files/bluetooth_arm64/pc_4/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/common/base.odex_maps_compiled_arm64.gz b/libunwindstack/offline_files/common/base.odex_maps_compiled_arm64.gz
new file mode 100644
index 0000000..e06cbbe
--- /dev/null
+++ b/libunwindstack/offline_files/common/base.odex_maps_compiled_arm64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/boot-core-libart.oat_8368d55c916dc1224e76017186edfceee88a9955.gz b/libunwindstack/offline_files/common/boot-core-libart.oat_8368d55c916dc1224e76017186edfceee88a9955.gz
new file mode 100644
index 0000000..be140f3
--- /dev/null
+++ b/libunwindstack/offline_files/common/boot-core-libart.oat_8368d55c916dc1224e76017186edfceee88a9955.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6.gz b/libunwindstack/offline_files/common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6.gz
new file mode 100644
index 0000000..0348933
--- /dev/null
+++ b/libunwindstack/offline_files/common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c.gz b/libunwindstack/offline_files/common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c.gz
new file mode 100644
index 0000000..1afbe4a
--- /dev/null
+++ b/libunwindstack/offline_files/common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4.gz b/libunwindstack/offline_files/common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4.gz
new file mode 100644
index 0000000..63d2c2d
--- /dev/null
+++ b/libunwindstack/offline_files/common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libart.so_82c0556f4b66528e4a608c100a63b712.gz b/libunwindstack/offline_files/common/libart.so_82c0556f4b66528e4a608c100a63b712.gz
new file mode 100644
index 0000000..8936a9c
--- /dev/null
+++ b/libunwindstack/offline_files/common/libart.so_82c0556f4b66528e4a608c100a63b712.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libbluetooth.so_41de80f38623dac3c221b849566fb858.gz b/libunwindstack/offline_files/common/libbluetooth.so_41de80f38623dac3c221b849566fb858.gz
new file mode 100644
index 0000000..2c5cc32
--- /dev/null
+++ b/libunwindstack/offline_files/common/libbluetooth.so_41de80f38623dac3c221b849566fb858.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libc.so_3106fb6eab1b2dc6704157e908a50eba.gz b/libunwindstack/offline_files/common/libc.so_3106fb6eab1b2dc6704157e908a50eba.gz
new file mode 100644
index 0000000..abb334f
--- /dev/null
+++ b/libunwindstack/offline_files/common/libc.so_3106fb6eab1b2dc6704157e908a50eba.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libc.so_f3791c53da47e6e72151dcc8088b9048.gz b/libunwindstack/offline_files/common/libc.so_f3791c53da47e6e72151dcc8088b9048.gz
new file mode 100644
index 0000000..c1ce2cc
--- /dev/null
+++ b/libunwindstack/offline_files/common/libc.so_f3791c53da47e6e72151dcc8088b9048.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libjavacore.so_1cda9a31939d0b05577a62e79f44fc40.gz b/libunwindstack/offline_files/common/libjavacore.so_1cda9a31939d0b05577a62e79f44fc40.gz
new file mode 100644
index 0000000..6749740
--- /dev/null
+++ b/libunwindstack/offline_files/common/libjavacore.so_1cda9a31939d0b05577a62e79f44fc40.gz
Binary files differ
diff --git a/libunwindstack/offline_files/common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4.gz b/libunwindstack/offline_files/common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4.gz
new file mode 100644
index 0000000..5154821
--- /dev/null
+++ b/libunwindstack/offline_files/common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_first_x86/libc.so.gz b/libunwindstack/offline_files/debug_frame_first_x86/libc.so.gz
new file mode 100644
index 0000000..33e085f
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_first_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt b/libunwindstack/offline_files/debug_frame_first_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_first_x86/maps.txt
rename to libunwindstack/offline_files/debug_frame_first_x86/maps.txt
diff --git a/libunwindstack/offline_files/debug_frame_first_x86/output.txt b/libunwindstack/offline_files/debug_frame_first_x86/output.txt
new file mode 100644
index 0000000..887e908
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_first_x86/output.txt
@@ -0,0 +1,5 @@
+ #00 pc 00000685 waiter (call_level3+53)
+ #01 pc 000006b7 waiter (call_level2+23)
+ #02 pc 000006d7 waiter (call_level1+23)
+ #03 pc 000006f7 waiter (main+23)
+ #04 pc 00018275 libc.so
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt b/libunwindstack/offline_files/debug_frame_first_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_first_x86/regs.txt
rename to libunwindstack/offline_files/debug_frame_first_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data b/libunwindstack/offline_files/debug_frame_first_x86/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_first_x86/stack.data
rename to libunwindstack/offline_files/debug_frame_first_x86/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_first_x86/waiter.gz b/libunwindstack/offline_files/debug_frame_first_x86/waiter.gz
new file mode 100644
index 0000000..4b84cd9
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_first_x86/waiter.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/libbinder.so.gz b/libunwindstack/offline_files/debug_frame_load_bias_arm/libbinder.so.gz
new file mode 100644
index 0000000..c9469fa
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/libbinder.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/libc.so.gz b/libunwindstack/offline_files/debug_frame_load_bias_arm/libc.so.gz
new file mode 100644
index 0000000..9063e51
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt b/libunwindstack/offline_files/debug_frame_load_bias_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_load_bias_arm/maps.txt
rename to libunwindstack/offline_files/debug_frame_load_bias_arm/maps.txt
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/mediaserver.gz b/libunwindstack/offline_files/debug_frame_load_bias_arm/mediaserver.gz
new file mode 100644
index 0000000..719fea0
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/mediaserver.gz
Binary files differ
diff --git a/libunwindstack/offline_files/debug_frame_load_bias_arm/output.txt b/libunwindstack/offline_files/debug_frame_load_bias_arm/output.txt
new file mode 100644
index 0000000..0860ec9
--- /dev/null
+++ b/libunwindstack/offline_files/debug_frame_load_bias_arm/output.txt
@@ -0,0 +1,8 @@
+ #00 pc 0005138c libc.so (__ioctl+8)
+ #01 pc 0002140f libc.so (ioctl+30)
+ #02 pc 00039535 libbinder.so (android::IPCThreadState::talkWithDriver(bool)+204)
+ #03 pc 00039633 libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)
+ #04 pc 00039b57 libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)
+ #05 pc 00000c21 mediaserver (main+104)
+ #06 pc 00084b89 libc.so (__libc_init+48)
+ #07 pc 00000b77 mediaserver (_start_main+38)
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt b/libunwindstack/offline_files/debug_frame_load_bias_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_load_bias_arm/regs.txt
rename to libunwindstack/offline_files/debug_frame_load_bias_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data b/libunwindstack/offline_files/debug_frame_load_bias_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/debug_frame_load_bias_arm/stack.data
rename to libunwindstack/offline_files/debug_frame_load_bias_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/libc.so.gz b/libunwindstack/offline_files/eh_frame_bias_x86/libc.so.gz
new file mode 100644
index 0000000..f26eb69
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt b/libunwindstack/offline_files/eh_frame_bias_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_bias_x86/maps.txt
rename to libunwindstack/offline_files/eh_frame_bias_x86/maps.txt
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/output.txt b/libunwindstack/offline_files/eh_frame_bias_x86/output.txt
new file mode 100644
index 0000000..e073809
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/output.txt
@@ -0,0 +1,11 @@
+ #00 pc ffffe430 vdso.so (__kernel_vsyscall+16)
+ #01 pc 00082a4b libc.so (__epoll_pwait+43)
+ #02 pc 000303a3 libc.so (epoll_pwait+115)
+ #03 pc 000303ed libc.so (epoll_wait+45)
+ #04 pc 00010ea2 tombstoned (epoll_dispatch+226)
+ #05 pc 0000c5e7 tombstoned (event_base_loop+1095)
+ #06 pc 0000c193 tombstoned (event_base_dispatch+35)
+ #07 pc 00005c77 tombstoned (main+884)
+ #08 pc 00015f66 libc.so (__libc_init+102)
+ #09 pc 0000360e tombstoned (_start+98)
+ #10 pc 00000001 <unknown>
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt b/libunwindstack/offline_files/eh_frame_bias_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_bias_x86/regs.txt
rename to libunwindstack/offline_files/eh_frame_bias_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data b/libunwindstack/offline_files/eh_frame_bias_x86/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_bias_x86/stack.data
rename to libunwindstack/offline_files/eh_frame_bias_x86/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/tombstoned.gz b/libunwindstack/offline_files/eh_frame_bias_x86/tombstoned.gz
new file mode 100644
index 0000000..35d6b39
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/tombstoned.gz
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_bias_x86/vdso.so.gz b/libunwindstack/offline_files/eh_frame_bias_x86/vdso.so.gz
new file mode 100644
index 0000000..93490de
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_bias_x86/vdso.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/libc.so.gz b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/libc.so.gz
new file mode 100644
index 0000000..e64a160
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/maps.txt
rename to libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/maps.txt
diff --git a/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/output.txt b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/output.txt
new file mode 100644
index 0000000..ea75136
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/output.txt
@@ -0,0 +1,5 @@
+ #00 pc 0000000000000a80 unwind_test64 (calling3)
+ #01 pc 0000000000000dd9 unwind_test64 (calling2+633)
+ #02 pc 000000000000121e unwind_test64 (calling1+638)
+ #03 pc 00000000000013ed unwind_test64 (main+13)
+ #04 pc 00000000000202b0 libc.so
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/regs.txt
rename to libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/regs.txt
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/stack.data
rename to libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/unwind_test64.gz b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/unwind_test64.gz
new file mode 100644
index 0000000..5f8f954
--- /dev/null
+++ b/libunwindstack/offline_files/eh_frame_hdr_begin_x86_64/unwind_test64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/empty_arm64/libbinder.so.gz b/libunwindstack/offline_files/empty_arm64/libbinder.so.gz
new file mode 100644
index 0000000..e752f7d
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/libbinder.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/empty_arm64/libc.so.gz b/libunwindstack/offline_files/empty_arm64/libc.so.gz
new file mode 100644
index 0000000..8bd14b5
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/maps.txt b/libunwindstack/offline_files/empty_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/empty_arm64/maps.txt
rename to libunwindstack/offline_files/empty_arm64/maps.txt
diff --git a/libunwindstack/offline_files/empty_arm64/netd.gz b/libunwindstack/offline_files/empty_arm64/netd.gz
new file mode 100644
index 0000000..ba6efa1
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/netd.gz
Binary files differ
diff --git a/libunwindstack/offline_files/empty_arm64/output.txt b/libunwindstack/offline_files/empty_arm64/output.txt
new file mode 100644
index 0000000..486508a
--- /dev/null
+++ b/libunwindstack/offline_files/empty_arm64/output.txt
@@ -0,0 +1,7 @@
+ #00 pc 00000000000963a4 libc.so (__ioctl+4)
+ #01 pc 000000000005344c libc.so (ioctl+140)
+ #02 pc 0000000000050ce4 libbinder.so (android::IPCThreadState::talkWithDriver(bool)+308)
+ #03 pc 0000000000050e98 libbinder.so (android::IPCThreadState::getAndExecuteCommand()+24)
+ #04 pc 00000000000516ac libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)
+ #05 pc 00000000000443b0 netd (main+1056)
+ #06 pc 0000000000045594 libc.so (__libc_init+108)
diff --git a/libunwindstack/tests/files/offline/empty_arm64/regs.txt b/libunwindstack/offline_files/empty_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/empty_arm64/regs.txt
rename to libunwindstack/offline_files/empty_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/empty_arm64/stack.data b/libunwindstack/offline_files/empty_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/empty_arm64/stack.data
rename to libunwindstack/offline_files/empty_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/gnu_debugdata_arm/libandroid_runtime.so.gz b/libunwindstack/offline_files/gnu_debugdata_arm/libandroid_runtime.so.gz
new file mode 100644
index 0000000..199c4b2
--- /dev/null
+++ b/libunwindstack/offline_files/gnu_debugdata_arm/libandroid_runtime.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt b/libunwindstack/offline_files/gnu_debugdata_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm/maps.txt
rename to libunwindstack/offline_files/gnu_debugdata_arm/maps.txt
diff --git a/libunwindstack/offline_files/gnu_debugdata_arm/output.txt b/libunwindstack/offline_files/gnu_debugdata_arm/output.txt
new file mode 100644
index 0000000..3b6fe65
--- /dev/null
+++ b/libunwindstack/offline_files/gnu_debugdata_arm/output.txt
@@ -0,0 +1,2 @@
+ #00 pc 0006dc49 libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+80)
+ #01 pc 0006dce5 libandroid_runtime.so (android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, unsigned int, void**))
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt b/libunwindstack/offline_files/gnu_debugdata_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm/regs.txt
rename to libunwindstack/offline_files/gnu_debugdata_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data b/libunwindstack/offline_files/gnu_debugdata_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/gnu_debugdata_arm/stack.data
rename to libunwindstack/offline_files/gnu_debugdata_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt b/libunwindstack/offline_files/invalid_elf_offset_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/invalid_elf_offset_arm/maps.txt
rename to libunwindstack/offline_files/invalid_elf_offset_arm/maps.txt
diff --git a/libunwindstack/offline_files/invalid_elf_offset_arm/output.txt b/libunwindstack/offline_files/invalid_elf_offset_arm/output.txt
new file mode 100644
index 0000000..fe7e485
--- /dev/null
+++ b/libunwindstack/offline_files/invalid_elf_offset_arm/output.txt
@@ -0,0 +1 @@
+ #00 pc 00aa7508 invalid.apk (offset 0x12e4000)
diff --git a/libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt b/libunwindstack/offline_files/invalid_elf_offset_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/invalid_elf_offset_arm/regs.txt
rename to libunwindstack/offline_files/invalid_elf_offset_arm/regs.txt
diff --git a/libunwindstack/offline_files/jit_debug_arm/137-cfi.odex.gz b/libunwindstack/offline_files/jit_debug_arm/137-cfi.odex.gz
new file mode 100644
index 0000000..7150613
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/137-cfi.odex.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/dalvikvm32.gz b/libunwindstack/offline_files/jit_debug_arm/dalvikvm32.gz
new file mode 100644
index 0000000..868440f
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/dalvikvm32.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data b/libunwindstack/offline_files/jit_debug_arm/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/descriptor.data
rename to libunwindstack/offline_files/jit_debug_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data b/libunwindstack/offline_files/jit_debug_arm/descriptor1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/descriptor1.data
rename to libunwindstack/offline_files/jit_debug_arm/descriptor1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry0.data b/libunwindstack/offline_files/jit_debug_arm/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry0.data
rename to libunwindstack/offline_files/jit_debug_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry1.data b/libunwindstack/offline_files/jit_debug_arm/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry1.data
rename to libunwindstack/offline_files/jit_debug_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry2.data b/libunwindstack/offline_files/jit_debug_arm/entry2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry2.data
rename to libunwindstack/offline_files/jit_debug_arm/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry3.data b/libunwindstack/offline_files/jit_debug_arm/entry3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry3.data
rename to libunwindstack/offline_files/jit_debug_arm/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry4.data b/libunwindstack/offline_files/jit_debug_arm/entry4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry4.data
rename to libunwindstack/offline_files/jit_debug_arm/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry5.data b/libunwindstack/offline_files/jit_debug_arm/entry5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry5.data
rename to libunwindstack/offline_files/jit_debug_arm/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/entry6.data b/libunwindstack/offline_files/jit_debug_arm/entry6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/entry6.data
rename to libunwindstack/offline_files/jit_debug_arm/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit0.data b/libunwindstack/offline_files/jit_debug_arm/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit0.data
rename to libunwindstack/offline_files/jit_debug_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit1.data b/libunwindstack/offline_files/jit_debug_arm/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit1.data
rename to libunwindstack/offline_files/jit_debug_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit2.data b/libunwindstack/offline_files/jit_debug_arm/jit2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit2.data
rename to libunwindstack/offline_files/jit_debug_arm/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit3.data b/libunwindstack/offline_files/jit_debug_arm/jit3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit3.data
rename to libunwindstack/offline_files/jit_debug_arm/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit4.data b/libunwindstack/offline_files/jit_debug_arm/jit4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit4.data
rename to libunwindstack/offline_files/jit_debug_arm/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit5.data b/libunwindstack/offline_files/jit_debug_arm/jit5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit5.data
rename to libunwindstack/offline_files/jit_debug_arm/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/jit6.data b/libunwindstack/offline_files/jit_debug_arm/jit6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/jit6.data
rename to libunwindstack/offline_files/jit_debug_arm/jit6.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libart.so.gz b/libunwindstack/offline_files/jit_debug_arm/libart.so.gz
new file mode 100644
index 0000000..f23b6fd
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libart.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libartd.so.gz b/libunwindstack/offline_files/jit_debug_arm/libartd.so.gz
new file mode 100644
index 0000000..4f9d860
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libartd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libarttestd.so.gz b/libunwindstack/offline_files/jit_debug_arm/libarttestd.so.gz
new file mode 100644
index 0000000..49c9933
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libarttestd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_arm/libc.so.gz b/libunwindstack/offline_files/jit_debug_arm/libc.so.gz
new file mode 100644
index 0000000..65f3cf2
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/maps.txt b/libunwindstack/offline_files/jit_debug_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/maps.txt
rename to libunwindstack/offline_files/jit_debug_arm/maps.txt
diff --git a/libunwindstack/offline_files/jit_debug_arm/output.txt b/libunwindstack/offline_files/jit_debug_arm/output.txt
new file mode 100644
index 0000000..094168c
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_arm/output.txt
@@ -0,0 +1,76 @@
+ #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)
+ #01 pc 0000212d 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, boolean)+92)
+ #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)
+ #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #04 pc 00467129 libartd.so (art_quick_invoke_stub+228)
+ #05 pc 000bf7a9 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+ #06 pc 00247833 libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+ #07 pc 0022e935 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+ #08 pc 0022f71d libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+ #09 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)
+ #10 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)
+ #11 pc 00011c31 anonymous:e2796000 (int Main.compare(Main, Main)+64)
+ #12 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #13 pc 00467129 libartd.so (art_quick_invoke_stub+228)
+ #14 pc 000bf7a9 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+ #15 pc 00247833 libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+ #16 pc 0022e935 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+ #17 pc 0022f71d libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+ #18 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)
+ #19 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)
+ #20 pc 00011b77 anonymous:e2796000 (int Main.compare(java.lang.Object, java.lang.Object)+118)
+ #21 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #22 pc 00467129 libartd.so (art_quick_invoke_stub+228)
+ #23 pc 000bf7a9 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+ #24 pc 00247833 libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+ #25 pc 0022e935 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+ #26 pc 0022f71d libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+ #27 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)
+ #28 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)
+ #29 pc 00011a29 anonymous:e2796000 (int java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)+304)
+ #30 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #31 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)
+ #32 pc 000bf7bb libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+882)
+ #33 pc 00247833 libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+ #34 pc 0022e935 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+ #35 pc 0022f71d libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+ #36 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)
+ #37 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)
+ #38 pc 0001139b anonymous:e2796000 (boolean Main.foo()+178)
+ #39 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #40 pc 00467129 libartd.so (art_quick_invoke_stub+228)
+ #41 pc 000bf7a9 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+ #42 pc 00247833 libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+ #43 pc 0022e935 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+ #44 pc 0022f71d libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+ #45 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)
+ #46 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)
+ #47 pc 00010aa7 anonymous:e2796000 (void Main.runPrimary()+70)
+ #48 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #49 pc 00467129 libartd.so (art_quick_invoke_stub+228)
+ #50 pc 000bf7a9 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+864)
+ #51 pc 00247833 libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+ #52 pc 0022e935 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+ #53 pc 0022f71d libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+ #54 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)
+ #55 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)
+ #56 pc 0000ba99 anonymous:e2796000 (void Main.main(java.lang.String[])+144)
+ #57 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #58 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)
+ #59 pc 000bf7bb libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+882)
+ #60 pc 00247833 libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+382)
+ #61 pc 0022e935 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+244)
+ #62 pc 0022f71d libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+128)
+ #63 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)
+ #64 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)
+ #65 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)
+ #66 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)
+ #67 pc 000bf7bb libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+882)
+ #68 pc 003b292d libartd.so (art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)+52)
+ #69 pc 003b26c3 libartd.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+210)
+ #70 pc 00308411 libartd.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)
+ #71 pc 000e6a9f libartd.so (art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, std::__va_list, art::Primitive::Type, art::InvokeType)+1486)
+ #72 pc 000e19b9 libartd.so (art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)
+ #73 pc 0000159f dalvikvm32 (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)
+ #74 pc 00001349 dalvikvm32 (main+896)
+ #75 pc 000850c9 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/regs.txt b/libunwindstack/offline_files/jit_debug_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/regs.txt
rename to libunwindstack/offline_files/jit_debug_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/stack.data b/libunwindstack/offline_files/jit_debug_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_arm/stack.data
rename to libunwindstack/offline_files/jit_debug_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/137-cfi.odex.gz b/libunwindstack/offline_files/jit_debug_x86/137-cfi.odex.gz
new file mode 100644
index 0000000..6b7a055
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/137-cfi.odex.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/dalvikvm32.gz b/libunwindstack/offline_files/jit_debug_x86/dalvikvm32.gz
new file mode 100644
index 0000000..e9306c6
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/dalvikvm32.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data b/libunwindstack/offline_files/jit_debug_x86/descriptor.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/descriptor.data
rename to libunwindstack/offline_files/jit_debug_x86/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry0.data b/libunwindstack/offline_files/jit_debug_x86/entry0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry0.data
rename to libunwindstack/offline_files/jit_debug_x86/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry1.data b/libunwindstack/offline_files/jit_debug_x86/entry1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry1.data
rename to libunwindstack/offline_files/jit_debug_x86/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry2.data b/libunwindstack/offline_files/jit_debug_x86/entry2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry2.data
rename to libunwindstack/offline_files/jit_debug_x86/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry3.data b/libunwindstack/offline_files/jit_debug_x86/entry3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry3.data
rename to libunwindstack/offline_files/jit_debug_x86/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry4.data b/libunwindstack/offline_files/jit_debug_x86/entry4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry4.data
rename to libunwindstack/offline_files/jit_debug_x86/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry5.data b/libunwindstack/offline_files/jit_debug_x86/entry5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry5.data
rename to libunwindstack/offline_files/jit_debug_x86/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/entry6.data b/libunwindstack/offline_files/jit_debug_x86/entry6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/entry6.data
rename to libunwindstack/offline_files/jit_debug_x86/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit0.data b/libunwindstack/offline_files/jit_debug_x86/jit0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit0.data
rename to libunwindstack/offline_files/jit_debug_x86/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit1.data b/libunwindstack/offline_files/jit_debug_x86/jit1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit1.data
rename to libunwindstack/offline_files/jit_debug_x86/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit2.data b/libunwindstack/offline_files/jit_debug_x86/jit2.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit2.data
rename to libunwindstack/offline_files/jit_debug_x86/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit3.data b/libunwindstack/offline_files/jit_debug_x86/jit3.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit3.data
rename to libunwindstack/offline_files/jit_debug_x86/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit4.data b/libunwindstack/offline_files/jit_debug_x86/jit4.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit4.data
rename to libunwindstack/offline_files/jit_debug_x86/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit5.data b/libunwindstack/offline_files/jit_debug_x86/jit5.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit5.data
rename to libunwindstack/offline_files/jit_debug_x86/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/jit6.data b/libunwindstack/offline_files/jit_debug_x86/jit6.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/jit6.data
rename to libunwindstack/offline_files/jit_debug_x86/jit6.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/libartd.so.gz b/libunwindstack/offline_files/jit_debug_x86/libartd.so.gz
new file mode 100644
index 0000000..83716b1
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/libartd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/libarttestd.so.gz b/libunwindstack/offline_files/jit_debug_x86/libarttestd.so.gz
new file mode 100644
index 0000000..097eeb0
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/libarttestd.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_debug_x86/libc.so.gz b/libunwindstack/offline_files/jit_debug_x86/libc.so.gz
new file mode 100644
index 0000000..30230f7
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/maps.txt b/libunwindstack/offline_files/jit_debug_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/maps.txt
rename to libunwindstack/offline_files/jit_debug_x86/maps.txt
diff --git a/libunwindstack/offline_files/jit_debug_x86/output.txt b/libunwindstack/offline_files/jit_debug_x86/output.txt
new file mode 100644
index 0000000..08b2401
--- /dev/null
+++ b/libunwindstack/offline_files/jit_debug_x86/output.txt
@@ -0,0 +1,69 @@
+ #00 pc 00068fb8 libarttestd.so (art::CauseSegfault()+72)
+ #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)
+ #02 pc 000021a8 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, boolean)+136)
+ #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)
+ #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)
+ #05 pc 00146ab5 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+ #06 pc 0039cf0d libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+ #07 pc 00392552 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+ #08 pc 0039399a libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+ #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)
+ #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)
+ #11 pc 0000fe03 anonymous:ee74c000 (int Main.compare(Main, Main)+51)
+ #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)
+ #13 pc 00146ab5 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+ #14 pc 0039cf0d libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+ #15 pc 00392552 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+ #16 pc 0039399a libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+ #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)
+ #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)
+ #19 pc 0000fd3b anonymous:ee74c000 (int Main.compare(java.lang.Object, java.lang.Object)+107)
+ #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)
+ #21 pc 00146ab5 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+ #22 pc 0039cf0d libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+ #23 pc 00392552 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+ #24 pc 0039399a libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+ #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)
+ #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)
+ #27 pc 0000fbdb anonymous:ee74c000 (int java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)+331)
+ #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)
+ #29 pc 00146acb libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+907)
+ #30 pc 0039cf0d libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+ #31 pc 00392552 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+ #32 pc 0039399a libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+ #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)
+ #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)
+ #35 pc 0000f624 anonymous:ee74c000 (boolean Main.foo()+164)
+ #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)
+ #37 pc 00146ab5 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+ #38 pc 0039cf0d libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+ #39 pc 00392552 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+ #40 pc 0039399a libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+ #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)
+ #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)
+ #43 pc 0000eedb anonymous:ee74c000 (void Main.runPrimary()+59)
+ #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)
+ #45 pc 00146ab5 libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+885)
+ #46 pc 0039cf0d libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+ #47 pc 00392552 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+ #48 pc 0039399a libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+ #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)
+ #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)
+ #51 pc 0000ac21 anonymous:ee74c000 (void Main.main(java.lang.String[])+97)
+ #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)
+ #53 pc 00146acb libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+907)
+ #54 pc 0039cf0d libartd.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+653)
+ #55 pc 00392552 libartd.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool)+354)
+ #56 pc 0039399a libartd.so (art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*)+234)
+ #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)
+ #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)
+ #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)
+ #60 pc 00146acb libartd.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+907)
+ #61 pc 005aac95 libartd.so (art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)+85)
+ #62 pc 005aab5a libartd.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, char*)+362)
+ #63 pc 0048a3dd libartd.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)
+ #64 pc 0018448c libartd.so (art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, art::Primitive::Type, art::InvokeType)+1964)
+ #65 pc 0017cf06 libartd.so (art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)
+ #66 pc 00001d8c dalvikvm32 (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)
+ #67 pc 00001a80 dalvikvm32 (main+1312)
+ #68 pc 00018275 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/regs.txt b/libunwindstack/offline_files/jit_debug_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/regs.txt
rename to libunwindstack/offline_files/jit_debug_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/stack.data b/libunwindstack/offline_files/jit_debug_x86/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_debug_x86/stack.data
rename to libunwindstack/offline_files/jit_debug_x86/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/jit_map0.so.gz b/libunwindstack/offline_files/jit_map_arm/jit_map0.so.gz
new file mode 100644
index 0000000..2a332c9
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/jit_map0.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/jit_map1.so.gz b/libunwindstack/offline_files/jit_map_arm/jit_map1.so.gz
new file mode 100644
index 0000000..c1341db
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/jit_map1.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/libart.so.gz b/libunwindstack/offline_files/jit_map_arm/libart.so.gz
new file mode 100644
index 0000000..bacf50b
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/libart.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/jit_map_arm/libc.so.gz b/libunwindstack/offline_files/jit_map_arm/libc.so.gz
new file mode 100644
index 0000000..a33f276
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/maps.txt b/libunwindstack/offline_files/jit_map_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_map_arm/maps.txt
rename to libunwindstack/offline_files/jit_map_arm/maps.txt
diff --git a/libunwindstack/offline_files/jit_map_arm/output.txt b/libunwindstack/offline_files/jit_map_arm/output.txt
new file mode 100644
index 0000000..f9eb2ef
--- /dev/null
+++ b/libunwindstack/offline_files/jit_map_arm/output.txt
@@ -0,0 +1,6 @@
+ #00 pc 00000000 jit_map0.so (com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)
+ #01 pc 0000003d jit_map1.so (com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)
+ #02 pc 004135bb libart.so (art_quick_osr_stub+42)
+ #03 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)
+ #04 pc 00062925 libc.so (__pthread_start(void*)+22)
+ #05 pc 0001de39 libc.so (__start_thread+24)
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/regs.txt b/libunwindstack/offline_files/jit_map_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_map_arm/regs.txt
rename to libunwindstack/offline_files/jit_map_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/stack.data b/libunwindstack/offline_files/jit_map_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/jit_map_arm/stack.data
rename to libunwindstack/offline_files/jit_map_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/libc.so.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/libc.so.gz
new file mode 100644
index 0000000..ad5f4e5
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/linker64.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/linker64.gz
new file mode 100644
index 0000000..0224762
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/linker64.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/maps.txt
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/maps.txt
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/output.txt b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/output.txt
new file mode 100644
index 0000000..d34c97e
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/output.txt
@@ -0,0 +1,12 @@
+ #00 pc 00000000000d59bc linker64 (__dl_syscall+28)
+ #01 pc 00000000000554e8 linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1148)
+ #02 pc 00000000000008c0 vdso (__kernel_rt_sigreturn)
+ #03 pc 000000000007f3e8 libc.so (abort+168)
+ #04 pc 00000000000459fc test (std::__ndk1::__throw_bad_cast()+4)
+ #05 pc 0000000000056d80 test (testing::Test::Run()+88)
+ #06 pc 000000000005724c test (testing::TestInfo::Run()+112)
+ #07 pc 0000000000057558 test (testing::TestSuite::Run()+116)
+ #08 pc 000000000005bffc test (testing::internal::UnitTestImpl::RunAllTests()+464)
+ #09 pc 000000000005bd9c test (testing::UnitTest::Run()+116)
+ #10 pc 00000000000464e4 test (main+144)
+ #11 pc 000000000007aa34 libc.so (__libc_init+108)
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/regs.txt
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack0.data
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/stack1.data
rename to libunwindstack/offline_files/load_bias_different_section_bias_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/test.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/test.gz
new file mode 100644
index 0000000..5a49557
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/test.gz
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_different_section_bias_arm64/vdso.gz b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/vdso.gz
new file mode 100644
index 0000000..99a8b7f
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_different_section_bias_arm64/vdso.gz
Binary files differ
diff --git a/libunwindstack/offline_files/load_bias_ro_rx_x86_64/libc.so.gz b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/libc.so.gz
new file mode 100644
index 0000000..b63a853
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
rename to libunwindstack/offline_files/load_bias_ro_rx_x86_64/maps.txt
diff --git a/libunwindstack/offline_files/load_bias_ro_rx_x86_64/output.txt b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/output.txt
new file mode 100644
index 0000000..592fb89
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/output.txt
@@ -0,0 +1,17 @@
+ #00 pc 00000000000e9dd4 libc.so (__write+20)
+ #01 pc 000000000007ab9c libc.so (_IO_file_write+44)
+ #02 pc 0000000000079f3e libc.so
+ #03 pc 000000000007bce8 libc.so (_IO_do_write+24)
+ #04 pc 000000000007b26e libc.so (_IO_file_xsputn+270)
+ #05 pc 000000000004f7f9 libc.so (_IO_vfprintf+1945)
+ #06 pc 0000000000057cb5 libc.so (_IO_printf+165)
+ #07 pc 0000000000ed1796 perfetto_unittests (testing::internal::PrettyUnitTestResultPrinter::OnTestIterationStart(testing::UnitTest const&, int)+374)
+ #08 pc 0000000000ed30fd perfetto_unittests (testing::internal::TestEventRepeater::OnTestIterationStart(testing::UnitTest const&, int)+125)
+ #09 pc 0000000000ed5e25 perfetto_unittests (testing::internal::UnitTestImpl::RunAllTests()+581)
+ #10 pc 0000000000ef63f3 perfetto_unittests (bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*)+131)
+ #11 pc 0000000000ee2a21 perfetto_unittests (bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*)+113)
+ #12 pc 0000000000ed5bb9 perfetto_unittests (testing::UnitTest::Run()+185)
+ #13 pc 0000000000e900f0 perfetto_unittests (RUN_ALL_TESTS()+16)
+ #14 pc 0000000000e900d8 perfetto_unittests (main+56)
+ #15 pc 000000000002352a libc.so (__libc_start_main+234)
+ #16 pc 0000000000919029 perfetto_unittests (_start+41)
diff --git a/libunwindstack/offline_files/load_bias_ro_rx_x86_64/perfetto_unittests.gz b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/perfetto_unittests.gz
new file mode 100644
index 0000000..1172e25
--- /dev/null
+++ b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/perfetto_unittests.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
rename to libunwindstack/offline_files/load_bias_ro_rx_x86_64/regs.txt
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data b/libunwindstack/offline_files/load_bias_ro_rx_x86_64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
rename to libunwindstack/offline_files/load_bias_ro_rx_x86_64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/app_process64.gz b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/app_process64.gz
new file mode 100644
index 0000000..a2dc750
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/app_process64.gz
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/links.txt
new file mode 100644
index 0000000..27beb3e
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/links.txt
@@ -0,0 +1,6 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot-framework.oat_6da45a084bf1f153be922249096389b66d69b6e6 boot-framework.oat
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4 libandroid_runtime.so
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/maps.txt
new file mode 100644
index 0000000..7bcd383
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/maps.txt
@@ -0,0 +1,14 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0 boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0 boot.oat
+71227000-713aa000 r--p 0 00:00 0 boot-framework.oat
+713aa000-71ab4000 r-xp 183000 00:00 0 boot-framework.oat
+6193c7e000-6193c80000 r--p 0 00:00 0 app_process64
+6193c80000-6193c81000 r-xp 2000 00:00 0 app_process64
+7b590f4000-7b5a2aa000 r--p 0 00:00 0 base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0 base.odex_maps_compiled_arm64
+7bdd800000-7bdda00000 r--p 0 00:00 0 libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0 libart.so
+7e7c400000-7e7c4af000 r--p 0 00:00 0 libandroid_runtime.so
+7e7c4af000-7e7c5df000 r-xp af000 00:00 0 libandroid_runtime.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/output.txt
new file mode 100644
index 0000000..97e939a
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/output.txt
@@ -0,0 +1,50 @@
+ #00 pc 000000000023b1e0 libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+2980)
+ #01 pc 0000000000244698 libart.so (art::ClassLinker::InitializeClass(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)+2348)
+ #02 pc 0000000000243fd0 libart.so (art::ClassLinker::InitializeClass(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)+612)
+ #03 pc 0000000000359730 libart.so (artAllocObjectFromCodeResolvedRegionTLAB+668)
+ #04 pc 000000000029ba88 libart.so (art_quick_alloc_object_resolved_region_tlab+104)
+ #05 pc 0000000005b4ba38 base.odex_maps_compiled_arm64 (ffs.h+4600)
+ #06 pc 0000000005b8a620 base.odex_maps_compiled_arm64 (ffs.a+528)
+ #07 pc 0000000005a1fb18 base.odex_maps_compiled_arm64 (ebhk.a+184)
+ #08 pc 00000000023600d4 base.odex_maps_compiled_arm64 (bdvg.<init>+6356)
+ #09 pc 000000000236678c base.odex_maps_compiled_arm64 (bdvh.a+2652)
+ #10 pc 0000000002a59b00 base.odex_maps_compiled_arm64 (bdux.O+816)
+ #11 pc 0000000005afc2a4 base.odex_maps_compiled_arm64 (er.tO+244)
+ #12 pc 00000000058a6444 base.odex_maps_compiled_arm64 (gi.c+1396)
+ #13 pc 00000000058a8104 base.odex_maps_compiled_arm64 (gi.e+4708)
+ #14 pc 0000000005893ce0 base.odex_maps_compiled_arm64 (fy.aB+5952)
+ #15 pc 0000000005894e4c base.odex_maps_compiled_arm64 (fy.aD+492)
+ #16 pc 000000000589dde0 base.odex_maps_compiled_arm64 (fy.at+544)
+ #17 pc 0000000005f09288 base.odex_maps_compiled_arm64 (hfn.F+2040)
+ #18 pc 0000000005f0a550 base.odex_maps_compiled_arm64 (hfn.K+480)
+ #19 pc 0000000005f09d3c base.odex_maps_compiled_arm64 (hfn.J+364)
+ #20 pc 0000000005f09b48 base.odex_maps_compiled_arm64 (hfn.H+56)
+ #21 pc 00000000028b9e98 base.odex_maps_compiled_arm64 (bdtd.o+3528)
+ #22 pc 0000000003367630 base.odex_maps_compiled_arm64 (bqyr.q+4368)
+ #23 pc 0000000003354ad8 base.odex_maps_compiled_arm64 (bqye.b+3144)
+ #24 pc 00000000033828fc base.odex_maps_compiled_arm64 (brna.e+332)
+ #25 pc 0000000003382a80 base.odex_maps_compiled_arm64 (brna.b+96)
+ #26 pc 000000000338f35c base.odex_maps_compiled_arm64 (brsh.e+236)
+ #27 pc 000000000338f214 base.odex_maps_compiled_arm64 (brsh.d+212)
+ #28 pc 000000000338dafc base.odex_maps_compiled_arm64 (brsf.a+76)
+ #29 pc 000000000339173c base.odex_maps_compiled_arm64 (brsp.d+316)
+ #30 pc 000000000338f560 base.odex_maps_compiled_arm64 (brsj.xI+224)
+ #31 pc 0000000002eb4f64 base.odex_maps_compiled_arm64 (buzf.b+1380)
+ #32 pc 0000000004ce2d98 base.odex_maps_compiled_arm64 (dgvq.run+440)
+ #33 pc 00000000004eef60 boot-framework.oat (android.os.Handler.dispatchMessage+80)
+ #34 pc 00000000004f1dfc boot-framework.oat (android.os.Looper.loopOnce+1036)
+ #35 pc 00000000004f1954 boot-framework.oat (android.os.Looper.loop+516)
+ #36 pc 00000000002ce21c boot-framework.oat (android.app.ActivityThread.main+732)
+ #37 pc 00000000002937e8 libart.so (art_quick_invoke_static_stub+568)
+ #38 pc 00000000003026c0 libart.so (_jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)+608)
+ #39 pc 0000000000302438 libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)+52)
+ #40 pc 00000000000b2f74 boot.oat (art_jni_trampoline+132)
+ #41 pc 000000000081aadc boot-framework.oat (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run+140)
+ #42 pc 0000000000822e18 boot-framework.oat (com.android.internal.os.ZygoteInit.main+2472)
+ #43 pc 00000000002937e8 libart.so (art_quick_invoke_static_stub+568)
+ #44 pc 00000000003dff34 libart.so (art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+464)
+ #45 pc 00000000005c43f0 libart.so (art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+268)
+ #46 pc 00000000000b0ac4 libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+120)
+ #47 pc 00000000000bc078 libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+836)
+ #48 pc 000000000000258c app_process64 (main+1336)
+ #49 pc 00000000000488d8 libc.so (__libc_init+96)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/regs.txt
new file mode 100644
index 0000000..44b1c3d
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/regs.txt
@@ -0,0 +1,34 @@
+x0: 7e868e244c
+x1: 7e868e244c
+x2: 7bff757440
+x3: 7fe2362a48
+x4: 20
+x5: 7c1f7185a0
+x6: 7d4f6ba140
+x7: 7d4f6a6f50
+x8: 7c1f7185c0
+x9: 0
+x10: 7bff69b000
+x11: 30
+x12: f
+x13: 7d4f6cb760
+x14: 1
+x15: 16b54cbc
+x16: 7e77ab57d0
+x17: 7e86850b40
+x18: 7e8a6b6000
+x19: 7d4f6a1be0
+x20: 7c6f6de2d0
+x21: 8
+x22: 7c1f7185b8
+x23: 7d4f6d26a0
+x24: 7bff6e28c0
+x25: 1
+x26: 7bde215000
+x27: 7c1f718580
+x28: 7d5f69d220
+x29: 7fe2362b00
+lr: 7bdda3b300
+sp: 7fe2362a80
+pc: 7bdda3b1e0
+pst: 80000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/stack.data
new file mode 100644
index 0000000..45170f3
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28613_main-thread/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/links.txt
new file mode 100644
index 0000000..03ba1a5
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/links.txt
@@ -0,0 +1,4 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/maps.txt
new file mode 100644
index 0000000..b624632
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0 boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0 boot.oat
+7b590f4000-7b5a2aa000 r--p 0 00:00 0 base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0 base.odex_maps_compiled_arm64
+7bdd800000-7bdda00000 r--p 0 00:00 0 libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0 libart.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/output.txt
new file mode 100644
index 0000000..cd61268
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/output.txt
@@ -0,0 +1,24 @@
+ #00 pc 000000000004c35c libc.so (syscall+28)
+ #01 pc 0000000000411604 libart.so (art::Thread::Park(bool, long)+2300)
+ #02 pc 0000000000410790 libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+ #03 pc 00000000000adf4c boot.oat (art_jni_trampoline+108)
+ #04 pc 00000000002f9ccc boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos+1228)
+ #05 pc 00000000001235b0 boot.oat (java.util.concurrent.CountDownLatch.await+480)
+ #06 pc 0000000003e2e390 base.odex_maps_compiled_arm64 (cqqp.e+448)
+ #07 pc 0000000003ac39a4 base.odex_maps_compiled_arm64 (clys.e+1060)
+ #08 pc 0000000003ac3e4c base.odex_maps_compiled_arm64 (clys.c+44)
+ #09 pc 0000000002b8ebb0 base.odex_maps_compiled_arm64 (bmob.call+64)
+ #10 pc 00000000054c72d8 base.odex_maps_compiled_arm64 (dgxq.a+72)
+ #11 pc 0000000004ce4d54 base.odex_maps_compiled_arm64 (dgwi.run+148)
+ #12 pc 0000000005672604 base.odex_maps_compiled_arm64 (dgxr.run+68)
+ #13 pc 0000000002f064d4 base.odex_maps_compiled_arm64 (bwkg.run+148)
+ #14 pc 000000000037b75c boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+988)
+ #15 pc 0000000000374f84 boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+ #16 pc 0000000002f041f4 base.odex_maps_compiled_arm64 (bwjw.run+564)
+ #17 pc 00000000001bf0bc boot.oat (java.lang.Thread.run+76)
+ #18 pc 0000000000293564 libart.so (art_quick_invoke_stub+548)
+ #19 pc 00000000002c6558 libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+ #20 pc 0000000000367ea8 libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+ #21 pc 00000000003e9b6c libart.so (art::Thread::CreateCallback(void*)+1004)
+ #22 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #23 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28644/regs.txt
new file mode 100644
index 0000000..6ed344d
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6e9068
+x1: 80
+x2: 2
+x3: 7b58af6368
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 3b9ac0a4
+x10: 3b
+x11: 87fc4d8789a28136
+x12: 18
+x13: 9271d475358
+x14: bb1f91d69b7d5
+x15: 15f199c2
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b4d7ea000
+x19: 7d4f6e9068
+x20: 7d4f6e9030
+x21: df8474ea4
+x22: 0
+x23: 44
+x24: 7bde215000
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b58af7000
+x29: 7b58af6390
+lr: 7bddc11608
+sp: 7b58af6360
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28644/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28644/stack.data
new file mode 100644
index 0000000..4662d5f
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28644/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/links.txt
new file mode 100644
index 0000000..03ba1a5
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/links.txt
@@ -0,0 +1,4 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/maps.txt
new file mode 100644
index 0000000..b624632
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0 boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0 boot.oat
+7b590f4000-7b5a2aa000 r--p 0 00:00 0 base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0 base.odex_maps_compiled_arm64
+7bdd800000-7bdda00000 r--p 0 00:00 0 libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0 libart.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/output.txt
new file mode 100644
index 0000000..e0fff1f
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/output.txt
@@ -0,0 +1,18 @@
+ #00 pc 000000000004c35c libc.so (syscall+28)
+ #01 pc 0000000000411148 libart.so (art::Thread::Park(bool, long)+1088)
+ #02 pc 0000000000410790 libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+ #03 pc 00000000000adf4c boot.oat (art_jni_trampoline+108)
+ #04 pc 000000000023c924 boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await+756)
+ #05 pc 00000000003d966c boot.oat (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take+524)
+ #06 pc 0000000000394ab8 boot.oat ([DEDUPED]+40)
+ #07 pc 0000000000377574 boot.oat (java.util.concurrent.ThreadPoolExecutor.getTask+484)
+ #08 pc 000000000037b46c boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+236)
+ #09 pc 0000000000374f84 boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+ #10 pc 00000000049a8668 base.odex_maps_compiled_arm64 (czkj.run+120)
+ #11 pc 00000000001bf0bc boot.oat (java.lang.Thread.run+76)
+ #12 pc 0000000000293564 libart.so (art_quick_invoke_stub+548)
+ #13 pc 00000000002c6558 libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+ #14 pc 0000000000367ea8 libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+ #15 pc 00000000003e9b6c libart.so (art::Thread::CreateCallback(void*)+1004)
+ #16 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #17 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28648/regs.txt
new file mode 100644
index 0000000..19fdcdb
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6effa8
+x1: 80
+x2: 2
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 7bff6b7e40
+x10: 430000
+x11: 10
+x12: 13758080
+x13: 10
+x14: 0
+x15: 177d30dc
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b4bc6a000
+x19: 7d4f6effa8
+x20: 7d4f6eff70
+x21: 47
+x22: 0
+x23: 7bde215000
+x24: 0
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b586cf000
+x29: 7b586ce500
+lr: 7bddc1114c
+sp: 7b586ce4d0
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28648/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28648/stack.data
new file mode 100644
index 0000000..dbf4731
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28648/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/links.txt
new file mode 100644
index 0000000..03ba1a5
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/links.txt
@@ -0,0 +1,4 @@
+../../common/base.odex_maps_compiled_arm64 base.odex_maps_compiled_arm64
+../../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/maps.txt
new file mode 100644
index 0000000..c3ff7aa
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/maps.txt
@@ -0,0 +1,9 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0 boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0 boot.oat
+7b590f4000-7b5a2aa000 r--p 0 00:00 0 base.odex_maps_compiled_arm64
+7b5a2aa000-7b6061c000 r-xp 11b6000 00:00 0 base.odex_maps_compiled_arm64
+7bdd326000-7bdd800000 r--p 0 00:00 0 core-oj.jar
+7bdd800000-7bdda00000 r--p 0 00:00 0 libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0 libart.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/output.txt
new file mode 100644
index 0000000..3ca316f
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/output.txt
@@ -0,0 +1,20 @@
+ #00 pc 000000000004c35c libc.so (syscall+28)
+ #01 pc 0000000000411148 libart.so (art::Thread::Park(bool, long)+1088)
+ #02 pc 0000000000410790 libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+ #03 pc 00000000000adf4c boot.oat (art_jni_trampoline+108)
+ #04 pc 000000000023c924 boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await+756)
+ #05 pc 000000000020aec4 libart.so (nterp_helper+7636)
+ #06 pc 00000000001faf02 core-oj.jar
+ #07 pc 000000000020a044 libart.so (nterp_helper+3924)
+ #08 pc 00000000001fab38 core-oj.jar
+ #09 pc 0000000000377574 boot.oat (java.util.concurrent.ThreadPoolExecutor.getTask+484)
+ #10 pc 000000000037b46c boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+236)
+ #11 pc 0000000000374f84 boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+ #12 pc 0000000002f041f4 base.odex_maps_compiled_arm64 (bwjw.run+564)
+ #13 pc 00000000001bf0bc boot.oat (java.lang.Thread.run+76)
+ #14 pc 0000000000293564 libart.so (art_quick_invoke_stub+548)
+ #15 pc 00000000002c6558 libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+ #16 pc 0000000000367ea8 libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+ #17 pc 00000000003e9b6c libart.so (art::Thread::CreateCallback(void*)+1004)
+ #18 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #19 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/regs.txt
new file mode 100644
index 0000000..6428f11
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6f8ab8
+x1: 80
+x2: 2
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 7bff6b7e40
+x10: 430000
+x11: 10
+x12: 4
+x13: 1ac8703d
+x14: 7b57dcf610
+x15: 177d143c
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b44f5e000
+x19: 7d4f6f8ab8
+x20: 7d4f6f8a80
+x21: 47
+x22: 0
+x23: 7bde215000
+x24: 0
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b57dd0000
+x29: 7b57dcf3c0
+lr: 7bddc1114c
+sp: 7b57dcf390
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/stack.data
new file mode 100644
index 0000000..a44fcdd
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28656_oat_odex_jar/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/links.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/links.txt
new file mode 100644
index 0000000..3f9febc
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/links.txt
@@ -0,0 +1 @@
+../../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/maps.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/maps.txt
new file mode 100644
index 0000000..b9a4bb8
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/maps.txt
@@ -0,0 +1,3 @@
+7b38a95000-7b38ef0000 r-xp 1000 00:00 0 CronetDynamite.apk
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/output.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/output.txt
new file mode 100644
index 0000000..7a65fc6
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/output.txt
@@ -0,0 +1,4 @@
+ #00 pc 000000000004c35c libc.so (syscall+28)
+ #01 pc 0000000000050980 libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+144)
+ #02 pc 00000000000b0c64 libc.so (pthread_cond_timedwait+140)
+ #03 pc 000000000027550c CronetDynamite.apk (offset 0x1000)
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/regs.txt b/libunwindstack/offline_files/maps_compiled_arm64/28667/regs.txt
new file mode 100644
index 0000000..9416bb2
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/regs.txt
@@ -0,0 +1,34 @@
+x0: 7b35894a10
+x1: 89
+x2: 2
+x3: 7b358948e0
+x4: 0
+x5: ffffffff
+x6: ffffffff
+x7: 2e65746163696669
+x8: 62
+x9: 89
+x10: 9
+x11: 0
+x12: 0
+x13: 92708000f58
+x14: 23ef2f8bd6c2ce
+x15: 15f199c2
+x16: 7e868d40a8
+x17: 7e86860340
+x18: 7b34300000
+x19: 2
+x20: 7b358948e0
+x21: 7b35894a10
+x22: 89
+x23: 7b35894fb0
+x24: 7b35895000
+x25: 4b
+x26: 7c2f6b0000
+x27: 7b38b4a5b0
+x28: 1
+x29: 7b35894840
+lr: 7e86864984
+sp: 7b35894820
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/maps_compiled_arm64/28667/stack.data b/libunwindstack/offline_files/maps_compiled_arm64/28667/stack.data
new file mode 100644
index 0000000..5f8f82c
--- /dev/null
+++ b/libunwindstack/offline_files/maps_compiled_arm64/28667/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/offset_arm/libc.so.gz b/libunwindstack/offline_files/offset_arm/libc.so.gz
new file mode 100644
index 0000000..e592dfb
--- /dev/null
+++ b/libunwindstack/offline_files/offset_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/offset_arm/libunwindstack_test.gz b/libunwindstack/offline_files/offset_arm/libunwindstack_test.gz
new file mode 100644
index 0000000..c27ce1a
--- /dev/null
+++ b/libunwindstack/offline_files/offset_arm/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/maps.txt b/libunwindstack/offline_files/offset_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/maps.txt
rename to libunwindstack/offline_files/offset_arm/maps.txt
diff --git a/libunwindstack/offline_files/offset_arm/output.txt b/libunwindstack/offline_files/offset_arm/output.txt
new file mode 100644
index 0000000..36bd388
--- /dev/null
+++ b/libunwindstack/offline_files/offset_arm/output.txt
@@ -0,0 +1,19 @@
+ #00 pc 0032bfa0 libunwindstack_test (SignalInnerFunction+40)
+ #01 pc 0032bfeb libunwindstack_test (SignalMiddleFunction+2)
+ #02 pc 0032bff3 libunwindstack_test (SignalOuterFunction+2)
+ #03 pc 0032fed3 libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)
+ #04 pc 0002652c libc.so (__restore)
+ #05 pc 00000000 <unknown>
+ #06 pc 0032c2d9 libunwindstack_test (InnerFunction+736)
+ #07 pc 0032cc4f libunwindstack_test (MiddleFunction+42)
+ #08 pc 0032cc81 libunwindstack_test (OuterFunction+42)
+ #09 pc 0032e547 libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+270)
+ #10 pc 0032ed99 libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)
+ #11 pc 00354453 libunwindstack_test (testing::Test::Run()+154)
+ #12 pc 00354de7 libunwindstack_test (testing::TestInfo::Run()+194)
+ #13 pc 00355105 libunwindstack_test (testing::TestCase::Run()+180)
+ #14 pc 0035a215 libunwindstack_test (testing::internal::UnitTestImpl::RunAllTests()+664)
+ #15 pc 00359f4f libunwindstack_test (testing::UnitTest::Run()+110)
+ #16 pc 0034d3db libunwindstack_test (main+38)
+ #17 pc 00092c0d libc.so (__libc_init+48)
+ #18 pc 0004202f libunwindstack_test (_start_main+38)
diff --git a/libunwindstack/tests/files/offline/offset_arm/regs.txt b/libunwindstack/offline_files/offset_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/regs.txt
rename to libunwindstack/offline_files/offset_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack0.data b/libunwindstack/offline_files/offset_arm/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/stack0.data
rename to libunwindstack/offline_files/offset_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/stack1.data b/libunwindstack/offline_files/offset_arm/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/offset_arm/stack1.data
rename to libunwindstack/offline_files/offset_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/pauth_pc_arm64/libc.so.gz b/libunwindstack/offline_files/pauth_pc_arm64/libc.so.gz
new file mode 100644
index 0000000..48397a0
--- /dev/null
+++ b/libunwindstack/offline_files/pauth_pc_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/maps.txt b/libunwindstack/offline_files/pauth_pc_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/pauth_pc_arm64/maps.txt
rename to libunwindstack/offline_files/pauth_pc_arm64/maps.txt
diff --git a/libunwindstack/offline_files/pauth_pc_arm64/output.txt b/libunwindstack/offline_files/pauth_pc_arm64/output.txt
new file mode 100644
index 0000000..a41d36a
--- /dev/null
+++ b/libunwindstack/offline_files/pauth_pc_arm64/output.txt
@@ -0,0 +1,26 @@
+ #00 pc 00000000000404a8 toybox (do_print+28)
+ #01 pc 0000000000040270 toybox (do_find+5072)
+ #02 pc 000000000002c640 toybox (dirtree_handle_callback+40)
+ #03 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #04 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #05 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #06 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #07 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #08 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #09 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #10 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #11 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #12 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #13 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #14 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #15 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #16 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #17 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #18 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #19 pc 000000000002c588 toybox (dirtree_recurse+200)
+ #20 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)
+ #21 pc 000000000003ee54 toybox (find_main+272)
+ #22 pc 0000000000034834 toybox (toy_exec_which+88)
+ #23 pc 00000000000342cc toybox (toybox_main+148)
+ #24 pc 00000000000348b4 toybox (main+120)
+ #25 pc 00000000000499d8 libc.so (__libc_init+112)
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/regs.txt b/libunwindstack/offline_files/pauth_pc_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/pauth_pc_arm64/regs.txt
rename to libunwindstack/offline_files/pauth_pc_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/stack.data b/libunwindstack/offline_files/pauth_pc_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/pauth_pc_arm64/stack.data
rename to libunwindstack/offline_files/pauth_pc_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/pauth_pc_arm64/toybox.gz b/libunwindstack/offline_files/pauth_pc_arm64/toybox.gz
new file mode 100644
index 0000000..a8edec9
--- /dev/null
+++ b/libunwindstack/offline_files/pauth_pc_arm64/toybox.gz
Binary files differ
diff --git a/libunwindstack/offline_files/photos_reset_arm64/links.txt b/libunwindstack/offline_files/photos_reset_arm64/links.txt
new file mode 100644
index 0000000..aadfa08
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/links.txt
@@ -0,0 +1,3 @@
+../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/photos_reset_arm64/maps.txt b/libunwindstack/offline_files/photos_reset_arm64/maps.txt
new file mode 100644
index 0000000..49237cc
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0 boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0 boot.oat
+7b64f9d000-7b65286000 r--p 0 00:00 0 base.apk!classes4.dex
+7b65c00000-7b66307000 r--p 0 00:00 0 base.apk!classes2.dex
+7bdd800000-7bdda00000 r--p 0 00:00 0 libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0 libart.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/photos_reset_arm64/output.txt b/libunwindstack/offline_files/photos_reset_arm64/output.txt
new file mode 100644
index 0000000..d046c00
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/output.txt
@@ -0,0 +1,20 @@
+ #00 pc 000000000004c35c libc.so (syscall+28)
+ #01 pc 0000000000353010 libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+148)
+ #02 pc 000000000031d7dc libart.so (art::Monitor::Wait(art::Thread*, long, int, bool, art::ThreadState)+1228)
+ #03 pc 000000000031ce90 libart.so (art::Monitor::Wait(art::Thread*, art::ObjPtr<art::mirror::Object>, long, int, bool, art::ThreadState)+524)
+ #04 pc 00000000000ab35c boot.oat (art_jni_trampoline+108)
+ #05 pc 00000000000cc278 boot.oat (java.lang.ref.ReferenceQueue.remove+360)
+ #06 pc 00000000000cc0ec boot.oat (java.lang.ref.ReferenceQueue.remove+44)
+ #07 pc 000000000020a0a0 libart.so (nterp_helper+4016)
+ #08 pc 0000000000404264 base.apk!classes2.dex
+ #09 pc 000000000037b75c boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+988)
+ #10 pc 0000000000374f84 boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+ #11 pc 000000000020aec4 libart.so (nterp_helper+7636)
+ #12 pc 00000000000f2a24 base.apk!classes4.dex
+ #13 pc 00000000001bf0bc boot.oat (java.lang.Thread.run+76)
+ #14 pc 0000000000293564 libart.so (art_quick_invoke_stub+548)
+ #15 pc 00000000002c6558 libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+ #16 pc 0000000000367ea8 libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+ #17 pc 00000000003e9b6c libart.so (art::Thread::CreateCallback(void*)+1004)
+ #18 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #19 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/photos_reset_arm64/regs.txt b/libunwindstack/offline_files/photos_reset_arm64/regs.txt
new file mode 100644
index 0000000..77c2e78
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 7bff6e4bb0
+x1: 80
+x2: 0
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 3030303030303034
+x8: 62
+x9: 87fc4d8789a28136
+x10: 0
+x11: ff6fd6f8
+x12: ffff00000eff
+x13: 106b9c07
+x14: 7b5d33e730
+x15: 0
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b5ce14000
+x19: 7bff6e4ba0
+x20: 7d4f6eac00
+x21: 0
+x22: 7bff6e4bb0
+x23: 0
+x24: 7c0f6ccb50
+x25: 7b5d33f000
+x26: 1
+x27: 7d2f6b2f30
+x28: 7d2f6b2f18
+x29: 7b5d33e090
+lr: 7bddb53014
+sp: 7b5d33e080
+pc: 7e8686035c
+pst: 60000000
diff --git a/libunwindstack/offline_files/photos_reset_arm64/stack.data b/libunwindstack/offline_files/photos_reset_arm64/stack.data
new file mode 100644
index 0000000..0f576e3
--- /dev/null
+++ b/libunwindstack/offline_files/photos_reset_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk b/libunwindstack/offline_files/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/ANGLEPrebuilt.apk
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/libc.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_arm64/libc.so.gz
new file mode 100644
index 0000000..259534f
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/linker64.gz b/libunwindstack/offline_files/shared_lib_in_apk_arm64/linker64.gz
new file mode 100644
index 0000000..abcaa33
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/linker64.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt b/libunwindstack/offline_files/shared_lib_in_apk_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/maps.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/maps.txt
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/output.txt b/libunwindstack/offline_files/shared_lib_in_apk_arm64/output.txt
new file mode 100644
index 0000000..d7bbf39
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/output.txt
@@ -0,0 +1,7 @@
+ #00 pc 000000000014ccbc linker64 (__dl_syscall+28)
+ #01 pc 000000000005426c linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)
+ #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)
+ #03 pc 00000000000846f4 libc.so (abort+172)
+ #04 pc 0000000000084ad4 libc.so (__assert2+36)
+ #05 pc 000000000003d5b4 ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) (ANGLEGetUtilityAPI+56)
+ #06 pc 000000000007fe68 libc.so (__libc_init)
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt b/libunwindstack/offline_files/shared_lib_in_apk_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/regs.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data b/libunwindstack/offline_files/shared_lib_in_apk_arm64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack0.data
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data b/libunwindstack/offline_files/shared_lib_in_apk_arm64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/stack1.data
rename to libunwindstack/offline_files/shared_lib_in_apk_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_arm64/vdso.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_arm64/vdso.so.gz
new file mode 100644
index 0000000..e08b7cc
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_arm64/vdso.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/lib_mem.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/lib_mem.data
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/lib_mem.data
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/libc.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/libc.so.gz
new file mode 100644
index 0000000..31e71ca
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/linker64.gz b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/linker64.gz
new file mode 100644
index 0000000..0f1424e
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/linker64.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/maps.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/maps.txt
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/output.txt b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/output.txt
new file mode 100644
index 0000000..c733893
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/output.txt
@@ -0,0 +1,7 @@
+ #00 pc 000000000014ccbc linker64 (__dl_syscall+28)
+ #01 pc 000000000005426c linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)
+ #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)
+ #03 pc 00000000000846f4 libc.so (abort+172)
+ #04 pc 0000000000084ad4 libc.so (__assert2+36)
+ #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x21d5000)
+ #06 pc 000000000007fe68 libc.so (__libc_init)
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/regs.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack0.data
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/stack1.data
rename to libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/vdso.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/vdso.so.gz
new file mode 100644
index 0000000..bf06ecf
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_memory_only_arm64/vdso.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/libc.so.gz b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/libc.so.gz
new file mode 100644
index 0000000..cac2635
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/maps.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/maps.txt
diff --git a/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/output.txt b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/output.txt
new file mode 100644
index 0000000..cb1978b
--- /dev/null
+++ b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/output.txt
@@ -0,0 +1,13 @@
+ #00 pc 00000000000814bc libc.so (syscall+28)
+ #01 pc 00000000008cdf5c test.apk (offset 0x5000)
+ #02 pc 00000000008cde9c test.apk (offset 0x5000)
+ #03 pc 00000000008cdd70 test.apk (offset 0x5000)
+ #04 pc 00000000008ce408 test.apk (offset 0x5000)
+ #05 pc 00000000008ce8d8 test.apk (offset 0x5000)
+ #06 pc 00000000008ce814 test.apk (offset 0x5000)
+ #07 pc 00000000008bcf60 test.apk (offset 0x5000)
+ #08 pc 0000000000133024 test.apk (offset 0x5000)
+ #09 pc 0000000000134ad0 test.apk (offset 0x5000)
+ #10 pc 0000000000134b64 test.apk (offset 0x5000)
+ #11 pc 00000000000e406c libc.so (__pthread_start(void*)+36)
+ #12 pc 0000000000085e18 libc.so (__start_thread+64)
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/regs.txt
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/stack.data
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk b/libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/test.apk
similarity index 100%
rename from libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/test.apk
rename to libunwindstack/offline_files/shared_lib_in_apk_single_map_arm64/test.apk
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86/libc.so.gz b/libunwindstack/offline_files/signal_fde_x86/libc.so.gz
new file mode 100644
index 0000000..af44763
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86/libunwindstack_test.gz b/libunwindstack/offline_files/signal_fde_x86/libunwindstack_test.gz
new file mode 100644
index 0000000..92e59ea
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/maps.txt b/libunwindstack/offline_files/signal_fde_x86/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/maps.txt
rename to libunwindstack/offline_files/signal_fde_x86/maps.txt
diff --git a/libunwindstack/offline_files/signal_fde_x86/output.txt b/libunwindstack/offline_files/signal_fde_x86/output.txt
new file mode 100644
index 0000000..eb1a0ec
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86/output.txt
@@ -0,0 +1,20 @@
+ #00 pc 007914d9 libunwindstack_test (SignalInnerFunction+25)
+ #01 pc 007914fc libunwindstack_test (SignalMiddleFunction+28)
+ #02 pc 0079152c libunwindstack_test (SignalOuterFunction+28)
+ #03 pc 0079af62 libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+50)
+ #04 pc 00058fb0 libc.so (__restore)
+ #05 pc 00000000 <unknown>
+ #06 pc 0079161a libunwindstack_test (InnerFunction+218)
+ #07 pc 007923aa libunwindstack_test (MiddleFunction+42)
+ #08 pc 007923ea libunwindstack_test (OuterFunction+42)
+ #09 pc 00797444 libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+868)
+ #10 pc 007985b8 libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+56)
+ #11 pc 00817a19 libunwindstack_test
+ #12 pc 008178c5 libunwindstack_test (testing::Test::Run()+277)
+ #13 pc 00818d3e libunwindstack_test (testing::TestInfo::Run()+318)
+ #14 pc 008198b4 libunwindstack_test (testing::TestSuite::Run()+436)
+ #15 pc 00828cb0 libunwindstack_test (testing::internal::UnitTestImpl::RunAllTests()+1216)
+ #16 pc 0082870f libunwindstack_test (testing::UnitTest::Run()+367)
+ #17 pc 0084031e libunwindstack_test (IsolateMain+2334)
+ #18 pc 0083f9e9 libunwindstack_test (main+41)
+ #19 pc 00050646 libc.so (__libc_init+118)
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/regs.txt b/libunwindstack/offline_files/signal_fde_x86/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/regs.txt
rename to libunwindstack/offline_files/signal_fde_x86/regs.txt
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/stack0.data b/libunwindstack/offline_files/signal_fde_x86/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/stack0.data
rename to libunwindstack/offline_files/signal_fde_x86/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/stack1.data b/libunwindstack/offline_files/signal_fde_x86/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86/stack1.data
rename to libunwindstack/offline_files/signal_fde_x86/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86_64/libc.so.gz b/libunwindstack/offline_files/signal_fde_x86_64/libc.so.gz
new file mode 100644
index 0000000..048cb54
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86_64/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/signal_fde_x86_64/libunwindstack_test.gz b/libunwindstack/offline_files/signal_fde_x86_64/libunwindstack_test.gz
new file mode 100644
index 0000000..e60eb34
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86_64/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/maps.txt b/libunwindstack/offline_files/signal_fde_x86_64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/maps.txt
rename to libunwindstack/offline_files/signal_fde_x86_64/maps.txt
diff --git a/libunwindstack/offline_files/signal_fde_x86_64/output.txt b/libunwindstack/offline_files/signal_fde_x86_64/output.txt
new file mode 100644
index 0000000..6ac6e3a
--- /dev/null
+++ b/libunwindstack/offline_files/signal_fde_x86_64/output.txt
@@ -0,0 +1,18 @@
+ #00 pc 000000000058415b libunwindstack_test (SignalInnerFunction+11)
+ #01 pc 0000000000584168 libunwindstack_test (SignalMiddleFunction+8)
+ #02 pc 0000000000584178 libunwindstack_test (SignalOuterFunction+8)
+ #03 pc 000000000058ac77 libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+23)
+ #04 pc 0000000000057d10 libc.so (__restore_rt)
+ #05 pc 0000000000000000 <unknown>
+ #06 pc 0000000000584244 libunwindstack_test (InnerFunction+196)
+ #07 pc 0000000000584b44 libunwindstack_test (MiddleFunction+20)
+ #08 pc 0000000000584b64 libunwindstack_test (OuterFunction+20)
+ #09 pc 0000000000588457 libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+583)
+ #10 pc 0000000000588f67 libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+23)
+ #11 pc 00000000005d9c38 libunwindstack_test (testing::Test::Run()+216)
+ #12 pc 00000000005daf9a libunwindstack_test (testing::TestInfo::Run()+266)
+ #13 pc 00000000005dba46 libunwindstack_test (testing::TestSuite::Run()+390)
+ #14 pc 00000000005ea4c6 libunwindstack_test (testing::internal::UnitTestImpl::RunAllTests()+1190)
+ #15 pc 00000000005e9f61 libunwindstack_test (testing::UnitTest::Run()+337)
+ #16 pc 0000000000600155 libunwindstack_test (IsolateMain+2037)
+ #17 pc 000000000004e405 libc.so (__libc_init+101)
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/regs.txt b/libunwindstack/offline_files/signal_fde_x86_64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/regs.txt
rename to libunwindstack/offline_files/signal_fde_x86_64/regs.txt
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/stack0.data b/libunwindstack/offline_files/signal_fde_x86_64/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/stack0.data
rename to libunwindstack/offline_files/signal_fde_x86_64/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/stack1.data b/libunwindstack/offline_files/signal_fde_x86_64/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_fde_x86_64/stack1.data
rename to libunwindstack/offline_files/signal_fde_x86_64/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/signal_load_bias_arm/libc.so.gz b/libunwindstack/offline_files/signal_load_bias_arm/libc.so.gz
new file mode 100644
index 0000000..8b9d517
--- /dev/null
+++ b/libunwindstack/offline_files/signal_load_bias_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/signal_load_bias_arm/libunwindstack_unit_test.gz b/libunwindstack/offline_files/signal_load_bias_arm/libunwindstack_unit_test.gz
new file mode 100644
index 0000000..bd28d05
--- /dev/null
+++ b/libunwindstack/offline_files/signal_load_bias_arm/libunwindstack_unit_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt b/libunwindstack/offline_files/signal_load_bias_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/maps.txt
rename to libunwindstack/offline_files/signal_load_bias_arm/maps.txt
diff --git a/libunwindstack/offline_files/signal_load_bias_arm/output.txt b/libunwindstack/offline_files/signal_load_bias_arm/output.txt
new file mode 100644
index 0000000..a4e2d6c
--- /dev/null
+++ b/libunwindstack/offline_files/signal_load_bias_arm/output.txt
@@ -0,0 +1,17 @@
+ #00 pc 0029ef9e libunwindstack_unit_test (SignalInnerFunction+10)
+ #01 pc 0029efa7 libunwindstack_unit_test (SignalMiddleFunction+2)
+ #02 pc 0029efaf libunwindstack_unit_test (SignalOuterFunction+2)
+ #03 pc 002a280b libunwindstack_unit_test (unwindstack::SignalCallerHandler(int, siginfo*, void*)+10)
+ #04 pc 00058bd4 libc.so (__restore)
+ #05 pc 0029f01e libunwindstack_unit_test (InnerFunction+106)
+ #06 pc 0029f633 libunwindstack_unit_test (MiddleFunction+16)
+ #07 pc 0029f64b libunwindstack_unit_test (OuterFunction+16)
+ #08 pc 002a1711 libunwindstack_unit_test (unwindstack::RemoteThroughSignal(int, unsigned int)+260)
+ #09 pc 002a1603 libunwindstack_unit_test (unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+10)
+ #10 pc 002c8fe3 libunwindstack_unit_test (testing::Test::Run()+130)
+ #11 pc 002c9b25 libunwindstack_unit_test (testing::TestInfo::Run()+184)
+ #12 pc 002c9e27 libunwindstack_unit_test (testing::TestSuite::Run()+202)
+ #13 pc 002d193d libunwindstack_unit_test (testing::internal::UnitTestImpl::RunAllTests()+660)
+ #14 pc 002d160b libunwindstack_unit_test (testing::UnitTest::Run()+134)
+ #15 pc 002de035 libunwindstack_unit_test (IsolateMain+680)
+ #16 pc 00058155 libc.so (__libc_init+68)
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt b/libunwindstack/offline_files/signal_load_bias_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/regs.txt
rename to libunwindstack/offline_files/signal_load_bias_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data b/libunwindstack/offline_files/signal_load_bias_arm/stack0.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/stack0.data
rename to libunwindstack/offline_files/signal_load_bias_arm/stack0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data b/libunwindstack/offline_files/signal_load_bias_arm/stack1.data
similarity index 100%
rename from libunwindstack/tests/files/offline/signal_load_bias_arm/stack1.data
rename to libunwindstack/offline_files/signal_load_bias_arm/stack1.data
Binary files differ
diff --git a/libunwindstack/offline_files/straddle_arm/libbase.so.gz b/libunwindstack/offline_files/straddle_arm/libbase.so.gz
new file mode 100644
index 0000000..94093a5
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm/libbase.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/straddle_arm/libc.so.gz b/libunwindstack/offline_files/straddle_arm/libc.so.gz
new file mode 100644
index 0000000..415e83f
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm/libc.so.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/maps.txt b/libunwindstack/offline_files/straddle_arm/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm/maps.txt
rename to libunwindstack/offline_files/straddle_arm/maps.txt
diff --git a/libunwindstack/offline_files/straddle_arm/output.txt b/libunwindstack/offline_files/straddle_arm/output.txt
new file mode 100644
index 0000000..1a26405
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm/output.txt
@@ -0,0 +1,4 @@
+ #00 pc 0001a9f8 libc.so (abort+64)
+ #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6)
+ #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748)
+ #03 pc 00015147 /does/not/exist/libhidlbase.so
diff --git a/libunwindstack/tests/files/offline/straddle_arm/regs.txt b/libunwindstack/offline_files/straddle_arm/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm/regs.txt
rename to libunwindstack/offline_files/straddle_arm/regs.txt
diff --git a/libunwindstack/tests/files/offline/straddle_arm/stack.data b/libunwindstack/offline_files/straddle_arm/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm/stack.data
rename to libunwindstack/offline_files/straddle_arm/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/straddle_arm64/libunwindstack_test.gz b/libunwindstack/offline_files/straddle_arm64/libunwindstack_test.gz
new file mode 100644
index 0000000..82460c8
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm64/libunwindstack_test.gz
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/maps.txt b/libunwindstack/offline_files/straddle_arm64/maps.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm64/maps.txt
rename to libunwindstack/offline_files/straddle_arm64/maps.txt
diff --git a/libunwindstack/offline_files/straddle_arm64/output.txt b/libunwindstack/offline_files/straddle_arm64/output.txt
new file mode 100644
index 0000000..c54daec
--- /dev/null
+++ b/libunwindstack/offline_files/straddle_arm64/output.txt
@@ -0,0 +1,6 @@
+ #00 pc 0000000000429fd8 libunwindstack_test (SignalInnerFunction+24)
+ #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)
+ #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)
+ #03 pc 000000000042d8fc libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned int)+20)
+ #04 pc 000000000042d8d8 libunwindstack_test (unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)
+ #05 pc 0000000000455d70 libunwindstack_test (testing::Test::Run()+392)
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/regs.txt b/libunwindstack/offline_files/straddle_arm64/regs.txt
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm64/regs.txt
rename to libunwindstack/offline_files/straddle_arm64/regs.txt
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/stack.data b/libunwindstack/offline_files/straddle_arm64/stack.data
similarity index 100%
rename from libunwindstack/tests/files/offline/straddle_arm64/stack.data
rename to libunwindstack/offline_files/straddle_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libbinder.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libbinder.so.gz
new file mode 100644
index 0000000..0e52d42
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libbinder.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libcodec2_client.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libcodec2_client.so.gz
new file mode 100644
index 0000000..19a563e
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libcodec2_client.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libgui.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libgui.so.gz
new file mode 100644
index 0000000..45b4068
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libgui.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libsfplugin_ccodec.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libsfplugin_ccodec.so.gz
new file mode 100644
index 0000000..3db0b38
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libsfplugin_ccodec.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright.so.gz
new file mode 100644
index 0000000..34398ad
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright_foundation.so.gz b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright_foundation.so.gz
new file mode 100644
index 0000000..770de8a
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/libstagefright_foundation.so.gz
Binary files differ
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/links.txt b/libunwindstack/offline_files/youtube_compiled_arm64/links.txt
new file mode 100644
index 0000000..72892f4
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/links.txt
@@ -0,0 +1,3 @@
+../common/libandroid_runtime.so_7d88088666db374aecde2fbe51bff2f4 libandroid_runtime.so
+../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
+../common/libutils.so_b8aa8db7e6895d0ba92398ca5d3ed2d4 libutils.so
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/maps.txt b/libunwindstack/offline_files/youtube_compiled_arm64/maps.txt
new file mode 100644
index 0000000..0d595c3
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/maps.txt
@@ -0,0 +1,18 @@
+7e6f820000-7e6f82d000 r--p 0 00:00 0 libutils.so
+7e6f82d000-7e6f839000 r-xp d000 00:00 0 libutils.so
+7e6f881000-7e6f895000 r--p 0 00:00 0 libstagefright_foundation.so
+7e6f895000-7e6f8b4000 r-xp 14000 00:00 0 libstagefright_foundation.so
+7e71cab000-7e71d2f000 r--p 0 00:00 0 libgui.so
+7e71d2f000-7e71dce000 r-xp 84000 00:00 0 libgui.so
+7e72942000-7e7299b000 r--p 0 00:00 0 libbinder.so
+7e7299b000-7e729ec000 r-xp 59000 00:00 0 libbinder.so
+7e77b5e000-7e77b86000 r--p 0 00:00 0 libsfplugin_ccodec.so
+7e77b86000-7e77bf6000 r-xp 28000 00:00 0 libsfplugin_ccodec.so
+7e7a40c000-7e7a48a000 r--p 0 00:00 0 libstagefright.so
+7e7a48a000-7e7a5b8000 r-xp 7e000 00:00 0 libstagefright.so
+7e7c400000-7e7c4af000 r--p 0 00:00 0 libandroid_runtime.so
+7e7c4af000-7e7c5df000 r-xp af000 00:00 0 libandroid_runtime.so
+7e84114000-7e84121000 r--p 0 00:00 0 libcodec2_client.so
+7e84121000-7e8413c000 r-xp d000 00:00 0 libcodec2_client.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/output.txt b/libunwindstack/offline_files/youtube_compiled_arm64/output.txt
new file mode 100644
index 0000000..d832383
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/output.txt
@@ -0,0 +1,23 @@
+ #00 pc 000000000009d5e4 libc.so (__ioctl+4)
+ #01 pc 00000000000593e8 libc.so (ioctl+152)
+ #02 pc 000000000005ddac libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+920)
+ #03 pc 000000000005d7d8 libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+176)
+ #04 pc 00000000000aa0e4 libgui.so (android::BpSurfaceComposer::setTransactionState(android::FrameTimelineInfo const&, android::Vector<android::ComposerState> const&, android::Vector<android::DisplayState> const&, unsigned int, android::sp<android::IBinder> const&, android::InputWindowCommands const&, long, bool, android::client_cache_t const&, bool, std::__1::vector<android::ListenerCallbacks, std::__1::allocator<android::ListenerCallbacks> > const&, unsigned long)+872)
+ #05 pc 00000000000a0ed8 libgui.so (android::SurfaceComposerClient::Transaction::apply(bool)+556)
+ #06 pc 00000000000947ac libgui.so (android::BLASTBufferQueue::processNextBufferLocked(bool)+9748)
+ #07 pc 00000000000ae488 libgui.so (android::BLASTBufferQueue::onFrameAvailable(android::BufferItem const&)+136)
+ #08 pc 00000000000888d8 libgui.so (android::ConsumerBase::onFrameAvailable(android::BufferItem const&)+172)
+ #09 pc 0000000000084448 libgui.so (android::BufferQueue::ProxyConsumerListener::onFrameAvailable(android::BufferItem const&)+92)
+ #10 pc 00000000000c7b8c libgui.so (android::BufferQueueProducer::queueBuffer(int, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+2232)
+ #11 pc 00000000000258f4 libcodec2_client.so (android::hardware::media::c2::OutputBufferQueue::outputBuffer(C2ConstGraphicBlock const&, android::IGraphicBufferProducer::QueueBufferInput const&, android::IGraphicBufferProducer::QueueBufferOutput*)+1104)
+ #12 pc 000000000004a038 libsfplugin_ccodec.so (android::CCodecBufferChannel::renderOutputBuffer(android::sp<android::MediaCodecBuffer> const&, long)+2572)
+ #13 pc 000000000012036c libstagefright.so (android::MediaCodec::onReleaseOutputBuffer(android::sp<android::AMessage> const&)+1184)
+ #14 pc 0000000000110158 libstagefright.so (android::MediaCodec::onMessageReceived(android::sp<android::AMessage> const&)+376)
+ #15 pc 00000000000188c0 libstagefright_foundation.so (android::AHandler::deliverMessage(android::sp<android::AMessage> const&)+84)
+ #16 pc 000000000001e68c libstagefright_foundation.so (android::AMessage::deliver()+188)
+ #17 pc 0000000000019c08 libstagefright_foundation.so (android::ALooper::loop()+592)
+ #18 pc 0000000000012138 libutils.so (android::Thread::_threadLoop(void*)+460)
+ #19 pc 00000000000bc3ec libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+144)
+ #20 pc 0000000000011928 libutils.so (thread_data_t::trampoline(thread_data_t const*)+404)
+ #21 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #22 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/regs.txt b/libunwindstack/offline_files/youtube_compiled_arm64/regs.txt
new file mode 100644
index 0000000..110362b
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 36
+x1: c0306201
+x2: 7b34407370
+x3: 7b344075a0
+x4: 7b344073b0
+x5: 7c8f6ffe54
+x6: 2c
+x7: 0
+x8: 1d
+x9: 7b34407310
+x10: 7b34407310
+x11: 7b344072e0
+x12: ffffff80ffffffd0
+x13: 7c1f790fd0
+x14: 1
+x15: 26
+x16: 7e729f9448
+x17: 7e8686d350
+x18: 7acb076000
+x19: 7b34409000
+x20: 7cbf755480
+x21: 7b34407520
+x22: 7cbf755408
+x23: 7cbf7553e0
+x24: 7cbf7553b8
+x25: 74
+x26: 80407203
+x27: 7b34409000
+x28: 7204
+x29: 7b34407340
+lr: 7e8686d3ec
+sp: 7b34407260
+pc: 7e868b15e4
+pst: 80000000
diff --git a/libunwindstack/offline_files/youtube_compiled_arm64/stack.data b/libunwindstack/offline_files/youtube_compiled_arm64/stack.data
new file mode 100644
index 0000000..e238489
--- /dev/null
+++ b/libunwindstack/offline_files/youtube_compiled_arm64/stack.data
Binary files differ
diff --git a/libunwindstack/offline_files/yt_music_arm64/base.odex.gz b/libunwindstack/offline_files/yt_music_arm64/base.odex.gz
new file mode 100644
index 0000000..a19d1ed
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/base.odex.gz
Binary files differ
diff --git a/libunwindstack/offline_files/yt_music_arm64/links.txt b/libunwindstack/offline_files/yt_music_arm64/links.txt
new file mode 100644
index 0000000..aadfa08
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/links.txt
@@ -0,0 +1,3 @@
+../common/boot.oat_c3e0e6503935c4103ec839d45f3a2183bd910e3c boot.oat
+../common/libart.so_82c0556f4b66528e4a608c100a63b712 libart.so
+../common/libc.so_f3791c53da47e6e72151dcc8088b9048 libc.so
diff --git a/libunwindstack/offline_files/yt_music_arm64/maps.txt b/libunwindstack/offline_files/yt_music_arm64/maps.txt
new file mode 100644
index 0000000..f54f627
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/maps.txt
@@ -0,0 +1,8 @@
+6fcf3000-6fd9e000 r--p 0 00:00 0 boot.oat
+6fd9e000-700fe000 r-xp ab000 00:00 0 boot.oat
+7b620b5000-7b6221b000 r--p 0 00:00 0 base.odex
+7b6221b000-7b6279f000 r-xp 166000 00:00 0 base.odex
+7bdd800000-7bdda00000 r--p 0 00:00 0 libart.so
+7bdda00000-7bddf4a000 r-xp 200000 00:00 0 libart.so
+7e86814000-7e86850000 r--p 0 00:00 0 libc.so
+7e86850000-7e868d1000 r-xp 3c000 00:00 0 libc.so
diff --git a/libunwindstack/offline_files/yt_music_arm64/output.txt b/libunwindstack/offline_files/yt_music_arm64/output.txt
new file mode 100644
index 0000000..0cba24a
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/output.txt
@@ -0,0 +1,18 @@
+ #00 pc 000000000004c35c libc.so (syscall+28)
+ #01 pc 0000000000411148 libart.so (art::Thread::Park(bool, long)+1088)
+ #02 pc 0000000000410790 libart.so (art::Unsafe_park(_JNIEnv*, _jobject*, unsigned char, long)+280)
+ #03 pc 00000000000adf4c boot.oat (art_jni_trampoline+108)
+ #04 pc 000000000023c924 boot.oat (java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await+756)
+ #05 pc 00000000003d966c boot.oat (java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take+524)
+ #06 pc 0000000000394ab8 boot.oat ([DEDUPED]+40)
+ #07 pc 0000000000377574 boot.oat (java.util.concurrent.ThreadPoolExecutor.getTask+484)
+ #08 pc 000000000037b46c boot.oat (java.util.concurrent.ThreadPoolExecutor.runWorker+236)
+ #09 pc 0000000000374f84 boot.oat (java.util.concurrent.ThreadPoolExecutor$Worker.run+68)
+ #10 pc 000000000059ec1c base.odex (lwo.run+92)
+ #11 pc 00000000001bf0bc boot.oat (java.lang.Thread.run+76)
+ #12 pc 0000000000293564 libart.so (art_quick_invoke_stub+548)
+ #13 pc 00000000002c6558 libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+156)
+ #14 pc 0000000000367ea8 libart.so (art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+380)
+ #15 pc 00000000003e9b6c libart.so (art::Thread::CreateCallback(void*)+1004)
+ #16 pc 00000000000b1920 libc.so (__pthread_start(void*)+264)
+ #17 pc 00000000000513f0 libc.so (__start_thread+64)
diff --git a/libunwindstack/offline_files/yt_music_arm64/regs.txt b/libunwindstack/offline_files/yt_music_arm64/regs.txt
new file mode 100644
index 0000000..9bbe3ce
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/regs.txt
@@ -0,0 +1,34 @@
+x0: 7d4f6e3cf8
+x1: 80
+x2: 2
+x3: 0
+x4: 0
+x5: 0
+x6: 0
+x7: 20
+x8: 62
+x9: 7bff6b7e40
+x10: 430000
+x11: 40
+x12: 341555ac
+x13: 18
+x14: f5c2b97f58
+x15: 12dd106c
+x16: 7bde011728
+x17: 7e86860340
+x18: 7b5e54e000
+x19: 7d4f6e3cf8
+x20: 7d4f6e3cc0
+x21: 47
+x22: 0
+x23: 7bde215000
+x24: 0
+x25: 1
+x26: 0
+x27: 7bde217000
+x28: 7b5e85c000
+x29: 7b5e85b500
+lr: 7bddc1114c
+sp: 7b5e85b4d0
+pc: 7e8686035c
+pst: 40000000
diff --git a/libunwindstack/offline_files/yt_music_arm64/stack.data b/libunwindstack/offline_files/yt_music_arm64/stack.data
new file mode 100644
index 0000000..06345b7
--- /dev/null
+++ b/libunwindstack/offline_files/yt_music_arm64/stack.data
Binary files differ
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/ArmExidxDecodeTest.cpp b/libunwindstack/tests/ArmExidxDecodeTest.cpp
index 69a7816..ea0dc0e 100644
--- a/libunwindstack/tests/ArmExidxDecodeTest.cpp
+++ b/libunwindstack/tests/ArmExidxDecodeTest.cpp
@@ -29,7 +29,7 @@
#include "ArmExidx.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/ArmExidxExtractTest.cpp b/libunwindstack/tests/ArmExidxExtractTest.cpp
index 79c799c..cfd5b7e 100644
--- a/libunwindstack/tests/ArmExidxExtractTest.cpp
+++ b/libunwindstack/tests/ArmExidxExtractTest.cpp
@@ -26,7 +26,7 @@
#include "ArmExidx.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/DexFileData.h b/libunwindstack/tests/DexFileData.h
index 6975c68..d4408d9 100644
--- a/libunwindstack/tests/DexFileData.h
+++ b/libunwindstack/tests/DexFileData.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_DEXFILESDATA_H
-#define _LIBUNWINDSTACK_DEXFILESDATA_H
+#pragma once
namespace unwindstack {
@@ -41,5 +40,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DEXFILESDATA_H
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 05b368b..96d99e3 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <unordered_map>
#include <MemoryLocal.h>
@@ -29,12 +30,12 @@
#include "DexFile.h"
#include "DexFileData.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
static constexpr size_t kNumLeakLoops = 5000;
-static constexpr size_t kMaxAllowedLeakBytes = 1024;
+static constexpr size_t kMaxAllowedLeakBytes = 4 * 1024;
static void CheckForLeak(size_t loop, size_t* first_allocated_bytes, size_t* last_allocated_bytes) {
size_t allocated_bytes = mallinfo().uordblks;
@@ -60,8 +61,8 @@
size_t last_allocated_bytes = 0;
for (size_t i = 0; i < kNumLeakLoops; i++) {
MemoryFake memory;
- MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
- EXPECT_TRUE(DexFile::Create(0, sizeof(kDexData), &memory, &info) != nullptr);
+ auto info = MapInfo::Create(0, 0x10000, 0, 0x5, tf.path);
+ EXPECT_TRUE(DexFile::Create(0, sizeof(kDexData), &memory, info.get()) != nullptr);
ASSERT_NO_FATAL_FAILURE(CheckForLeak(i, &first_allocated_bytes, &last_allocated_bytes));
}
}
@@ -88,8 +89,8 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(nullptr, nullptr, 0, 0x10000, 0, 0x5, tf.path);
- EXPECT_TRUE(DexFile::Create(0x500, sizeof(kDexData), &memory, &info) != nullptr);
+ auto info = MapInfo::Create(0, 0x10000, 0, 0x5, tf.path);
+ EXPECT_TRUE(DexFile::Create(0x500, sizeof(kDexData), &memory, info.get()) != nullptr);
}
TEST(DexFileTest, create_using_file_non_zero_start) {
@@ -101,8 +102,8 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0, 0x5, tf.path);
- EXPECT_TRUE(DexFile::Create(0x600, sizeof(kDexData), &memory, &info) != nullptr);
+ auto info = MapInfo::Create(0x100, 0x10000, 0, 0x5, tf.path);
+ EXPECT_TRUE(DexFile::Create(0x600, sizeof(kDexData), &memory, info.get()) != nullptr);
}
TEST(DexFileTest, create_using_file_non_zero_offset) {
@@ -114,22 +115,22 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, tf.path);
- EXPECT_TRUE(DexFile::Create(0x400, sizeof(kDexData), &memory, &info) != nullptr);
+ auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, tf.path);
+ EXPECT_TRUE(DexFile::Create(0x400, sizeof(kDexData), &memory, info.get()) != nullptr);
}
TEST(DexFileTest, create_using_memory_empty_file) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
- EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info) != nullptr);
+ auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
+ EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()) != nullptr);
}
TEST(DexFileTest, create_using_memory_file_does_not_exist) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
- EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info) != nullptr);
+ auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "/does/not/exist");
+ EXPECT_TRUE(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()) != nullptr);
}
TEST(DexFileTest, create_using_memory_file_is_malformed) {
@@ -141,13 +142,14 @@
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
- std::shared_ptr<DexFile> dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
+ auto info = MapInfo::Create(0x4000, 0x10000, 0x200, 0x5, "/does/not/exist");
+ std::shared_ptr<DexFile> dex_file =
+ DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
ASSERT_TRUE(dex_file != nullptr);
// Check it came from memory by clearing memory and verifying it fails.
memory.Clear();
- dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
+ dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
EXPECT_TRUE(dex_file == nullptr);
}
@@ -168,8 +170,8 @@
TEST(DexFileTest, get_method) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
- std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info));
+ auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
+ std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()));
ASSERT_TRUE(dex_file != nullptr);
SharedString method;
@@ -186,8 +188,8 @@
TEST(DexFileTest, get_method_empty) {
MemoryFake memory;
memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
- MapInfo info(nullptr, nullptr, 0x100, 0x10000, 0x200, 0x5, "");
- std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, &info));
+ auto info = MapInfo::Create(0x100, 0x10000, 0x200, 0x5, "");
+ std::shared_ptr<DexFile> dex_file(DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get()));
ASSERT_TRUE(dex_file != nullptr);
SharedString method;
@@ -205,8 +207,9 @@
static_cast<size_t>(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData)))));
MemoryFake memory;
- MapInfo info(nullptr, nullptr, 0x4000, 0x10000, 0, 0x5, tf.path);
- std::shared_ptr<DexFile> dex_file = DexFile::Create(0x4000, sizeof(kDexData), &memory, &info);
+ auto info = MapInfo::Create(0x4000, 0x10000, 0, 0x5, tf.path);
+ std::shared_ptr<DexFile> dex_file =
+ DexFile::Create(0x4000, sizeof(kDexData), &memory, info.get());
EXPECT_TRUE(dex_file != nullptr);
SharedString method;
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index c18a437..9250d6d 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -31,7 +31,7 @@
#include "DexFile.h"
#include "DexFileData.h"
#include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -72,22 +72,22 @@
ASSERT_TRUE(maps_->Parse());
// Global variable in a section that is not readable.
- MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
+ MapInfo* map_info = maps_->Get(kMapGlobalNonReadable).get();
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
// Global variable not set by default.
- map_info = maps_->Get(kMapGlobalSetToZero);
+ map_info = maps_->Get(kMapGlobalSetToZero).get();
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
// Global variable set in this map.
- map_info = maps_->Get(kMapGlobal);
+ map_info = maps_->Get(kMapGlobal).get();
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
// Global variable set in this map, but there is an empty map before rw map.
- map_info = maps_->Get(kMapGlobalAfterEmpty);
+ map_info = maps_->Get(kMapGlobalAfterEmpty).get();
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
}
@@ -278,7 +278,7 @@
EXPECT_EQ("nothing", method_name);
EXPECT_EQ(0x124U, method_offset);
- MapInfo* map_info = maps_->Get(kMapGlobal);
+ auto map_info = maps_->Get(kMapGlobal);
map_info->set_name("/system/lib/libart.so");
dex_files_ = CreateDexFiles(ARCH_ARM, process_memory_, libs);
// Set the rw map to the same name or this will not scan this entry.
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
index 7cb51ef..876f951 100644
--- a/libunwindstack/tests/DwarfCfaLogTest.cpp
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -32,7 +32,7 @@
#include "DwarfCfa.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 5ed428d..45728fb 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -32,7 +32,7 @@
#include "DwarfCfa.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -204,7 +204,7 @@
ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
ASSERT_EQ(0U, loc_regs.size());
- ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("6 unwind Invalid: restore while processing cie.\n", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
@@ -233,7 +233,7 @@
ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
ASSERT_EQ(0U, loc_regs.size());
- ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("6 unwind Invalid: restore while processing cie.\n", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
@@ -598,7 +598,7 @@
ASSERT_EQ(0U, loc_regs.size());
ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
- ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
+ ASSERT_EQ("6 unwind Attempt to set new register, but cfa is not already set to a register.\n",
GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
@@ -641,7 +641,7 @@
ASSERT_EQ(0U, loc_regs.size());
ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
- ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ ASSERT_EQ("6 unwind Attempt to set offset, but cfa is not set to a register.\n",
GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
@@ -683,7 +683,7 @@
ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->LastErrorCode());
- ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ ASSERT_EQ("6 unwind Attempt to set offset, but cfa is not set to a register.\n",
GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 235151b..47a00b4 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -26,7 +26,7 @@
#include "DwarfEncoding.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -195,6 +195,25 @@
ASSERT_EQ(3U, fdes.size());
}
+TYPED_TEST_P(DwarfDebugFrameTest, GetFdes32_big_function_address) {
+ SetCie32(&this->memory_, 0x5000, 0xfc, std::vector<uint8_t>{1, '\0', 0, 0, 1});
+ SetFde32(&this->memory_, 0x5100, 0xfc, 0, 0xe9ad9b1f, 0x200);
+ ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200, 0));
+
+ std::vector<const DwarfFde*> fdes;
+ this->debug_frame_->GetFdes(&fdes);
+
+ ASSERT_EQ(1U, fdes.size());
+
+ EXPECT_EQ(0x5000U, fdes[0]->cie_offset);
+ EXPECT_EQ(0x5110U, fdes[0]->cfa_instructions_offset);
+ EXPECT_EQ(0x5200U, fdes[0]->cfa_instructions_end);
+ EXPECT_EQ(0xe9ad9b1fU, fdes[0]->pc_start);
+ EXPECT_EQ(0xe9ad9d1fU, fdes[0]->pc_end);
+ EXPECT_EQ(0U, fdes[0]->lsda_address);
+ EXPECT_TRUE(fdes[0]->cie != nullptr);
+}
+
TYPED_TEST_P(DwarfDebugFrameTest, GetFdeFromPc32) {
SetFourFdes32(&this->memory_);
ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600, 0));
@@ -414,7 +433,7 @@
ASSERT_TRUE(fde->cie != nullptr);
EXPECT_EQ(1U, fde->cie->version);
- EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_udata4, fde->cie->fde_address_encoding);
EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
EXPECT_EQ(0U, fde->cie->segment_size);
EXPECT_EQ(1U, fde->cie->augmentation_string.size());
@@ -442,7 +461,7 @@
ASSERT_TRUE(fde->cie != nullptr);
EXPECT_EQ(1U, fde->cie->version);
- EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_udata8, fde->cie->fde_address_encoding);
EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
EXPECT_EQ(0U, fde->cie->segment_size);
EXPECT_EQ(1U, fde->cie->augmentation_string.size());
@@ -477,14 +496,14 @@
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata4, 0x20, 0xd, 0x104);
std::vector<uint8_t> zero(0x100, 0);
this->memory_.SetMemory(0x5000, zero);
cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata4, 0x20, 0xd, 0x104);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_cie_cached) {
@@ -492,14 +511,14 @@
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata8, 0x20, 0x19, 0x10c);
std::vector<uint8_t> zero(0x100, 0);
this->memory_.SetMemory(0x5000, zero);
cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata8, 0x20, 0x19, 0x10c);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version1) {
@@ -507,7 +526,7 @@
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata4, 0x20, 0xd, 0x104);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata4, 0x20, 0xd, 0x104);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version1) {
@@ -515,7 +534,7 @@
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 1, 0, DW_EH_PE_sdata8, 0x20, 0x19, 0x10c);
+ VerifyCieVersion(cie, 1, 0, DW_EH_PE_udata8, 0x20, 0x19, 0x10c);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version3) {
@@ -523,7 +542,7 @@
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata4, 0x181, 0xe, 0x104);
+ VerifyCieVersion(cie, 3, 0, DW_EH_PE_udata4, 0x181, 0xe, 0x104);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version3) {
@@ -531,39 +550,55 @@
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 3, 0, DW_EH_PE_sdata8, 0x181, 0x1a, 0x10c);
+ VerifyCieVersion(cie, 3, 0, DW_EH_PE_udata8, 0x181, 0x1a, 0x10c);
}
-TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4) {
- SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4_32bit_address) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 4, 10, 4, 8, 0x81, 3});
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+ VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata4, 0x181, 0x10, 0x104);
}
-TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4) {
- SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 0, 10, 4, 8, 0x81, 3});
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version4_64bit_address) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 8, 10, 4, 8, 0x81, 3});
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+ VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata8, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4_32bit_address) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 4, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata4, 0x181, 0x1c, 0x10c);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version4_64bit_address) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{4, '\0', 8, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 4, 10, DW_EH_PE_udata8, 0x181, 0x1c, 0x10c);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version5) {
- SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 4, 10, 4, 8, 0x81, 3});
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_udata4, 0x181, 0x10, 0x104);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
- SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 8, 10, 4, 8, 0x81, 3});
const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
ASSERT_TRUE(cie != nullptr);
- VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_udata8, 0x181, 0x1c, 0x10c);
}
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
@@ -639,7 +674,7 @@
SetCie32(&this->memory_, 0x5000, 0xfc,
std::vector<uint8_t>{/* version */ 4,
/* augment string */ 'z', '\0',
- /* address size */ 8,
+ /* address size */ 4,
/* segment size */ 0x10,
/* code alignment factor */ 16,
/* data alignment factor */ 32,
@@ -872,15 +907,18 @@
REGISTER_TYPED_TEST_SUITE_P(
DwarfDebugFrameTest, GetFdes32, GetFdes32_after_GetFdeFromPc, GetFdes32_not_in_section,
- GetFdeFromPc32, GetFdeFromPc32_reverse, GetFdeFromPc32_not_in_section, GetFdes64,
- GetFdes64_after_GetFdeFromPc, GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse,
- GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
- GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
- GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
- GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
- GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
- GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
- GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved, GetFdeFromPc_overlap);
+ GetFdes32_big_function_address, GetFdeFromPc32, GetFdeFromPc32_reverse,
+ GetFdeFromPc32_not_in_section, GetFdes64, GetFdes64_after_GetFdeFromPc,
+ GetFdes64_not_in_section, GetFdeFromPc64, GetFdeFromPc64_reverse, GetFdeFromPc64_not_in_section,
+ GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached, GetCieFromOffset64_cie_cached,
+ GetCieFromOffset32_version1, GetCieFromOffset64_version1, GetCieFromOffset32_version3,
+ GetCieFromOffset64_version3, GetCieFromOffset32_version4_32bit_address,
+ GetCieFromOffset32_version4_64bit_address, GetCieFromOffset64_version4_32bit_address,
+ GetCieFromOffset64_version4_64bit_address, GetCieFromOffset32_version5,
+ GetCieFromOffset64_version5, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
+ GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
+ GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved,
+ GetFdeFromPc_overlap);
typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 46a25a4..ea2e397 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -24,7 +24,7 @@
#include "DwarfEncoding.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -74,7 +74,7 @@
const DwarfCie* cie = fde->cie;
ASSERT_TRUE(cie != nullptr);
EXPECT_EQ(1U, cie->version);
- EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_udata4, cie->fde_address_encoding);
EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
EXPECT_EQ(0U, cie->segment_size);
EXPECT_EQ('\0', cie->augmentation_string[0]);
@@ -113,7 +113,7 @@
const DwarfCie* cie = fde->cie;
ASSERT_TRUE(cie != nullptr);
EXPECT_EQ(1U, cie->version);
- EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_udata8, cie->fde_address_encoding);
EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
EXPECT_EQ(0U, cie->segment_size);
EXPECT_EQ('\0', cie->augmentation_string[0]);
diff --git a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
index 6aa3867..87b477a 100644
--- a/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameWithHdrTest.cpp
@@ -25,7 +25,7 @@
#include "DwarfEncoding.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -479,7 +479,7 @@
ASSERT_TRUE(fde->cie != nullptr);
EXPECT_EQ(1U, fde->cie->version);
- EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_udata4, fde->cie->fde_address_encoding);
EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
EXPECT_EQ(0U, fde->cie->segment_size);
EXPECT_EQ(1U, fde->cie->augmentation_string.size());
@@ -517,7 +517,7 @@
ASSERT_TRUE(fde->cie != nullptr);
EXPECT_EQ(1U, fde->cie->version);
- EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding);
+ EXPECT_EQ(DW_EH_PE_udata8, fde->cie->fde_address_encoding);
EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding);
EXPECT_EQ(0U, fde->cie->segment_size);
EXPECT_EQ(1U, fde->cie->augmentation_string.size());
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 650e965..b3d04c4 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -23,7 +23,7 @@
#include <unwindstack/DwarfMemory.h>
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
index 8dbf6e8..f4dbf8d 100644
--- a/libunwindstack/tests/DwarfOpLogTest.cpp
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -28,7 +28,7 @@
#include "DwarfOp.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 942d074..eda9a58 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -27,8 +27,8 @@
#include "DwarfOp.h"
-#include "MemoryFake.h"
#include "RegsFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 9d047f2..a487bc0 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -25,49 +25,25 @@
#include "DwarfEncoding.h"
#include "LogFake.h"
-#include "MemoryFake.h"
-#include "RegsFake.h"
+#include "utils/DwarfSectionImplFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/RegsFake.h"
namespace unwindstack {
template <typename TypeParam>
-class TestDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
- public:
- TestDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
- virtual ~TestDwarfSectionImpl() = default;
-
- bool Init(uint64_t, uint64_t, int64_t) override { return false; }
-
- void GetFdes(std::vector<const DwarfFde*>*) override {}
-
- const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
-
- uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
-
- uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
-
- uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
-
- void TestSetCachedCieLocRegs(uint64_t offset, const DwarfLocations& loc_regs) {
- this->cie_loc_regs_[offset] = loc_regs;
- }
- void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
- void TestClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
-};
-
-template <typename TypeParam>
class DwarfSectionImplTest : public ::testing::Test {
protected:
void SetUp() override {
memory_.Clear();
- section_ = new TestDwarfSectionImpl<TypeParam>(&memory_);
+ section_ = new DwarfSectionImplFake<TypeParam>(&memory_);
ResetLogs();
}
void TearDown() override { delete section_; }
MemoryFake memory_;
- TestDwarfSectionImpl<TypeParam>* section_ = nullptr;
+ DwarfSectionImplFake<TypeParam>* section_ = nullptr;
};
TYPED_TEST_SUITE_P(DwarfSectionImplTest);
@@ -78,7 +54,7 @@
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
- this->section_->TestClearError();
+ this->section_->FakeClearError();
ASSERT_TRUE(this->section_->GetCieFromOffset(0x4000) == nullptr);
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
@@ -89,7 +65,7 @@
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
- this->section_->TestClearError();
+ this->section_->FakeClearError();
ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
EXPECT_EQ(0x4000U, this->section_->LastErrorAddress());
@@ -209,19 +185,19 @@
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
- this->section_->TestClearError();
+ this->section_->FakeClearError();
loc_regs.erase(CFA_REG);
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
- this->section_->TestClearError();
+ this->section_->FakeClearError();
loc_regs.erase(CFA_REG);
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
- this->section_->TestClearError();
+ this->section_->FakeClearError();
loc_regs.erase(CFA_REG);
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
@@ -566,7 +542,7 @@
DwarfLocations cie_loc_regs;
cie_loc_regs[6] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 0}};
- this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
+ this->section_->FakeSetCachedCieLocRegs(0x8000, cie_loc_regs);
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
DwarfLocations loc_regs;
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index e1ed885..55f1d3a 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -22,8 +22,8 @@
#include <unwindstack/DwarfSection.h>
#include <unwindstack/Elf.h>
-#include "MemoryFake.h"
#include "RegsFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/ElfCacheTest.cpp b/libunwindstack/tests/ElfCacheTest.cpp
index 65eef20..1687a4c 100644
--- a/libunwindstack/tests/ElfCacheTest.cpp
+++ b/libunwindstack/tests/ElfCacheTest.cpp
@@ -15,17 +15,24 @@
*/
#include <elf.h>
+#include <sys/mman.h>
#include <unistd.h>
+#include <memory>
+
#include <android-base/file.h>
#include <gtest/gtest.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include "ElfFake.h"
#include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
+
+#include <inttypes.h>
namespace unwindstack {
@@ -33,231 +40,337 @@
protected:
static void SetUpTestSuite() { memory_.reset(new MemoryFake); }
- void SetUp() override { Elf::SetCachingEnabled(true); }
+ void SetUp() override {
+ Elf::SetCachingEnabled(true);
- void TearDown() override { Elf::SetCachingEnabled(false); }
+ // Create maps for testing.
+ maps_.reset(
+ new BufferMaps("1000-2000 r-xs 00000000 00:00 0 elf_one.so\n"
+ "2000-3000 r-xs 00000000 00:00 0 elf_two.so\n"
+ "3000-4000 ---s 00000000 00:00 0\n"
+ "4000-5000 r--s 00000000 00:00 0 elf_three.so\n"
+ "5000-6000 r-xs 00001000 00:00 0 elf_three.so\n"
+ "6000-7000 ---s 00000000 00:00 0\n"
+ "7000-8000 r--s 00001000 00:00 0 app_one.apk\n"
+ "8000-9000 r-xs 00005000 00:00 0 app_one.apk\n"
+ "9000-a000 r--s 00004000 00:00 0 app_two.apk\n"
+ "a000-b000 r-xs 00005000 00:00 0 app_two.apk\n"
+ "b000-c000 r--s 00008000 00:00 0 app_two.apk\n"
+ "c000-d000 r-xs 00009000 00:00 0 app_two.apk\n"
+ "d000-e000 ---s 00000000 00:00 0\n"
+ "e000-f000 r-xs 00000000 00:00 0 invalid\n"
+ "f000-10000 r-xs 00000000 00:00 0 invalid\n"
+ "10000-11000 r-xs 00000000 00:00 0 elf_two.so\n"
+ "11000-12000 r-xs 00000000 00:00 0 elf_one.so\n"
+ "12000-13000 r--s 00000000 00:00 0 elf_three.so\n"
+ "13000-14000 r-xs 00001000 00:00 0 elf_three.so\n"
+ "14000-15000 ---s 00000000 00:00 0\n"
+ "15000-16000 r--s 00001000 00:00 0 app_one.apk\n"
+ "16000-17000 r-xs 00005000 00:00 0 app_one.apk\n"
+ "17000-18000 r--s 00004000 00:00 0 app_two.apk\n"
+ "18000-19000 r-xs 00005000 00:00 0 app_two.apk\n"
+ "19000-1a000 r--s 00008000 00:00 0 app_two.apk\n"
+ "1a000-1b000 r-xs 00009000 00:00 0 app_two.apk\n"));
+ ASSERT_TRUE(maps_->Parse());
- void WriteElfFile(uint64_t offset, TemporaryFile* tf, uint32_t type) {
- ASSERT_TRUE(type == EM_ARM || type == EM_386 || type == EM_X86_64);
- size_t ehdr_size;
- Elf32_Ehdr ehdr32;
- Elf64_Ehdr ehdr64;
- void* ptr;
- if (type == EM_ARM || type == EM_386) {
- ehdr_size = sizeof(ehdr32);
- ptr = &ehdr32;
- TestInitEhdr(&ehdr32, ELFCLASS32, type);
- } else {
- ehdr_size = sizeof(ehdr64);
- ptr = &ehdr64;
- TestInitEhdr(&ehdr64, ELFCLASS64, type);
+ std::unordered_map<std::string, std::string> renames;
+
+ temps_.emplace_back(new TemporaryFile);
+ renames["elf_one.so"] = temps_.back()->path;
+ WriteElfFile(0, temps_.back().get());
+
+ temps_.emplace_back(new TemporaryFile);
+ renames["elf_two.so"] = temps_.back()->path;
+ WriteElfFile(0, temps_.back().get());
+
+ temps_.emplace_back(new TemporaryFile);
+ renames["elf_three.so"] = temps_.back()->path;
+ WriteElfFile(0, temps_.back().get());
+
+ temps_.emplace_back(new TemporaryFile);
+ renames["app_one.apk"] = temps_.back()->path;
+ WriteElfFile(0x1000, temps_.back().get());
+ WriteElfFile(0x5000, temps_.back().get());
+
+ temps_.emplace_back(new TemporaryFile);
+ renames["app_two.apk"] = temps_.back()->path;
+ WriteElfFile(0x4000, temps_.back().get());
+ WriteElfFile(0x8000, temps_.back().get());
+
+ for (auto& map_info : *maps_) {
+ if (!map_info->name().empty()) {
+ if (renames.count(map_info->name()) != 0) {
+ // Replace the name with the temporary file name.
+ map_info->name() = renames.at(map_info->name());
+ }
+ }
}
-
- ASSERT_EQ(offset, static_cast<uint64_t>(lseek(tf->fd, offset, SEEK_SET)));
- ASSERT_TRUE(android::base::WriteFully(tf->fd, ptr, ehdr_size));
}
- void VerifyWithinSameMap(bool cache_enabled);
- void VerifySameMap(bool cache_enabled);
- void VerifyWithinSameMapNeverReadAtZero(bool cache_enabled);
+ // Make sure the cache is cleared between runs.
+ void TearDown() override { Elf::SetCachingEnabled(false); }
+ void WriteElfFile(uint64_t offset, TemporaryFile* tf) {
+ Elf32_Ehdr ehdr;
+ TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM);
+ Elf32_Shdr shdr = {};
+ shdr.sh_type = SHT_NULL;
+
+ ehdr.e_shnum = 1;
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(shdr);
+
+ ASSERT_EQ(offset, static_cast<uint64_t>(lseek(tf->fd, offset, SEEK_SET)));
+ ASSERT_TRUE(android::base::WriteFully(tf->fd, &ehdr, sizeof(ehdr)));
+ ASSERT_EQ(offset + 0x2000, static_cast<uint64_t>(lseek(tf->fd, offset + 0x2000, SEEK_SET)));
+ ASSERT_TRUE(android::base::WriteFully(tf->fd, &shdr, sizeof(shdr)));
+ }
+
+ std::vector<std::unique_ptr<TemporaryFile>> temps_;
+ std::unique_ptr<Maps> maps_;
static std::shared_ptr<Memory> memory_;
};
std::shared_ptr<Memory> ElfCacheTest::memory_;
-void ElfCacheTest::VerifySameMap(bool cache_enabled) {
- if (!cache_enabled) {
- Elf::SetCachingEnabled(false);
- }
+TEST_F(ElfCacheTest, verify_elf_caching) {
+ Elf* elf_one = maps_->Find(0x1000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_one->valid());
+ Elf* elf_two = maps_->Find(0x2000)->GetElf(memory_, ARCH_ARM);
+ EXPECT_TRUE(elf_two->valid());
+ Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_three->valid());
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- WriteElfFile(0, &tf, EM_ARM);
- close(tf.fd);
+ // Check that the caching is working for elf files.
+ EXPECT_EQ(maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_EQ(0U, maps_->Find(0x5000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x5000)->elf_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x5000)->offset());
- uint64_t start = 0x1000;
- uint64_t end = 0x20000;
- MapInfo info1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
- MapInfo info2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
+ EXPECT_EQ(maps_->Find(0x10000)->GetElf(memory_, ARCH_ARM), elf_two);
+ EXPECT_EQ(0U, maps_->Find(0x10000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x10000)->elf_offset());
+ EXPECT_EQ(0U, maps_->Find(0x10000)->offset());
- Elf* elf1 = info1.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf1->valid());
- Elf* elf2 = info2.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf2->valid());
+ EXPECT_EQ(maps_->Find(0x11000)->GetElf(memory_, ARCH_ARM), elf_one);
+ EXPECT_EQ(0U, maps_->Find(0x11000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x11000)->elf_offset());
+ EXPECT_EQ(0U, maps_->Find(0x11000)->offset());
- if (cache_enabled) {
- EXPECT_EQ(elf1, elf2);
- } else {
- EXPECT_NE(elf1, elf2);
- }
+ EXPECT_EQ(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_EQ(0U, maps_->Find(0x12000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x12000)->elf_offset());
+ EXPECT_EQ(0U, maps_->Find(0x12000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_EQ(0U, maps_->Find(0x13000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x13000)->elf_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x13000)->offset());
}
-TEST_F(ElfCacheTest, no_caching) {
- VerifySameMap(false);
+TEST_F(ElfCacheTest, verify_elf_caching_ro_first_ro_second) {
+ Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_three->valid());
+
+ EXPECT_EQ(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_EQ(0U, maps_->Find(0x12000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x12000)->elf_offset());
+ EXPECT_EQ(0U, maps_->Find(0x12000)->offset());
}
-TEST_F(ElfCacheTest, caching_invalid_elf) {
- VerifySameMap(true);
+TEST_F(ElfCacheTest, verify_elf_caching_ro_first_rx_second) {
+ Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_three->valid());
+
+ EXPECT_EQ(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_EQ(0U, maps_->Find(0x13000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x13000)->elf_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x13000)->offset());
}
-void ElfCacheTest::VerifyWithinSameMap(bool cache_enabled) {
- if (!cache_enabled) {
- Elf::SetCachingEnabled(false);
- }
+TEST_F(ElfCacheTest, verify_elf_caching_rx_first_ro_second) {
+ Elf* elf_three = maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_three->valid());
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- WriteElfFile(0, &tf, EM_ARM);
- WriteElfFile(0x100, &tf, EM_386);
- WriteElfFile(0x200, &tf, EM_X86_64);
- lseek(tf.fd, 0x500, SEEK_SET);
- uint8_t value = 0;
- write(tf.fd, &value, 1);
- close(tf.fd);
-
- uint64_t start = 0x1000;
- uint64_t end = 0x20000;
- // Will have an elf at offset 0 in file.
- MapInfo info0_1(nullptr, nullptr, start, end, 0, 0x5, tf.path);
- MapInfo info0_2(nullptr, nullptr, start, end, 0, 0x5, tf.path);
- // Will have an elf at offset 0x100 in file.
- MapInfo info100_1(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
- MapInfo info100_2(nullptr, nullptr, start, end, 0x100, 0x5, tf.path);
- // Will have an elf at offset 0x200 in file.
- MapInfo info200_1(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
- MapInfo info200_2(nullptr, nullptr, start, end, 0x200, 0x5, tf.path);
- // Will have an elf at offset 0 in file.
- MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
-
- Elf* elf0_1 = info0_1.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf0_1->valid());
- EXPECT_EQ(ARCH_ARM, elf0_1->arch());
- Elf* elf0_2 = info0_2.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf0_2->valid());
- EXPECT_EQ(ARCH_ARM, elf0_2->arch());
- EXPECT_EQ(0U, info0_1.elf_offset());
- EXPECT_EQ(0U, info0_2.elf_offset());
- if (cache_enabled) {
- EXPECT_EQ(elf0_1, elf0_2);
- } else {
- EXPECT_NE(elf0_1, elf0_2);
- }
-
- Elf* elf100_1 = info100_1.GetElf(memory_, ARCH_X86);
- ASSERT_TRUE(elf100_1->valid());
- EXPECT_EQ(ARCH_X86, elf100_1->arch());
- Elf* elf100_2 = info100_2.GetElf(memory_, ARCH_X86);
- ASSERT_TRUE(elf100_2->valid());
- EXPECT_EQ(ARCH_X86, elf100_2->arch());
- EXPECT_EQ(0U, info100_1.elf_offset());
- EXPECT_EQ(0U, info100_2.elf_offset());
- if (cache_enabled) {
- EXPECT_EQ(elf100_1, elf100_2);
- } else {
- EXPECT_NE(elf100_1, elf100_2);
- }
-
- Elf* elf200_1 = info200_1.GetElf(memory_, ARCH_X86_64);
- ASSERT_TRUE(elf200_1->valid());
- EXPECT_EQ(ARCH_X86_64, elf200_1->arch());
- Elf* elf200_2 = info200_2.GetElf(memory_, ARCH_X86_64);
- ASSERT_TRUE(elf200_2->valid());
- EXPECT_EQ(ARCH_X86_64, elf200_2->arch());
- EXPECT_EQ(0U, info200_1.elf_offset());
- EXPECT_EQ(0U, info200_2.elf_offset());
- if (cache_enabled) {
- EXPECT_EQ(elf200_1, elf200_2);
- } else {
- EXPECT_NE(elf200_1, elf200_2);
- }
-
- Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf300_1->valid());
- EXPECT_EQ(ARCH_ARM, elf300_1->arch());
- Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf300_2->valid());
- EXPECT_EQ(ARCH_ARM, elf300_2->arch());
- EXPECT_EQ(0x300U, info300_1.elf_offset());
- EXPECT_EQ(0x300U, info300_2.elf_offset());
- if (cache_enabled) {
- EXPECT_EQ(elf300_1, elf300_2);
- EXPECT_EQ(elf0_1, elf300_1);
- } else {
- EXPECT_NE(elf300_1, elf300_2);
- EXPECT_NE(elf0_1, elf300_1);
- }
+ EXPECT_EQ(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_EQ(0U, maps_->Find(0x12000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x12000)->elf_offset());
+ EXPECT_EQ(0U, maps_->Find(0x12000)->offset());
}
-TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero) {
- VerifyWithinSameMap(false);
+TEST_F(ElfCacheTest, verify_elf_caching_rx_first_rx_second) {
+ Elf* elf_three = maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_three->valid());
+
+ EXPECT_EQ(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_EQ(0U, maps_->Find(0x13000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x13000)->elf_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x13000)->offset());
}
-TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero) {
- VerifyWithinSameMap(true);
+TEST_F(ElfCacheTest, verify_elf_apk_caching) {
+ Elf* app_one_elf1 = maps_->Find(0x7000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_one_elf1->valid());
+ Elf* app_one_elf2 = maps_->Find(0x8000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_one_elf2->valid());
+ Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf1->valid());
+ Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf2->valid());
+
+ // Check that the caching is working for elf files in apks.
+ EXPECT_EQ(maps_->Find(0xa000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_EQ(0x4000U, maps_->Find(0xa000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0xa000)->elf_offset());
+ EXPECT_EQ(0x5000U, maps_->Find(0xa000)->offset());
+
+ EXPECT_EQ(maps_->Find(0xc000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_EQ(0x8000U, maps_->Find(0xc000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0xc000)->elf_offset());
+ EXPECT_EQ(0x9000U, maps_->Find(0xc000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x15000)->GetElf(memory_, ARCH_ARM), app_one_elf1);
+ EXPECT_EQ(0x1000U, maps_->Find(0x15000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x15000)->elf_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x15000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x16000)->GetElf(memory_, ARCH_ARM), app_one_elf2);
+ EXPECT_EQ(0x1000U, maps_->Find(0x16000)->elf_start_offset());
+ EXPECT_EQ(0x4000U, maps_->Find(0x16000)->elf_offset());
+ EXPECT_EQ(0x5000U, maps_->Find(0x16000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x18000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_EQ(0x4000U, maps_->Find(0x18000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x18000)->elf_offset());
+ EXPECT_EQ(0x5000U, maps_->Find(0x18000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x1a000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_EQ(0x8000U, maps_->Find(0x1a000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x1a000)->elf_offset());
+ EXPECT_EQ(0x9000U, maps_->Find(0x1a000)->offset());
}
-// Verify that when reading from multiple non-zero offsets in the same map
-// that when cached, all of the elf objects are the same.
-void ElfCacheTest::VerifyWithinSameMapNeverReadAtZero(bool cache_enabled) {
- if (!cache_enabled) {
- Elf::SetCachingEnabled(false);
- }
+TEST_F(ElfCacheTest, verify_elf_apk_caching_ro_first_ro_second) {
+ Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf1->valid());
+ Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf2->valid());
- TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
- WriteElfFile(0, &tf, EM_ARM);
- lseek(tf.fd, 0x500, SEEK_SET);
- uint8_t value = 0;
- write(tf.fd, &value, 1);
- close(tf.fd);
+ EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
- uint64_t start = 0x1000;
- uint64_t end = 0x20000;
- // Multiple info sections at different offsets will have non-zero elf offsets.
- MapInfo info300_1(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
- MapInfo info300_2(nullptr, nullptr, start, end, 0x300, 0x5, tf.path);
- MapInfo info400_1(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
- MapInfo info400_2(nullptr, nullptr, start, end, 0x400, 0x5, tf.path);
-
- Elf* elf300_1 = info300_1.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf300_1->valid());
- EXPECT_EQ(ARCH_ARM, elf300_1->arch());
- Elf* elf300_2 = info300_2.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf300_2->valid());
- EXPECT_EQ(ARCH_ARM, elf300_2->arch());
- EXPECT_EQ(0x300U, info300_1.elf_offset());
- EXPECT_EQ(0x300U, info300_2.elf_offset());
- if (cache_enabled) {
- EXPECT_EQ(elf300_1, elf300_2);
- } else {
- EXPECT_NE(elf300_1, elf300_2);
- }
-
- Elf* elf400_1 = info400_1.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf400_1->valid());
- EXPECT_EQ(ARCH_ARM, elf400_1->arch());
- Elf* elf400_2 = info400_2.GetElf(memory_, ARCH_ARM);
- ASSERT_TRUE(elf400_2->valid());
- EXPECT_EQ(ARCH_ARM, elf400_2->arch());
- EXPECT_EQ(0x400U, info400_1.elf_offset());
- EXPECT_EQ(0x400U, info400_2.elf_offset());
- if (cache_enabled) {
- EXPECT_EQ(elf400_1, elf400_2);
- EXPECT_EQ(elf300_1, elf400_1);
- } else {
- EXPECT_NE(elf400_1, elf400_2);
- EXPECT_NE(elf300_1, elf400_1);
- }
+ EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
}
-TEST_F(ElfCacheTest, no_caching_valid_elf_offset_non_zero_never_read_at_zero) {
- VerifyWithinSameMapNeverReadAtZero(false);
+TEST_F(ElfCacheTest, verify_elf_apk_caching_ro_first_rx_second) {
+ Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf1->valid());
+ Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf2->valid());
+
+ EXPECT_EQ(maps_->Find(0x18000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_EQ(0x4000U, maps_->Find(0x18000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x18000)->elf_offset());
+ EXPECT_EQ(0x5000U, maps_->Find(0x18000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x1a000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_EQ(0x8000U, maps_->Find(0x1a000)->elf_start_offset());
+ EXPECT_EQ(0x1000U, maps_->Find(0x1a000)->elf_offset());
+ EXPECT_EQ(0x9000U, maps_->Find(0x1a000)->offset());
}
-TEST_F(ElfCacheTest, caching_valid_elf_offset_non_zero_never_read_at_zero) {
- VerifyWithinSameMapNeverReadAtZero(true);
+TEST_F(ElfCacheTest, verify_elf_apk_caching_rx_first_ro_second) {
+ Elf* app_two_elf1 = maps_->Find(0xa000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf1->valid());
+ Elf* app_two_elf2 = maps_->Find(0xc000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf2->valid());
+
+ EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
+}
+
+TEST_F(ElfCacheTest, verify_elf_apk_caching_rx_first_rx_second) {
+ Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf1->valid());
+ Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf2->valid());
+
+ EXPECT_EQ(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x17000)->elf_offset());
+ EXPECT_EQ(0x4000U, maps_->Find(0x17000)->offset());
+
+ EXPECT_EQ(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->elf_start_offset());
+ EXPECT_EQ(0U, maps_->Find(0x19000)->elf_offset());
+ EXPECT_EQ(0x8000U, maps_->Find(0x19000)->offset());
+}
+
+// Verify that with elf caching disabled, we aren't caching improperly.
+TEST_F(ElfCacheTest, verify_disable_elf_caching) {
+ Elf::SetCachingEnabled(false);
+
+ Elf* elf_one = maps_->Find(0x1000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_one->valid());
+ Elf* elf_two = maps_->Find(0x2000)->GetElf(memory_, ARCH_ARM);
+ EXPECT_TRUE(elf_two->valid());
+ Elf* elf_three = maps_->Find(0x4000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(elf_three->valid());
+ EXPECT_EQ(maps_->Find(0x5000)->GetElf(memory_, ARCH_ARM), elf_three);
+
+ EXPECT_NE(maps_->Find(0x10000)->GetElf(memory_, ARCH_ARM), elf_two);
+ EXPECT_NE(maps_->Find(0x11000)->GetElf(memory_, ARCH_ARM), elf_one);
+ EXPECT_NE(maps_->Find(0x12000)->GetElf(memory_, ARCH_ARM), elf_three);
+ EXPECT_NE(maps_->Find(0x13000)->GetElf(memory_, ARCH_ARM), elf_three);
+
+ Elf* app_one_elf1 = maps_->Find(0x7000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_one_elf1->valid());
+ Elf* app_one_elf2 = maps_->Find(0x8000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_one_elf2->valid());
+ Elf* app_two_elf1 = maps_->Find(0x9000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf1->valid());
+ EXPECT_EQ(maps_->Find(0xa000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ Elf* app_two_elf2 = maps_->Find(0xb000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_TRUE(app_two_elf2->valid());
+ EXPECT_EQ(maps_->Find(0xc000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+
+ EXPECT_NE(maps_->Find(0x15000)->GetElf(memory_, ARCH_ARM), app_one_elf1);
+ EXPECT_NE(maps_->Find(0x16000)->GetElf(memory_, ARCH_ARM), app_one_elf2);
+ EXPECT_NE(maps_->Find(0x17000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_NE(maps_->Find(0x18000)->GetElf(memory_, ARCH_ARM), app_two_elf1);
+ EXPECT_NE(maps_->Find(0x19000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+ EXPECT_NE(maps_->Find(0x1a000)->GetElf(memory_, ARCH_ARM), app_two_elf2);
+}
+
+// Verify that invalid elf objects are not cached.
+TEST_F(ElfCacheTest, verify_invalid_not_cached) {
+ Elf* invalid_elf1 = maps_->Find(0xe000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_FALSE(invalid_elf1->valid());
+ Elf* invalid_elf2 = maps_->Find(0xf000)->GetElf(memory_, ARCH_ARM);
+ ASSERT_FALSE(invalid_elf2->valid());
+ ASSERT_NE(invalid_elf1, invalid_elf2);
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 80c3e31..3da673e 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -26,7 +26,7 @@
#include <unwindstack/SharedString.h>
#include "ElfFake.h"
-#include "RegsFake.h"
+#include "utils/RegsFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index 8c5b9ef..97d6dcc 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+#pragma once
#include <stdint.h>
@@ -152,5 +151,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index 43c6a97..ee70cc0 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -26,7 +26,7 @@
#include "ElfInterfaceArm.h"
#include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index d250d0a..47ae3d0 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -26,7 +26,7 @@
#include "ElfInterfaceArm.h"
#include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
#if !defined(PT_ARM_EXIDX)
#define PT_ARM_EXIDX 0x70000001
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index c641f52..f16c6e5 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -31,7 +31,7 @@
#include "ElfFake.h"
#include "ElfTestUtils.h"
#include "LogFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
#if !defined(PT_ARM_EXIDX)
#define PT_ARM_EXIDX 0x70000001
@@ -172,8 +172,7 @@
ASSERT_FALSE(elf.Init());
ASSERT_EQ("", GetFakeLogBuf());
- ASSERT_EQ("4 unwind 32 bit elf that is neither arm nor x86 nor mips: e_machine = 20\n\n",
- GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogPrint());
}
TEST_F(ElfTest, elf64_invalid_machine) {
@@ -185,8 +184,7 @@
ASSERT_FALSE(elf.Init());
ASSERT_EQ("", GetFakeLogBuf());
- ASSERT_EQ("4 unwind 64 bit elf that is neither aarch64 nor x86_64 nor mips64: e_machine = 21\n\n",
- GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogPrint());
}
TEST_F(ElfTest, elf_arm) {
@@ -296,12 +294,12 @@
elf.FakeSetInterface(interface);
elf.FakeSetValid(true);
- MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
+ auto map_info = MapInfo::Create(0x1000, 0x2000, 0, 0, "");
- ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, map_info.get()));
elf.FakeSetValid(false);
- ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, &map_info));
+ ASSERT_EQ(0x101U, elf.GetRelPc(0x1101, map_info.get()));
}
void ElfTest::VerifyStepIfSignalHandler(uint64_t load_bias) {
@@ -553,4 +551,14 @@
EXPECT_EQ(0x1000U, elf.GetLastErrorAddress());
}
+TEST(ElfBuildIdTest, get_printable_build_id_empty) {
+ std::string empty;
+ ASSERT_EQ("", Elf::GetPrintableBuildID(empty));
+}
+
+TEST(ElfBuildIdTest, get_printable_build_id_check) {
+ std::string empty = {'\xff', '\x45', '\x40', '\x0f'};
+ ASSERT_EQ("ff45400f", Elf::GetPrintableBuildID(empty));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
index 62cd59a..25f0052 100644
--- a/libunwindstack/tests/ElfTestUtils.h
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
-#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+#pragma once
#include <functional>
#include <string>
@@ -34,5 +33,3 @@
std::string TestGetFileDirectory();
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/GlobalTest.cpp b/libunwindstack/tests/GlobalTest.cpp
new file mode 100644
index 0000000..b6893ba
--- /dev/null
+++ b/libunwindstack/tests/GlobalTest.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2021 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 <stdint.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Global.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+
+#include "ElfFake.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+namespace unwindstack {
+
+class GlobalMock : public Global {
+ public:
+ explicit GlobalMock(std::shared_ptr<Memory>& memory) : Global(memory) {}
+ GlobalMock(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+ : Global(memory, search_libs) {}
+
+ MOCK_METHOD(bool, ReadVariableData, (uint64_t), (override));
+
+ MOCK_METHOD(void, ProcessArch, (), (override));
+
+ void TestFindAndReadVariable(Maps* maps, const char* var_str) {
+ FindAndReadVariable(maps, var_str);
+ }
+};
+
+class GlobalTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ maps_.reset(
+ new BufferMaps("10000-11000 r--p 0000 00:00 0 first.so\n"
+ "11000-12000 r-xp 1000 00:00 0 first.so\n"
+ "12000-13000 rw-p 2000 00:00 0 first.so\n"
+
+ "20000-22000 r--p 0000 00:00 0 second.so\n"
+ "22000-23000 rw-p 2000 00:00 0 second.so\n"
+
+ "30000-31000 r--p 0000 00:00 0 third.so\n"
+ "31000-32000 ---p 0000 00:00 0\n"
+ "32000-33000 r-xp 1000 00:00 0 third.so\n"
+ "33000-34000 rw-p 2000 00:00 0 third.so\n"
+
+ "40000-42000 r--p 0000 00:00 0 fourth.so\n"
+ "42000-43000 rw-p 0000 00:00 0 fourth.so\n"));
+ ASSERT_TRUE(maps_->Parse());
+ ASSERT_EQ(11U, maps_->Total());
+
+ elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+ elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+ elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+ elf_fakes_.push_back(new ElfInterfaceFake(nullptr));
+
+ ElfFake* elf_fake = new ElfFake(nullptr);
+ elf_fake->FakeSetValid(true);
+ elf_fake->FakeSetInterface(elf_fakes_[0]);
+ elf_fakes_[0]->FakeSetDataVaddrStart(0x2000);
+ elf_fakes_[0]->FakeSetDataVaddrEnd(0x3000);
+ elf_fakes_[0]->FakeSetDataOffset(0x2000);
+ auto map_info = maps_->Find(0x10000);
+ map_info->GetElfFields().elf_.reset(elf_fake);
+
+ elf_fake = new ElfFake(nullptr);
+ elf_fake->FakeSetValid(true);
+ elf_fake->FakeSetInterface(elf_fakes_[1]);
+ elf_fakes_[1]->FakeSetDataVaddrStart(0x2000);
+ elf_fakes_[1]->FakeSetDataVaddrEnd(0x3000);
+ elf_fakes_[1]->FakeSetDataOffset(0x2000);
+ map_info = maps_->Find(0x20000);
+ map_info->GetElfFields().elf_.reset(elf_fake);
+
+ elf_fake = new ElfFake(nullptr);
+ elf_fake->FakeSetValid(true);
+ elf_fake->FakeSetInterface(elf_fakes_[2]);
+ elf_fakes_[2]->FakeSetDataVaddrStart(0x2000);
+ elf_fakes_[2]->FakeSetDataVaddrEnd(0x3000);
+ elf_fakes_[2]->FakeSetDataOffset(0x2000);
+ map_info = maps_->Find(0x30000);
+ map_info->GetElfFields().elf_.reset(elf_fake);
+
+ elf_fake = new ElfFake(nullptr);
+ elf_fake->FakeSetValid(true);
+ elf_fake->FakeSetInterface(elf_fakes_[3]);
+ elf_fakes_[3]->FakeSetDataVaddrStart(00);
+ elf_fakes_[3]->FakeSetDataVaddrEnd(0x1000);
+ elf_fakes_[3]->FakeSetDataOffset(0);
+ map_info = maps_->Find(0x40000);
+ map_info->GetElfFields().elf_.reset(elf_fake);
+
+ global_.reset(new GlobalMock(empty_));
+ }
+
+ std::shared_ptr<Memory> empty_;
+ std::unique_ptr<BufferMaps> maps_;
+ std::unique_ptr<GlobalMock> global_;
+ std::vector<ElfInterfaceFake*> elf_fakes_;
+};
+
+TEST_F(GlobalTest, ro_rx_rw) {
+ std::string global_var("fake_global");
+ elf_fakes_[0]->FakeSetGlobalVariable(global_var, 0x2010);
+ EXPECT_CALL(*global_, ReadVariableData(0x12010)).WillOnce(Return(true));
+
+ global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rx_rw_searchable) {
+ std::vector<std::string> search_libs = {"first.so"};
+ global_.reset(new GlobalMock(empty_, search_libs));
+
+ std::string global_var("fake_global");
+ elf_fakes_[0]->FakeSetGlobalVariable(global_var, 0x2010);
+ EXPECT_CALL(*global_, ReadVariableData(0x12010)).WillOnce(Return(true));
+
+ global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rx_rw_not_searchable) {
+ std::vector<std::string> search_libs = {"second.so"};
+ global_.reset(new GlobalMock(empty_, search_libs));
+
+ std::string global_var("fake_global");
+ elf_fakes_[0]->FakeSetGlobalVariable(global_var, 0x2010);
+
+ global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rw) {
+ std::string global_var("fake_global");
+ elf_fakes_[1]->FakeSetGlobalVariable(global_var, 0x2010);
+ EXPECT_CALL(*global_, ReadVariableData(0x22010)).WillOnce(Return(true));
+
+ global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_blank_rx_rw) {
+ std::string global_var("fake_global");
+ elf_fakes_[2]->FakeSetGlobalVariable(global_var, 0x2010);
+ EXPECT_CALL(*global_, ReadVariableData(0x33010)).WillOnce(Return(true));
+
+ global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+TEST_F(GlobalTest, ro_rw_with_zero_offset) {
+ std::string global_var("fake_global");
+ elf_fakes_[3]->FakeSetGlobalVariable(global_var, 0x10);
+ EXPECT_CALL(*global_, ReadVariableData(0x42010)).WillOnce(Return(true));
+
+ global_->TestFindAndReadVariable(maps_.get(), global_var.c_str());
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index 44378a8..66e220c 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -29,7 +29,7 @@
#include <unwindstack/Memory.h>
#include "ElfFake.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -65,15 +65,15 @@
"200000-210000 rw-p 01ee000 00:00 0 /fake/elf4\n"));
ASSERT_TRUE(maps_->Parse());
- MapInfo* map_info = maps_->Get(3);
+ MapInfo* map_info = maps_->Get(3).get();
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
- map_info = maps_->Get(5);
+ map_info = maps_->Get(5).get();
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
- map_info = maps_->Get(7);
+ map_info = maps_->Get(7).get();
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info, 0xee800, 0xee000, 0xee000, 0x10000);
}
@@ -414,7 +414,7 @@
EXPECT_TRUE(jit_debug_->Find(maps_.get(), 0x1500) == nullptr);
// Change the name of the map that includes the value and verify this works.
- MapInfo* map_info = maps_->Get(5);
+ auto map_info = maps_->Get(5);
map_info->set_name("/system/lib/libart.so");
map_info = maps_->Get(6);
map_info->set_name("/system/lib/libart.so");
diff --git a/libunwindstack/tests/LocalUnwinderTest.cpp b/libunwindstack/tests/LocalUnwinderTest.cpp
deleted file mode 100644
index 5658aa3..0000000
--- a/libunwindstack/tests/LocalUnwinderTest.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dlfcn.h>
-#include <inttypes.h>
-#include <signal.h>
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <android-base/stringprintf.h>
-
-#include <unwindstack/LocalUnwinder.h>
-
-namespace unwindstack {
-
-static std::vector<LocalFrameData>* g_frame_info;
-static LocalUnwinder* g_unwinder;
-
-extern "C" void SignalLocalInnerFunction() {
- g_unwinder->Unwind(g_frame_info, 256);
-}
-
-extern "C" void SignalLocalMiddleFunction() {
- SignalLocalInnerFunction();
-}
-
-extern "C" void SignalLocalOuterFunction() {
- SignalLocalMiddleFunction();
-}
-
-static void SignalLocalCallerHandler(int, siginfo_t*, void*) {
- SignalLocalOuterFunction();
-}
-
-static std::string ErrorMsg(const std::vector<const char*>& function_names,
- const std::vector<LocalFrameData>& frame_info) {
- std::string unwind;
- size_t i = 0;
- for (const auto& frame : frame_info) {
- unwind += android::base::StringPrintf("#%02zu pc 0x%" PRIx64 " rel_pc 0x%" PRIx64, i++,
- frame.pc, frame.rel_pc);
- if (frame.map_info != nullptr) {
- if (!frame.map_info->name().empty()) {
- unwind += " ";
- unwind += frame.map_info->name();
- } else {
- unwind += android::base::StringPrintf(" 0x%" PRIx64 "-0x%" PRIx64, frame.map_info->start(),
- frame.map_info->end());
- }
- if (frame.map_info->offset() != 0) {
- unwind += android::base::StringPrintf(" offset 0x%" PRIx64, frame.map_info->offset());
- }
- }
- if (!frame.function_name.empty()) {
- unwind += " " + frame.function_name;
- if (frame.function_offset != 0) {
- unwind += android::base::StringPrintf("+%" PRId64, frame.function_offset);
- }
- }
- unwind += '\n';
- }
-
- return std::string(
- "Unwind completed without finding all frames\n"
- " Looking for function: ") +
- function_names.front() + "\n" + "Unwind data:\n" + unwind;
-}
-
-// This test assumes that this code is compiled with optimizations turned
-// off. If this doesn't happen, then all of the calls will be optimized
-// away.
-extern "C" void LocalInnerFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
- std::vector<LocalFrameData> frame_info;
- g_frame_info = &frame_info;
- g_unwinder = unwinder;
- std::vector<const char*> expected_function_names;
-
- if (unwind_through_signal) {
- struct sigaction act, oldact;
- memset(&act, 0, sizeof(act));
- act.sa_sigaction = SignalLocalCallerHandler;
- act.sa_flags = SA_RESTART | SA_ONSTACK;
- ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
-
- raise(SIGUSR1);
-
- ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
-
- expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction",
- "LocalInnerFunction", "SignalLocalOuterFunction",
- "SignalLocalMiddleFunction", "SignalLocalInnerFunction"};
- } else {
- ASSERT_TRUE(unwinder->Unwind(&frame_info, 256));
-
- expected_function_names = {"LocalOuterFunction", "LocalMiddleFunction", "LocalInnerFunction"};
- }
-
- for (auto& frame : frame_info) {
- if (frame.function_name == expected_function_names.back()) {
- expected_function_names.pop_back();
- if (expected_function_names.empty()) {
- break;
- }
- }
- }
-
- ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
-}
-
-extern "C" void LocalMiddleFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
- LocalInnerFunction(unwinder, unwind_through_signal);
-}
-
-extern "C" void LocalOuterFunction(LocalUnwinder* unwinder, bool unwind_through_signal) {
- LocalMiddleFunction(unwinder, unwind_through_signal);
-}
-
-class LocalUnwinderTest : public ::testing::Test {
- protected:
- void SetUp() override {
- unwinder_.reset(new LocalUnwinder);
- ASSERT_TRUE(unwinder_->Init());
- }
-
- std::unique_ptr<LocalUnwinder> unwinder_;
-};
-
-TEST_F(LocalUnwinderTest, local) {
- LocalOuterFunction(unwinder_.get(), false);
-}
-
-TEST_F(LocalUnwinderTest, local_signal) {
- LocalOuterFunction(unwinder_.get(), true);
-}
-
-TEST_F(LocalUnwinderTest, local_multiple) {
- ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
-
- ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
-
- ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
-
- ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), true));
-}
-
-// This test verifies that doing an unwind before and after a dlopen
-// works. It's verifying that the maps read during the first unwind
-// do not cause a problem when doing the unwind using the code in
-// the dlopen'd code.
-TEST_F(LocalUnwinderTest, unwind_after_dlopen) {
- // Prime the maps data.
- ASSERT_NO_FATAL_FAILURE(LocalOuterFunction(unwinder_.get(), false));
-
- std::string testlib(testing::internal::GetArgvs()[0]);
- auto const value = testlib.find_last_of('/');
- if (value != std::string::npos) {
- testlib = testlib.substr(0, value + 1);
- } else {
- testlib = "";
- }
- testlib += "libunwindstack_local.so";
-
- void* handle = dlopen(testlib.c_str(), RTLD_NOW);
- ASSERT_TRUE(handle != nullptr);
-
- void (*unwind_function)(void*, void*) =
- reinterpret_cast<void (*)(void*, void*)>(dlsym(handle, "TestlibLevel1"));
- ASSERT_TRUE(unwind_function != nullptr);
-
- std::vector<LocalFrameData> frame_info;
- unwind_function(unwinder_.get(), &frame_info);
-
- ASSERT_EQ(0, dlclose(handle));
-
- std::vector<const char*> expected_function_names{"TestlibLevel1", "TestlibLevel2",
- "TestlibLevel3", "TestlibLevel4"};
-
- for (auto& frame : frame_info) {
- if (frame.function_name == expected_function_names.back()) {
- expected_function_names.pop_back();
- if (expected_function_names.empty()) {
- break;
- }
- }
- }
-
- ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, frame_info);
-}
-
-} // namespace unwindstack
diff --git a/libunwindstack/tests/LocalUpdatableMapsTest.cpp b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
index 0632a44..0dbabcd 100644
--- a/libunwindstack/tests/LocalUpdatableMapsTest.cpp
+++ b/libunwindstack/tests/LocalUpdatableMapsTest.cpp
@@ -36,8 +36,6 @@
void TestSetMapsFile(const std::string& maps_file) { maps_file_ = maps_file; }
- const std::vector<std::unique_ptr<MapInfo>>& TestGetSavedMaps() { return saved_maps_; }
-
private:
std::string maps_file_;
};
@@ -56,7 +54,7 @@
ASSERT_TRUE(maps_.Parse());
ASSERT_EQ(2U, maps_.Total());
- MapInfo* map_info = maps_.Get(0);
+ auto map_info = maps_.Get(0);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x3000U, map_info->start());
EXPECT_EQ(0x4000U, map_info->end());
@@ -83,15 +81,16 @@
maps_.TestSetMapsFile(tf.path);
ASSERT_TRUE(maps_.Reparse());
ASSERT_EQ(2U, maps_.Total());
- EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
- MapInfo* map_info = maps_.Get(0);
+ auto map_info = maps_.Get(0);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x3000U, map_info->start());
EXPECT_EQ(0x4000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(nullptr, map_info->prev_map());
+ EXPECT_EQ(maps_.Get(1), map_info->next_map());
map_info = maps_.Get(1);
ASSERT_TRUE(map_info != nullptr);
@@ -100,6 +99,8 @@
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+ EXPECT_EQ(nullptr, map_info->next_map());
}
TEST_F(LocalUpdatableMapsTest, same_map_new_perms) {
@@ -113,13 +114,15 @@
ASSERT_TRUE(maps_.Reparse());
ASSERT_EQ(2U, maps_.Total());
- MapInfo* map_info = maps_.Get(0);
+ auto map_info = maps_.Get(0);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x3000U, map_info->start());
EXPECT_EQ(0x4000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(nullptr, map_info->prev_map());
+ EXPECT_EQ(maps_.Get(1), map_info->next_map());
map_info = maps_.Get(1);
ASSERT_TRUE(map_info != nullptr);
@@ -128,16 +131,8 @@
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
-
- auto& saved_maps = maps_.TestGetSavedMaps();
- ASSERT_EQ(1U, saved_maps.size());
- map_info = saved_maps[0].get();
- ASSERT_TRUE(map_info != nullptr);
- EXPECT_EQ(0x3000U, map_info->start());
- EXPECT_EQ(0x4000U, map_info->end());
- EXPECT_EQ(0U, map_info->offset());
- EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
- EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+ EXPECT_EQ(nullptr, map_info->next_map());
}
TEST_F(LocalUpdatableMapsTest, same_map_new_name) {
@@ -151,13 +146,15 @@
ASSERT_TRUE(maps_.Reparse());
ASSERT_EQ(2U, maps_.Total());
- MapInfo* map_info = maps_.Get(0);
+ auto map_info = maps_.Get(0);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x3000U, map_info->start());
EXPECT_EQ(0x4000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_EQ("/fake/lib.so", map_info->name());
+ EXPECT_EQ(nullptr, map_info->prev_map());
+ EXPECT_EQ(maps_.Get(1), map_info->next_map());
map_info = maps_.Get(1);
ASSERT_TRUE(map_info != nullptr);
@@ -166,16 +163,8 @@
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
-
- auto& saved_maps = maps_.TestGetSavedMaps();
- ASSERT_EQ(1U, saved_maps.size());
- map_info = saved_maps[0].get();
- ASSERT_TRUE(map_info != nullptr);
- EXPECT_EQ(0x3000U, map_info->start());
- EXPECT_EQ(0x4000U, map_info->end());
- EXPECT_EQ(0U, map_info->offset());
- EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
- EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+ EXPECT_EQ(nullptr, map_info->next_map());
}
TEST_F(LocalUpdatableMapsTest, only_add_maps) {
@@ -190,15 +179,16 @@
maps_.TestSetMapsFile(tf.path);
ASSERT_TRUE(maps_.Reparse());
ASSERT_EQ(4U, maps_.Total());
- EXPECT_EQ(0U, maps_.TestGetSavedMaps().size());
- MapInfo* map_info = maps_.Get(0);
+ auto map_info = maps_.Get(0);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x1000U, map_info->start());
EXPECT_EQ(0x2000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(nullptr, map_info->prev_map());
+ EXPECT_EQ(maps_.Get(1), map_info->next_map());
map_info = maps_.Get(1);
ASSERT_TRUE(map_info != nullptr);
@@ -207,6 +197,8 @@
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+ EXPECT_EQ(maps_.Get(2), map_info->next_map());
map_info = maps_.Get(2);
ASSERT_TRUE(map_info != nullptr);
@@ -215,6 +207,8 @@
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(maps_.Get(1), map_info->prev_map());
+ EXPECT_EQ(maps_.Get(3), map_info->next_map());
map_info = maps_.Get(3);
ASSERT_TRUE(map_info != nullptr);
@@ -223,6 +217,8 @@
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(maps_.Get(2), map_info->prev_map());
+ EXPECT_EQ(nullptr, map_info->next_map());
}
TEST_F(LocalUpdatableMapsTest, all_new_maps) {
@@ -236,13 +232,15 @@
ASSERT_TRUE(maps_.Reparse());
ASSERT_EQ(2U, maps_.Total());
- MapInfo* map_info = maps_.Get(0);
+ auto map_info = maps_.Get(0);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x1000U, map_info->start());
EXPECT_EQ(0x2000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(nullptr, map_info->prev_map());
+ EXPECT_EQ(maps_.Get(1), map_info->next_map());
map_info = maps_.Get(1);
ASSERT_TRUE(map_info != nullptr);
@@ -251,24 +249,8 @@
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
-
- auto& saved_maps = maps_.TestGetSavedMaps();
- ASSERT_EQ(2U, saved_maps.size());
- map_info = saved_maps[0].get();
- ASSERT_TRUE(map_info != nullptr);
- EXPECT_EQ(0x3000U, map_info->start());
- EXPECT_EQ(0x4000U, map_info->end());
- EXPECT_EQ(0U, map_info->offset());
- EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
- EXPECT_TRUE(map_info->name().empty());
-
- map_info = saved_maps[1].get();
- ASSERT_TRUE(map_info != nullptr);
- EXPECT_EQ(0x8000U, map_info->start());
- EXPECT_EQ(0x9000U, map_info->end());
- EXPECT_EQ(0U, map_info->offset());
- EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
- EXPECT_TRUE(map_info->name().empty());
+ EXPECT_EQ(maps_.Get(0), map_info->prev_map());
+ EXPECT_EQ(nullptr, map_info->next_map());
}
TEST_F(LocalUpdatableMapsTest, add_map_prev_name_updated) {
@@ -283,14 +265,21 @@
ASSERT_TRUE(maps_.Reparse());
ASSERT_EQ(3U, maps_.Total());
- MapInfo* map_info = maps_.Get(2);
+ auto map_info = maps_.Get(2);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x9000U, map_info->start());
EXPECT_EQ(0xA000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_TRUE(map_info->name().empty());
- EXPECT_EQ(maps_.Get(1), map_info->prev_map());
+
+ // Check all of the prev and next pointers.
+ EXPECT_EQ(nullptr, maps_.Get(0)->prev_map());
+ EXPECT_EQ(maps_.Get(1), maps_.Get(0)->next_map());
+ EXPECT_EQ(maps_.Get(0), maps_.Get(1)->prev_map());
+ EXPECT_EQ(maps_.Get(2), maps_.Get(1)->next_map());
+ EXPECT_EQ(maps_.Get(1), maps_.Get(2)->prev_map());
+ EXPECT_EQ(nullptr, maps_.Get(2)->next_map());
}
TEST_F(LocalUpdatableMapsTest, add_map_prev_real_name_updated) {
@@ -298,7 +287,7 @@
ASSERT_TRUE(
android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
"4000-5000 ---p 00000 00:00 0\n"
- "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib.so\n"
"8000-9000 ---p 00000 00:00 0\n",
tf.path));
@@ -313,15 +302,16 @@
ASSERT_EQ(4U, maps_.Total());
ASSERT_FALSE(any_changed);
- MapInfo* map_info = maps_.Get(2);
+ auto map_info = maps_.Get(2);
ASSERT_TRUE(map_info != nullptr);
EXPECT_EQ(0x7000U, map_info->start());
EXPECT_EQ(0x8000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
- EXPECT_EQ(maps_.Get(0), map_info->prev_real_map());
+ EXPECT_EQ(maps_.Get(0), map_info->GetPrevRealMap());
EXPECT_EQ(maps_.Get(1), map_info->prev_map());
- EXPECT_EQ("/fake/lib1.so", map_info->name());
+ EXPECT_EQ(maps_.Get(3), map_info->next_map());
+ EXPECT_EQ("/fake/lib.so", map_info->name());
map_info = maps_.Get(3);
ASSERT_TRUE(map_info != nullptr);
@@ -329,14 +319,15 @@
EXPECT_EQ(0x9000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_TRUE(map_info->IsBlank());
- EXPECT_EQ(maps_.Get(2), map_info->prev_real_map());
+ EXPECT_EQ(nullptr, map_info->GetPrevRealMap());
EXPECT_EQ(maps_.Get(2), map_info->prev_map());
+ EXPECT_EQ(maps_.Get(4), map_info->next_map());
EXPECT_TRUE(map_info->name().empty());
ASSERT_TRUE(
android::base::WriteStringToFile("3000-4000 r-xp 00000 00:00 0 /fake/lib.so\n"
"4000-5000 ---p 00000 00:00 0\n"
- "7000-8000 r-xp 00000 00:00 0 /fake/lib1.so\n"
+ "7000-8000 r-xp 00000 00:00 0 /fake/lib.so\n"
"8000-9000 ---p 00000 00:00 0\n"
"9000-a000 r-xp 00000 00:00 0 /fake/lib2.so\n"
"a000-b000 r-xp 00000 00:00 0 /fake/lib3.so\n",
@@ -358,9 +349,10 @@
EXPECT_EQ(0x8000U, map_info->end());
EXPECT_EQ(0U, map_info->offset());
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
- EXPECT_EQ("/fake/lib1.so", map_info->name());
+ EXPECT_EQ("/fake/lib.so", map_info->name());
EXPECT_EQ(maps_.Get(1), map_info->prev_map());
- EXPECT_EQ(maps_.Get(0), map_info->prev_real_map());
+ EXPECT_EQ(maps_.Get(0), map_info->GetPrevRealMap());
+ EXPECT_EQ(maps_.Get(3), map_info->next_map());
map_info = maps_.Get(4);
ASSERT_TRUE(map_info != nullptr);
@@ -370,7 +362,8 @@
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_EQ("/fake/lib2.so", map_info->name());
EXPECT_EQ(maps_.Get(3), map_info->prev_map());
- EXPECT_EQ(maps_.Get(2), map_info->prev_real_map());
+ EXPECT_EQ(nullptr, map_info->GetPrevRealMap());
+ EXPECT_EQ(maps_.Get(5), map_info->next_map());
map_info = maps_.Get(5);
ASSERT_TRUE(map_info != nullptr);
@@ -380,7 +373,8 @@
EXPECT_EQ(PROT_READ | PROT_EXEC, map_info->flags());
EXPECT_EQ("/fake/lib3.so", map_info->name());
EXPECT_EQ(maps_.Get(4), map_info->prev_map());
- EXPECT_EQ(maps_.Get(4), map_info->prev_real_map());
+ EXPECT_EQ(nullptr, map_info->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map_info->next_map());
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/LogFake.h b/libunwindstack/tests/LogFake.h
index e1dc50d..11f47b1 100644
--- a/libunwindstack/tests/LogFake.h
+++ b/libunwindstack/tests/LogFake.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
+#pragma once
#include <string>
@@ -26,5 +25,3 @@
std::string GetFakeLogPrint();
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_TESTS_LOG_FAKE_H
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index d5e04a1..08e0d2b 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -35,7 +35,7 @@
#include <unwindstack/Memory.h>
#include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -89,32 +89,32 @@
};
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
- MapInfo info(nullptr, nullptr, 0x100, 0x100, 0, 0, elf_.path);
+ auto info = MapInfo::Create(0x100, 0x100, 0, 0, elf_.path);
- std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
- info.set_end(0xff);
- memory.reset(info.CreateMemory(process_memory_));
+ info->set_end(0xff);
+ memory.reset(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
// Make sure this test is valid.
- info.set_end(0x101);
- memory.reset(info.CreateMemory(process_memory_));
+ info->set_end(0x101);
+ memory.reset(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
+ EXPECT_FALSE(info->memory_backed_elf());
}
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x100, 0, elf_.path);
+ auto info = MapInfo::Create(0x100, 0x200, 0x100, 0, elf_.path);
- std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0x100U, info.elf_offset());
- EXPECT_EQ(0x100U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0x100U, info->elf_offset());
+ EXPECT_EQ(0U, info->elf_start_offset());
// Read the entire file.
std::vector<uint8_t> buffer(1024);
@@ -129,62 +129,61 @@
// Now verify the elf start offset is set correctly based on the previous
// info.
- MapInfo prev_info(nullptr, nullptr, 0, 0x100, 0x10, 0, "");
- info.set_prev_map(&prev_info);
- info.set_prev_real_map(&prev_info);
+ auto prev_info = MapInfo::Create(0, 0x100, 0x10, 0, "");
+ info->set_prev_map(prev_info);
// No preconditions met, change each one until it should set the elf start
// offset to zero.
- info.set_elf_offset(0);
- info.set_elf_start_offset(0);
- info.set_memory_backed_elf(false);
- memory.reset(info.CreateMemory(process_memory_));
+ info->set_elf_offset(0);
+ info->set_elf_start_offset(0);
+ info->set_memory_backed_elf(false);
+ memory.reset(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0x100U, info.elf_offset());
- EXPECT_EQ(0x100U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0x100U, info->elf_offset());
+ EXPECT_EQ(0U, info->elf_start_offset());
- prev_info.set_offset(0);
- info.set_elf_offset(0);
- info.set_elf_start_offset(0);
- info.set_memory_backed_elf(false);
- memory.reset(info.CreateMemory(process_memory_));
+ prev_info->set_offset(0);
+ info->set_elf_offset(0);
+ info->set_elf_start_offset(0);
+ info->set_memory_backed_elf(false);
+ memory.reset(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0x100U, info.elf_offset());
- EXPECT_EQ(0x100U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0x100U, info->elf_offset());
+ EXPECT_EQ(0U, info->elf_start_offset());
- prev_info.set_flags(PROT_READ);
- info.set_elf_offset(0);
- info.set_elf_start_offset(0);
- info.set_memory_backed_elf(false);
- memory.reset(info.CreateMemory(process_memory_));
+ prev_info->set_flags(PROT_READ);
+ info->set_elf_offset(0);
+ info->set_elf_start_offset(0);
+ info->set_memory_backed_elf(false);
+ memory.reset(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0x100U, info.elf_offset());
- EXPECT_EQ(0x100U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0x100U, info->elf_offset());
+ EXPECT_EQ(0U, info->elf_start_offset());
- prev_info.set_name(info.name());
- info.set_elf_offset(0);
- info.set_elf_start_offset(0);
- info.set_memory_backed_elf(false);
- memory.reset(info.CreateMemory(process_memory_));
+ prev_info->set_name(info->name());
+ info->set_elf_offset(0);
+ info->set_elf_start_offset(0);
+ info->set_memory_backed_elf(false);
+ memory.reset(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0x100U, info.elf_offset());
- EXPECT_EQ(0U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0x100U, info->elf_offset());
+ EXPECT_EQ(0U, info->elf_start_offset());
}
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(nullptr, nullptr, 0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
+ auto info = MapInfo::Create(0x100, 0x200, 0x1000, 0, elf_at_1000_.path);
- std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0U, info.elf_offset());
- EXPECT_EQ(0x1000U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0U, info->elf_offset());
+ EXPECT_EQ(0x1000U, info->elf_start_offset());
// Read the valid part of the file.
std::vector<uint8_t> buffer(0x100);
@@ -203,13 +202,13 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
+ auto info = MapInfo::Create(0x5000, 0x6000, 0x1000, 0, elf32_at_map_.path);
- std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0U, info.elf_offset());
- EXPECT_EQ(0x1000U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0U, info->elf_offset());
+ EXPECT_EQ(0x1000U, info->elf_start_offset());
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -221,13 +220,13 @@
}
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
+ auto info = MapInfo::Create(0x7000, 0x8000, 0x2000, 0, elf64_at_map_.path);
- std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_FALSE(info.memory_backed_elf());
- ASSERT_EQ(0U, info.elf_offset());
- EXPECT_EQ(0x2000U, info.elf_start_offset());
+ EXPECT_FALSE(info->memory_backed_elf());
+ ASSERT_EQ(0U, info->elf_offset());
+ EXPECT_EQ(0x2000U, info->elf_start_offset());
// Verify the memory is a valid elf.
uint8_t e_ident[SELFMAG + 1];
@@ -244,14 +243,14 @@
// be returned if the file mapping fails, but the device check is incorrect.
std::vector<uint8_t> buffer(1024);
uint64_t start = reinterpret_cast<uint64_t>(buffer.data());
- MapInfo info(nullptr, nullptr, start, start + buffer.size(), 0, 0x8000, "/dev/something");
+ auto info = MapInfo::Create(start, start + buffer.size(), 0, 0x8000, "/dev/something");
- std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
}
TEST_F(MapInfoCreateMemoryTest, process_memory) {
- MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
+ auto info = MapInfo::Create(0x2000, 0x3000, 0, PROT_READ, "");
Elf32_Ehdr ehdr = {};
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -263,11 +262,11 @@
for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
buffer[i] = i % 256;
}
- memory_->SetMemory(info.start(), buffer.data(), buffer.size());
+ memory_->SetMemory(info->start(), buffer.data(), buffer.size());
- std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ std::unique_ptr<Memory> memory(info->CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- EXPECT_TRUE(info.memory_backed_elf());
+ EXPECT_TRUE(info->memory_backed_elf());
memset(buffer.data(), 0, buffer.size());
ASSERT_TRUE(memory->ReadFully(0, buffer.data(), buffer.size()));
@@ -294,7 +293,7 @@
// Set the memory in the r-x map.
memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
- MapInfo* map_info = maps.Find(0x3000);
+ auto map_info = maps.Find(0x3000).get();
ASSERT_TRUE(map_info != nullptr);
std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
@@ -341,7 +340,7 @@
memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x4000 - sizeof(ehdr), 0x34);
memory_->SetMemoryBlock(0x4000, 0x1000, 0x43);
- MapInfo* map_info = maps.Find(0x4000);
+ auto map_info = maps.Find(0x4000).get();
ASSERT_TRUE(map_info != nullptr);
std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
@@ -367,13 +366,44 @@
}
}
+TEST_F(MapInfoCreateMemoryTest, valid_single_rx_non_zero_offset) {
+ Maps maps;
+ maps.Add(0x3000, 0x5000, 0xa000, PROT_READ | PROT_EXEC, "/only/in/memory.apk", 0);
+
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+
+ // Setup an elf at offset 0x3000 in memory..
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
+ memory_->SetMemoryBlock(0x3000 + sizeof(ehdr), 0x5000 - sizeof(ehdr), 0x34);
+
+ auto map_info = maps.Find(0x3000);
+ ASSERT_TRUE(map_info != nullptr);
+
+ std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
+ ASSERT_TRUE(mem.get() != nullptr);
+ EXPECT_TRUE(map_info->memory_backed_elf());
+ EXPECT_EQ(0UL, map_info->elf_offset());
+ EXPECT_EQ(0xa000UL, map_info->offset());
+ EXPECT_EQ(0xa000UL, map_info->elf_start_offset());
+
+ // Verify that reading values from this memory works properly.
+ std::vector<uint8_t> buffer(0x3000);
+ size_t bytes = mem->Read(0, buffer.data(), buffer.size());
+ ASSERT_EQ(0x2000UL, bytes);
+ ASSERT_EQ(0, memcmp(&ehdr, buffer.data(), sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < bytes; i++) {
+ ASSERT_EQ(0x34, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
TEST_F(MapInfoCreateMemoryTest, rosegment_from_file) {
Maps maps;
maps.Add(0x500, 0x600, 0, PROT_READ, "something_else", 0);
maps.Add(0x1000, 0x2000, 0x1000, PROT_READ, elf_at_1000_.path, 0);
maps.Add(0x2000, 0x3000, 0x2000, PROT_READ | PROT_EXEC, elf_at_1000_.path, 0);
- MapInfo* map_info = maps.Find(0x2000);
+ auto map_info = maps.Find(0x2000).get();
ASSERT_TRUE(map_info != nullptr);
// Set up the size
@@ -425,7 +455,7 @@
// Set the memory in the r-x map.
memory_->SetMemoryBlock(0x3000, 0x2000, 0x5d);
- MapInfo* map_info = maps.Find(0x3000);
+ auto map_info = maps.Find(0x3000).get();
ASSERT_TRUE(map_info != nullptr);
std::unique_ptr<Memory> mem(map_info->CreateMemory(process_memory_));
diff --git a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
index e123d0e..4877c68 100644
--- a/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
+++ b/libunwindstack/tests/MapInfoGetBuildIDTest.cpp
@@ -36,7 +36,8 @@
#include "ElfFake.h"
#include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/OfflineUnwindUtils.h"
namespace unwindstack {
@@ -50,8 +51,7 @@
elf_interface_ = new ElfInterfaceFake(memory_);
elf_->FakeSetInterface(elf_interface_);
elf_container_.reset(elf_);
- map_info_.reset(
- new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path));
+ map_info_ = MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, tf_->path);
}
void TearDown() override { delete memory_; }
@@ -62,15 +62,15 @@
ElfFake* elf_;
ElfInterfaceFake* elf_interface_;
std::unique_ptr<ElfFake> elf_container_;
- std::unique_ptr<MapInfo> map_info_;
+ std::shared_ptr<MapInfo> map_info_;
std::unique_ptr<TemporaryFile> tf_;
};
TEST_F(MapInfoGetBuildIDTest, no_elf_and_no_valid_elf_in_memory) {
- MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+ auto info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "");
- EXPECT_EQ("", info.GetBuildID());
- EXPECT_EQ("", info.GetPrintableBuildID());
+ EXPECT_EQ("", info->GetBuildID());
+ EXPECT_EQ("", info->GetPrintableBuildID());
}
TEST_F(MapInfoGetBuildIDTest, from_elf) {
@@ -197,9 +197,16 @@
}
TEST_F(MapInfoGetBuildIDTest, real_elf) {
- MapInfo map_info(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE,
- TestGetFileDirectory() + "offline/empty_arm64/libc.so");
- EXPECT_EQ("6df0590c4920f4c7b9f34fe833f37d54", map_info.GetPrintableBuildID());
+ auto map_info = MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE,
+ GetOfflineFilesDirectory() + "empty_arm64/libc.so");
+ EXPECT_EQ("6df0590c4920f4c7b9f34fe833f37d54", map_info->GetPrintableBuildID());
+}
+
+TEST_F(MapInfoGetBuildIDTest, in_device_map) {
+ auto map_info =
+ MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP,
+ GetOfflineFilesDirectory() + "empty_arm64/libc.so");
+ EXPECT_EQ("", map_info->GetPrintableBuildID());
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index 944f894..79f470f 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -37,7 +37,7 @@
#include <unwindstack/Memory.h>
#include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -59,7 +59,7 @@
ehdr->e_shnum = 4;
}
- void InitMapInfo(std::vector<std::unique_ptr<MapInfo>>& maps, bool in_memory);
+ void InitMapInfo(std::vector<std::shared_ptr<MapInfo>>& maps, bool in_memory);
const size_t kMapSize = 4096;
@@ -70,42 +70,40 @@
};
TEST_F(MapInfoGetElfTest, invalid) {
- MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+ auto info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "");
// The map is empty, but this should still create an invalid elf object.
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, valid32) {
- MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
-
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ auto info = MapInfo::Create(0x3000, 0x4000, 0, PROT_READ, "");
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
EXPECT_EQ(ELFCLASS32, elf->class_type());
// Now verify that an empty process memory returns an invalid elf object.
- info.set_elf(nullptr);
- elf = info.GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
+ info->set_elf(nullptr);
+ elf = info->GetElf(std::shared_ptr<Memory>(), ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, valid64) {
- MapInfo info(nullptr, nullptr, 0x8000, 0x9000, 0, PROT_READ, "");
-
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+ auto info = MapInfo::Create(0x8000, 0x9000, 0, PROT_READ, "");
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -113,26 +111,24 @@
}
TEST_F(MapInfoGetElfTest, invalid_arch_mismatch) {
- MapInfo info(nullptr, nullptr, 0x3000, 0x4000, 0, PROT_READ, "");
-
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, ARCH_X86);
+ auto info = MapInfo::Create(0x3000, 0x4000, 0, PROT_READ, "");
+ Elf* elf = info->GetElf(process_memory_, ARCH_X86);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
- MapInfo info(nullptr, nullptr, 0x2000, 0x3000, 0, PROT_READ, "");
-
TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x2000 + offset, ptr, size);
});
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ auto info = MapInfo::Create(0x2000, 0x3000, 0, PROT_READ, "");
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -141,14 +137,13 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
- MapInfo info(nullptr, nullptr, 0x5000, 0x8000, 0, PROT_READ, "");
-
TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
[&](uint64_t offset, const void* ptr, size_t size) {
memory_->SetMemory(0x5000 + offset, ptr, size);
});
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+ auto info = MapInfo::Create(0x5000, 0x8000, 0, PROT_READ, "");
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -157,26 +152,25 @@
}
TEST_F(MapInfoGetElfTest, end_le_start) {
- MapInfo info(nullptr, nullptr, 0x1000, 0x1000, 0, PROT_READ, elf_.path);
-
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ auto info = MapInfo::Create(0x1000, 0x1000, 0, PROT_READ, elf_.path);
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
- info.set_elf(nullptr);
- info.set_end(0xfff);
- elf = info.GetElf(process_memory_, ARCH_ARM);
+ info->set_elf(nullptr);
+ info->set_end(0xfff);
+ elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Make sure this test is valid.
- info.set_elf(nullptr);
- info.set_end(0x2000);
- elf = info.GetElf(process_memory_, ARCH_ARM);
+ info->set_elf(nullptr);
+ info->set_end(0x2000);
+ elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
}
@@ -184,8 +178,6 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
- MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
-
std::vector<uint8_t> buffer(0x1000);
memset(buffer.data(), 0, buffer.size());
Elf32_Ehdr ehdr;
@@ -193,11 +185,12 @@
memcpy(buffer.data(), &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ auto info = MapInfo::Create(0x1000, 0x2000, 0x100, PROT_READ, elf_.path);
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
- ASSERT_EQ(0x100U, info.elf_offset());
+ ASSERT_EQ(0x100U, info->elf_offset());
// Read the entire file.
memset(buffer.data(), 0, buffer.size());
@@ -213,20 +206,20 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
- MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
+ auto info = MapInfo::Create(0x1000, 0x2000, 0x2000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
- memcpy(&buffer[info.offset()], &ehdr, sizeof(ehdr));
+ memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
- ASSERT_EQ(0U, info.elf_offset());
+ ASSERT_EQ(0U, info->elf_offset());
// Read the valid part of the file.
ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
@@ -243,7 +236,7 @@
// embedded elf is bigger than the initial map, the new object is larger
// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
- MapInfo info(nullptr, nullptr, 0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
+ auto info = MapInfo::Create(0x5000, 0x6000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -252,14 +245,14 @@
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
ehdr.e_shnum = 4;
- memcpy(&buffer[info.offset()], &ehdr, sizeof(ehdr));
+ memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM);
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
- ASSERT_EQ(0U, info.elf_offset());
+ ASSERT_EQ(0U, info->elf_offset());
// Verify the memory is a valid elf.
memset(buffer.data(), 0, buffer.size());
@@ -271,7 +264,7 @@
}
TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
- MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
+ auto info = MapInfo::Create(0x7000, 0x8000, 0x1000, PROT_READ, elf_.path);
std::vector<uint8_t> buffer(0x4000);
memset(buffer.data(), 0, buffer.size());
@@ -280,14 +273,45 @@
ehdr.e_shoff = 0x2000;
ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
ehdr.e_shnum = 4;
- memcpy(&buffer[info.offset()], &ehdr, sizeof(ehdr));
+ memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- Elf* elf = info.GetElf(process_memory_, ARCH_ARM64);
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
ASSERT_TRUE(elf->memory() != nullptr);
- ASSERT_EQ(0U, info.elf_offset());
+ ASSERT_EQ(0U, info->elf_offset());
+
+ // Verify the memory is a valid elf.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->ReadFully(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(elf->memory()->ReadFully(0x1000, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// the initial map is smaller than elf header size, we can still read the elf.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64_small_map_range) {
+ auto info = MapInfo::Create(0x7000, 0x7004, 0x1000, PROT_READ, elf_.path);
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[info->offset()], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ Elf* elf = info->GetElf(process_memory_, ARCH_ARM64);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info->elf_offset());
// Verify the memory is a valid elf.
memset(buffer.data(), 0, buffer.size());
@@ -299,9 +323,6 @@
}
TEST_F(MapInfoGetElfTest, check_device_maps) {
- MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP,
- "/dev/something");
-
// Create valid elf data in process memory for this to verify that only
// the name is causing invalid elf data.
Elf64_Ehdr ehdr;
@@ -311,20 +332,22 @@
ehdr.e_shnum = 0;
memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
- Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
+ auto info =
+ MapInfo::Create(0x7000, 0x8000, 0x1000, PROT_READ | MAPS_FLAGS_DEVICE_MAP, "/dev/something");
+ Elf* elf = info->GetElf(process_memory_, ARCH_X86_64);
ASSERT_TRUE(elf != nullptr);
ASSERT_FALSE(elf->valid());
// Set the name to nothing to verify that it still fails.
- info.set_elf(nullptr);
- info.set_name("");
- elf = info.GetElf(process_memory_, ARCH_X86_64);
+ info->set_elf(nullptr);
+ info->set_name("");
+ elf = info->GetElf(process_memory_, ARCH_X86_64);
ASSERT_FALSE(elf->valid());
// Change the flags and verify the elf is valid now.
- info.set_elf(nullptr);
- info.set_flags(PROT_READ);
- elf = info.GetElf(process_memory_, ARCH_X86_64);
+ info->set_elf(nullptr);
+ info->set_flags(PROT_READ);
+ elf = info->GetElf(process_memory_, ARCH_X86_64);
ASSERT_TRUE(elf->valid());
}
@@ -345,17 +368,17 @@
wait = true;
// Create all of the threads and have them do the GetElf at the same time
// to make it likely that a race will occur.
- MapInfo info(nullptr, nullptr, 0x7000, 0x8000, 0x1000, PROT_READ, "");
+ auto info = MapInfo::Create(0x7000, 0x8000, 0x1000, PROT_READ, "");
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, this, &wait, &info, &elf_in_threads]() {
while (wait)
;
- Elf* elf = info.GetElf(process_memory_, ARCH_X86_64);
+ Elf* elf = info->GetElf(process_memory_, ARCH_X86_64);
elf_in_threads[i] = elf;
});
threads.push_back(thread);
}
- ASSERT_TRUE(info.elf() == nullptr);
+ ASSERT_TRUE(info->elf() == nullptr);
// Set them all going and wait for the threads to finish.
wait = false;
@@ -365,7 +388,7 @@
}
// Now verify that all of the elf files are exactly the same and valid.
- Elf* elf = info.elf().get();
+ Elf* elf = info->elf().get();
ASSERT_TRUE(elf != nullptr);
EXPECT_TRUE(elf->valid());
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
@@ -375,24 +398,23 @@
// Verify that previous maps don't automatically get the same elf object.
TEST_F(MapInfoGetElfTest, prev_map_elf_not_set) {
- MapInfo info1(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "/not/present");
- MapInfo info2(&info1, &info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
+ auto info1 = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "/not/present");
+ auto info2 = MapInfo::Create(info1, 0x2000, 0x3000, 0, PROT_READ, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x2000, &ehdr, sizeof(ehdr));
- Elf* elf = info2.GetElf(process_memory_, ARCH_ARM);
+ Elf* elf = info2->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
- ASSERT_NE(elf, info1.GetElf(process_memory_, ARCH_ARM));
+ ASSERT_NE(elf, info1->GetElf(process_memory_, ARCH_ARM));
}
-void MapInfoGetElfTest::InitMapInfo(std::vector<std::unique_ptr<MapInfo>>& maps, bool in_memory) {
+void MapInfoGetElfTest::InitMapInfo(std::vector<std::shared_ptr<MapInfo>>& maps, bool in_memory) {
maps.resize(2);
- maps[0].reset(new MapInfo(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path));
- maps[1].reset(new MapInfo(maps[0].get(), maps[0].get(), 0x2000, 0x3000, 0x1000,
- PROT_READ | PROT_EXEC, elf_.path));
+ maps[0] = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ maps[1] = MapInfo::Create(maps[0], 0x2000, 0x3000, 0x1000, PROT_READ | PROT_EXEC, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
@@ -406,7 +428,7 @@
// Verify that a read-only map followed by a read-execute map will result
// in the same elf object in both maps.
TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf_exec_first) {
- std::vector<std::unique_ptr<MapInfo>> maps;
+ std::vector<std::shared_ptr<MapInfo>> maps;
// First use in memory maps.
InitMapInfo(maps, true);
@@ -439,7 +461,7 @@
// Verify that a read-only map followed by a read-execute map will result
// in the same elf object in both maps.
TEST_F(MapInfoGetElfTest, read_only_followed_by_read_exec_share_elf_read_only_first) {
- std::vector<std::unique_ptr<MapInfo>> maps;
+ std::vector<std::shared_ptr<MapInfo>> maps;
// First use in memory maps.
InitMapInfo(maps, true);
@@ -472,18 +494,18 @@
// Verify that a read-only map followed by an empty map, then followed by
// a read-execute map will result in the same elf object in both maps.
TEST_F(MapInfoGetElfTest, read_only_followed_by_empty_then_read_exec_share_elf) {
- MapInfo r_info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, elf_.path);
- MapInfo empty(&r_info, &r_info, 0x2000, 0x3000, 0, 0, "");
- MapInfo rw_info(&empty, &r_info, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
+ auto r_info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, elf_.path);
+ auto empty = MapInfo::Create(r_info, 0x2000, 0x3000, 0, 0, "");
+ auto rw_info = MapInfo::Create(empty, 0x3000, 0x4000, 0x2000, PROT_READ | PROT_EXEC, elf_.path);
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
memory_->SetMemory(0x1000, &ehdr, sizeof(ehdr));
- Elf* elf = rw_info.GetElf(process_memory_, ARCH_ARM);
+ Elf* elf = rw_info->GetElf(process_memory_, ARCH_ARM);
ASSERT_TRUE(elf != nullptr);
ASSERT_TRUE(elf->valid());
- ASSERT_EQ(elf, r_info.GetElf(process_memory_, ARCH_ARM));
+ ASSERT_EQ(elf, r_info->GetElf(process_memory_, ARCH_ARM));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index 5ddf1de..a0aca4f 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -39,7 +39,7 @@
#include "ElfFake.h"
#include "ElfTestUtils.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -50,7 +50,7 @@
process_memory_.reset(memory_);
elf_ = new ElfFake(new MemoryFake);
elf_container_.reset(elf_);
- map_info_.reset(new MapInfo(nullptr, nullptr, 0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, ""));
+ map_info_ = MapInfo::Create(0x1000, 0x20000, 0, PROT_READ | PROT_WRITE, "");
}
void MultipleThreadTest(uint64_t expected_load_bias);
@@ -59,13 +59,13 @@
MemoryFake* memory_;
ElfFake* elf_;
std::unique_ptr<ElfFake> elf_container_;
- std::unique_ptr<MapInfo> map_info_;
+ std::shared_ptr<MapInfo> map_info_;
};
TEST_F(MapInfoGetLoadBiasTest, no_elf_and_no_valid_elf_in_memory) {
- MapInfo info(nullptr, nullptr, 0x1000, 0x2000, 0, PROT_READ, "");
+ auto info = MapInfo::Create(0x1000, 0x2000, 0, PROT_READ, "");
- EXPECT_EQ(0U, info.GetLoadBias(process_memory_));
+ EXPECT_EQ(0U, info->GetLoadBias(process_memory_));
}
TEST_F(MapInfoGetLoadBiasTest, load_bias_cached_from_elf) {
@@ -84,7 +84,7 @@
elf_->FakeSetLoadBias(0);
EXPECT_EQ(0U, map_info_->GetLoadBias(process_memory_));
- map_info_->set_load_bias(INT64_MAX);
+ map_info_->set_load_bias(UINT64_MAX);
elf_->FakeSetLoadBias(0x1000);
EXPECT_EQ(0x1000U, map_info_->GetLoadBias(process_memory_));
}
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
index 7ba9471..9cfbb44 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -15,6 +15,7 @@
*/
#include <stdint.h>
+#include <sys/mman.h>
#include <thread>
@@ -28,34 +29,98 @@
namespace unwindstack {
TEST(MapInfoTest, maps_constructor_const_char) {
- MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
- MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, "map");
+ auto prev_map = MapInfo::Create(0, 0, 0, 0, "");
+ auto map_info = MapInfo::Create(prev_map, 1, 2, 3, 4, "map");
- EXPECT_EQ(&prev_map, map_info.prev_map());
- EXPECT_EQ(1UL, map_info.start());
- EXPECT_EQ(2UL, map_info.end());
- EXPECT_EQ(3UL, map_info.offset());
- EXPECT_EQ(4UL, map_info.flags());
- EXPECT_EQ("map", map_info.name());
- EXPECT_EQ(INT64_MAX, map_info.load_bias());
- EXPECT_EQ(0UL, map_info.elf_offset());
- EXPECT_TRUE(map_info.elf().get() == nullptr);
+ EXPECT_EQ(prev_map.get(), map_info->prev_map().get());
+ EXPECT_EQ(1UL, map_info->start());
+ EXPECT_EQ(2UL, map_info->end());
+ EXPECT_EQ(3UL, map_info->offset());
+ EXPECT_EQ(4UL, map_info->flags());
+ EXPECT_EQ("map", map_info->name());
+ EXPECT_EQ(UINT64_MAX, map_info->load_bias());
+ EXPECT_EQ(0UL, map_info->elf_offset());
+ EXPECT_TRUE(map_info->elf().get() == nullptr);
}
TEST(MapInfoTest, maps_constructor_string) {
std::string name("string_map");
- MapInfo prev_map(nullptr, nullptr, 0, 0, 0, 0, "");
- MapInfo map_info(&prev_map, &prev_map, 1, 2, 3, 4, name);
+ auto prev_map = MapInfo::Create(0, 0, 0, 0, "");
+ auto map_info = MapInfo::Create(prev_map, 1, 2, 3, 4, name);
- EXPECT_EQ(&prev_map, map_info.prev_map());
- EXPECT_EQ(1UL, map_info.start());
- EXPECT_EQ(2UL, map_info.end());
- EXPECT_EQ(3UL, map_info.offset());
- EXPECT_EQ(4UL, map_info.flags());
- EXPECT_EQ("string_map", map_info.name());
- EXPECT_EQ(INT64_MAX, map_info.load_bias());
- EXPECT_EQ(0UL, map_info.elf_offset());
- EXPECT_TRUE(map_info.elf().get() == nullptr);
+ EXPECT_EQ(prev_map, map_info->prev_map());
+ EXPECT_EQ(1UL, map_info->start());
+ EXPECT_EQ(2UL, map_info->end());
+ EXPECT_EQ(3UL, map_info->offset());
+ EXPECT_EQ(4UL, map_info->flags());
+ EXPECT_EQ("string_map", map_info->name());
+ EXPECT_EQ(UINT64_MAX, map_info->load_bias());
+ EXPECT_EQ(0UL, map_info->elf_offset());
+ EXPECT_TRUE(map_info->elf().get() == nullptr);
+}
+
+TEST(MapInfoTest, real_map_check) {
+ auto map1 = MapInfo::Create(0, 0x1000, 0, PROT_READ, "fake.so");
+ auto map2 = MapInfo::Create(map1, 0, 0, 0, 0, "");
+ auto map3 = MapInfo::Create(map2, 0x1000, 0x2000, 0x1000, PROT_READ | PROT_EXEC, "fake.so");
+
+ EXPECT_EQ(nullptr, map1->prev_map());
+ EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+ EXPECT_EQ(map2, map1->next_map());
+ EXPECT_EQ(map3, map1->GetNextRealMap());
+
+ EXPECT_EQ(map1, map2->prev_map());
+ EXPECT_EQ(nullptr, map2->GetPrevRealMap());
+ EXPECT_EQ(map3, map2->next_map());
+ EXPECT_EQ(nullptr, map2->GetNextRealMap());
+
+ EXPECT_EQ(map2, map3->prev_map());
+ EXPECT_EQ(map1, map3->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map3->next_map());
+ EXPECT_EQ(nullptr, map3->GetNextRealMap());
+
+ // Verify that if the middle map is not blank, then the Get{Next,Prev}RealMap
+ // functions return nullptrs.
+ map2->set_offset(1);
+ EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map1->GetNextRealMap());
+ EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map3->GetNextRealMap());
+ map2->set_offset(0);
+ EXPECT_EQ(map3, map1->GetNextRealMap());
+
+ map2->set_flags(1);
+ EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map1->GetNextRealMap());
+ EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map3->GetNextRealMap());
+ map2->set_flags(0);
+ EXPECT_EQ(map3, map1->GetNextRealMap());
+
+ map2->set_name("something");
+ EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map1->GetNextRealMap());
+ EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map3->GetNextRealMap());
+ map2->set_name("");
+ EXPECT_EQ(map3, map1->GetNextRealMap());
+
+ // Verify that if the Get{Next,Prev}RealMap names must match.
+ map1->set_name("another");
+ EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map1->GetNextRealMap());
+ EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map3->GetNextRealMap());
+ map1->set_name("fake.so");
+ EXPECT_EQ(map3, map1->GetNextRealMap());
+
+ map3->set_name("another");
+ EXPECT_EQ(nullptr, map1->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map1->GetNextRealMap());
+ EXPECT_EQ(nullptr, map3->GetPrevRealMap());
+ EXPECT_EQ(nullptr, map3->GetNextRealMap());
+ map3->set_name("fake.so");
+ EXPECT_EQ(map3, map1->GetNextRealMap());
}
TEST(MapInfoTest, get_function_name) {
@@ -64,18 +129,18 @@
elf->FakeSetInterface(interface);
interface->FakePushFunctionData(FunctionData("function", 1000));
- MapInfo map_info(nullptr, nullptr, 1, 2, 3, 4, "");
- map_info.set_elf(elf);
+ auto map_info = MapInfo::Create(1, 2, 3, 4, "");
+ map_info->set_elf(elf);
SharedString name;
uint64_t offset;
- ASSERT_TRUE(map_info.GetFunctionName(1000, &name, &offset));
+ ASSERT_TRUE(map_info->GetFunctionName(1000, &name, &offset));
EXPECT_EQ("function", name);
EXPECT_EQ(1000UL, offset);
}
TEST(MapInfoTest, multiple_thread_get_elf_fields) {
- MapInfo map_info(nullptr, nullptr, 0, 0, 0, 0, "");
+ auto map_info = MapInfo::Create(0, 0, 0, 0, "");
static constexpr size_t kNumConcurrentThreads = 100;
MapInfo::ElfFields* elf_fields[kNumConcurrentThreads];
@@ -89,7 +154,7 @@
std::thread* thread = new std::thread([i, &wait, &map_info, &elf_fields]() {
while (wait)
;
- elf_fields[i] = &map_info.GetElfFields();
+ elf_fields[i] = &map_info->GetElfFields();
});
threads.push_back(thread);
}
@@ -102,11 +167,29 @@
}
// Now verify that all of elf fields are exactly the same and valid.
- MapInfo::ElfFields* expected_elf_fields = &map_info.GetElfFields();
+ MapInfo::ElfFields* expected_elf_fields = &map_info->GetElfFields();
ASSERT_TRUE(expected_elf_fields != nullptr);
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
EXPECT_EQ(expected_elf_fields, elf_fields[i]) << "Thread " << i << " mismatched.";
}
}
+TEST(MapInfoTest, elf_file_not_readable) {
+ auto map_info_readable = MapInfo::Create(0, 0x1000, 0, PROT_READ, "fake.so");
+ map_info_readable->set_memory_backed_elf(true);
+ ASSERT_TRUE(map_info_readable->ElfFileNotReadable());
+
+ auto map_info_no_name = MapInfo::Create(0, 0x1000, 0, PROT_READ, "");
+ map_info_no_name->set_memory_backed_elf(true);
+ ASSERT_FALSE(map_info_no_name->ElfFileNotReadable());
+
+ auto map_info_bracket = MapInfo::Create(0, 0x2000, 0, PROT_READ, "[vdso]");
+ map_info_bracket->set_memory_backed_elf(true);
+ ASSERT_FALSE(map_info_bracket->ElfFileNotReadable());
+
+ auto map_info_memfd = MapInfo::Create(0, 0x3000, 0, PROT_READ, "/memfd:jit-cache");
+ map_info_memfd->set_memory_backed_elf(true);
+ ASSERT_FALSE(map_info_memfd->ElfFileNotReadable());
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapsTest.cpp b/libunwindstack/tests/MapsTest.cpp
index 309bdc2..8226b11 100644
--- a/libunwindstack/tests/MapsTest.cpp
+++ b/libunwindstack/tests/MapsTest.cpp
@@ -32,7 +32,7 @@
ASSERT_FALSE(maps.Parse()) << "Failed on: " + line;
} else {
ASSERT_TRUE(maps.Parse()) << "Failed on: " + line;
- MapInfo* element = maps.Get(0);
+ MapInfo* element = maps.Get(0).get();
ASSERT_TRUE(element != nullptr) << "Failed on: " + line;
info->set_start(element->start());
info->set_end(element->end());
@@ -47,18 +47,36 @@
Maps maps;
maps.Add(0x1000, 0x2000, 0, PROT_READ, "fake_map", 0);
- maps.Add(0x3000, 0x4000, 0x10, 0, "", 0x1234);
- maps.Add(0x5000, 0x6000, 1, 2, "fake_map2", static_cast<uint64_t>(-1));
+ maps.Add(0x3000, 0x4000, 0, 0, "", 0x1234);
+ maps.Add(0x5000, 0x6000, 1, 2, "fake_map", static_cast<uint64_t>(-1));
ASSERT_EQ(3U, maps.Total());
- MapInfo* info = maps.Get(0);
- ASSERT_EQ(0x1000U, info->start());
- ASSERT_EQ(0x2000U, info->end());
- ASSERT_EQ(0U, info->offset());
- ASSERT_EQ(PROT_READ, info->flags());
- ASSERT_EQ("fake_map", info->name());
- ASSERT_EQ(0U, info->elf_offset());
- ASSERT_EQ(0U, info->load_bias().load());
+ auto info1 = maps.Get(0);
+ auto info2 = maps.Get(1);
+ auto info3 = maps.Get(2);
+
+ EXPECT_EQ(nullptr, info1->prev_map());
+ EXPECT_EQ(nullptr, info1->GetPrevRealMap());
+ EXPECT_EQ(info2, info1->next_map());
+ EXPECT_EQ(info3, info1->GetNextRealMap());
+
+ EXPECT_EQ(info1, info2->prev_map());
+ EXPECT_EQ(nullptr, info2->GetPrevRealMap());
+ EXPECT_EQ(info3, info2->next_map());
+ EXPECT_EQ(nullptr, info2->GetNextRealMap());
+
+ EXPECT_EQ(info2, info3->prev_map());
+ EXPECT_EQ(info1, info3->GetPrevRealMap());
+ EXPECT_EQ(nullptr, info3->next_map());
+ EXPECT_EQ(nullptr, info3->GetNextRealMap());
+
+ ASSERT_EQ(0x1000U, info1->start());
+ ASSERT_EQ(0x2000U, info1->end());
+ ASSERT_EQ(0U, info1->offset());
+ ASSERT_EQ(PROT_READ, info1->flags());
+ ASSERT_EQ("fake_map", info1->name());
+ ASSERT_EQ(0U, info1->elf_offset());
+ ASSERT_EQ(0U, info1->load_bias().load());
}
TEST(MapsTest, map_move) {
@@ -71,7 +89,7 @@
Maps maps2 = std::move(maps);
ASSERT_EQ(3U, maps2.Total());
- MapInfo* info = maps2.Get(0);
+ auto info = maps2.Get(0);
ASSERT_EQ(0x1000U, info->start());
ASSERT_EQ(0x2000U, info->end());
ASSERT_EQ(0U, info->offset());
@@ -82,28 +100,28 @@
}
TEST(MapsTest, verify_parse_line) {
- MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
+ auto info = MapInfo::Create(0, 0, 0, 0, "");
- VerifyLine("01-02 rwxp 03 04:05 06\n", &info);
- EXPECT_EQ(1U, info.start());
- EXPECT_EQ(2U, info.end());
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
- EXPECT_EQ(3U, info.offset());
- EXPECT_EQ("", info.name());
+ VerifyLine("01-02 rwxp 03 04:05 06\n", info.get());
+ EXPECT_EQ(1U, info->start());
+ EXPECT_EQ(2U, info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+ EXPECT_EQ(3U, info->offset());
+ EXPECT_EQ("", info->name());
- VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", &info);
- EXPECT_EQ(0xaU, info.start());
- EXPECT_EQ(0xbU, info.end());
- EXPECT_EQ(0U, info.flags());
- EXPECT_EQ(0xcU, info.offset());
- EXPECT_EQ("/fake/name", info.name());
+ VerifyLine("0a-0b ---s 0c 0d:0e 06 /fake/name\n", info.get());
+ EXPECT_EQ(0xaU, info->start());
+ EXPECT_EQ(0xbU, info->end());
+ EXPECT_EQ(0U, info->flags());
+ EXPECT_EQ(0xcU, info->offset());
+ EXPECT_EQ("/fake/name", info->name());
- VerifyLine("01-02 rwxp 03 04:05 06 /fake/name/again\n", &info);
- EXPECT_EQ(1U, info.start());
- EXPECT_EQ(2U, info.end());
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
- EXPECT_EQ(3U, info.offset());
- EXPECT_EQ("/fake/name/again", info.name());
+ VerifyLine("01-02 rwxp 03 04:05 06 /fake/name/again\n", info.get());
+ EXPECT_EQ(1U, info->start());
+ EXPECT_EQ(2U, info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+ EXPECT_EQ(3U, info->offset());
+ EXPECT_EQ("/fake/name/again", info->name());
VerifyLine("-00 rwxp 00 00:00 0\n", nullptr);
VerifyLine("00- rwxp 00 00:00 0\n", nullptr);
@@ -155,19 +173,19 @@
}
TEST(MapsTest, verify_large_values) {
- MapInfo info(nullptr, nullptr, 0, 0, 0, 0, "");
+ auto info = MapInfo::Create(0, 0, 0, 0, "");
#if defined(__LP64__)
- VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", &info);
- EXPECT_EQ(0xfabcdef012345678UL, info.start());
- EXPECT_EQ(0xf12345678abcdef8UL, info.end());
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
- EXPECT_EQ(0xf0b0d0f010305070UL, info.offset());
+ VerifyLine("fabcdef012345678-f12345678abcdef8 rwxp f0b0d0f010305070 00:00 0\n", info.get());
+ EXPECT_EQ(0xfabcdef012345678UL, info->start());
+ EXPECT_EQ(0xf12345678abcdef8UL, info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+ EXPECT_EQ(0xf0b0d0f010305070UL, info->offset());
#else
- VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", &info);
- EXPECT_EQ(0xf2345678UL, info.start());
- EXPECT_EQ(0xfabcdef8UL, info.end());
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info.flags());
- EXPECT_EQ(0xf0305070UL, info.offset());
+ VerifyLine("f2345678-fabcdef8 rwxp f0305070 00:00 0\n", info.get());
+ EXPECT_EQ(0xf2345678UL, info->start());
+ EXPECT_EQ(0xfabcdef8UL, info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, info->flags());
+ EXPECT_EQ(0xf0305070UL, info->offset());
#endif
}
@@ -182,7 +200,7 @@
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(5U, maps.Total());
- MapInfo* info = maps.Get(0);
+ auto info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(PROT_NONE, info->flags());
EXPECT_EQ(0x1000U, info->start());
@@ -234,7 +252,7 @@
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(3U, maps.Total());
- MapInfo* info = maps.Get(0);
+ auto info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ("", info->name());
EXPECT_EQ(0x7b29b000U, info->start());
@@ -269,7 +287,7 @@
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(2U, maps.Total());
- MapInfo* info = maps.Get(0);
+ auto info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0U, info->offset());
EXPECT_EQ(0xa000U, info->start());
@@ -334,7 +352,7 @@
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(4U, maps.Total());
- MapInfo* info = maps.Get(0);
+ auto info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_TRUE(info->flags() & 0x8000);
EXPECT_EQ("/dev/", info->name());
@@ -367,7 +385,7 @@
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(3U, maps.Total());
- MapInfo* info = maps.Get(0);
+ auto info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b29b000U, info->start());
EXPECT_EQ(0x7b29e000U, info->end());
@@ -409,7 +427,7 @@
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(3U, maps.Total());
- MapInfo* info = maps.Get(0);
+ auto info = maps.Get(0);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x7b29b000U, info->start());
EXPECT_EQ(0x7b29e000U, info->end());
@@ -501,7 +519,7 @@
EXPECT_EQ(index, maps.Total());
// Verify all of the maps.
for (size_t i = 0; i < index; i++) {
- MapInfo* info = maps.Get(i);
+ auto info = maps.Get(i);
ASSERT_TRUE(info != nullptr) << "Failed verifying index " + std::to_string(i);
EXPECT_EQ(i * 4096, info->start()) << "Failed verifying index " + std::to_string(i);
EXPECT_EQ((i + 1) * 4096, info->end()) << "Failed verifying index " + std::to_string(i);
@@ -551,7 +569,7 @@
ASSERT_TRUE(maps.Parse());
ASSERT_EQ(5000U, maps.Total());
for (size_t i = 0; i < 5000; i++) {
- MapInfo* info = maps.Get(i);
+ auto info = maps.Get(i);
EXPECT_EQ(start + i * 4096, info->start()) << "Failed at map " + std::to_string(i);
EXPECT_EQ(start + (i + 1) * 4096, info->end()) << "Failed at map " + std::to_string(i);
std::string name = "/fake" + std::to_string(i) + ".so";
@@ -576,7 +594,7 @@
EXPECT_TRUE(maps.Find(0xf000) == nullptr);
EXPECT_TRUE(maps.Find(0xf010) == nullptr);
- MapInfo* info = maps.Find(0x1000);
+ auto info = maps.Find(0x1000);
ASSERT_TRUE(info != nullptr);
EXPECT_EQ(0x1000U, info->start());
EXPECT_EQ(0x2000U, info->end());
@@ -617,4 +635,75 @@
EXPECT_EQ("/system/lib/fake5.so", info->name());
}
+TEST(MapsTest, sort) {
+ Maps maps;
+
+ maps.Add(0x8000, 0x9000, 0, 0, "", 0);
+ maps.Add(0x7000, 0x8000, 0, 0, "lib.so", 0);
+ maps.Add(0x6000, 0x7000, 0, 0, "", 0);
+ maps.Add(0x5000, 0x6000, 0, 0, "lib.so", 0);
+ maps.Add(0x4000, 0x5000, 0, 0, "", 0);
+ maps.Add(0x3000, 0x4000, 0, 0, "", 0);
+ maps.Add(0x2000, 0x3000, 0, 0, "lib.so", 0);
+ maps.Add(0x1000, 0x2000, 0, 0, "", 0);
+
+ maps.Sort();
+
+ EXPECT_EQ(0x1000UL, maps.Get(0)->start());
+ EXPECT_EQ(nullptr, maps.Get(0)->prev_map());
+ EXPECT_EQ(maps.Get(1), maps.Get(0)->next_map());
+ EXPECT_EQ(nullptr, maps.Get(0)->GetPrevRealMap());
+ EXPECT_EQ(nullptr, maps.Get(0)->GetNextRealMap());
+
+ EXPECT_EQ(0x2000UL, maps.Get(1)->start());
+ EXPECT_EQ(maps.Get(0), maps.Get(1)->prev_map());
+ EXPECT_EQ(maps.Get(2), maps.Get(1)->next_map());
+ EXPECT_EQ(nullptr, maps.Get(1)->GetPrevRealMap());
+ EXPECT_EQ(maps.Get(4), maps.Get(1)->GetNextRealMap());
+
+ EXPECT_EQ(0x3000UL, maps.Get(2)->start());
+ EXPECT_EQ(maps.Get(1), maps.Get(2)->prev_map());
+ EXPECT_EQ(maps.Get(3), maps.Get(2)->next_map());
+ EXPECT_EQ(nullptr, maps.Get(2)->GetPrevRealMap());
+ EXPECT_EQ(nullptr, maps.Get(2)->GetNextRealMap());
+
+ EXPECT_EQ(0x4000UL, maps.Get(3)->start());
+ EXPECT_EQ(maps.Get(2), maps.Get(3)->prev_map());
+ EXPECT_EQ(maps.Get(4), maps.Get(3)->next_map());
+ EXPECT_EQ(nullptr, maps.Get(3)->GetPrevRealMap());
+ EXPECT_EQ(nullptr, maps.Get(3)->GetNextRealMap());
+
+ EXPECT_EQ(0x5000UL, maps.Get(4)->start());
+ EXPECT_EQ(maps.Get(3), maps.Get(4)->prev_map());
+ EXPECT_EQ(maps.Get(5), maps.Get(4)->next_map());
+ EXPECT_EQ(maps.Get(1), maps.Get(4)->GetPrevRealMap());
+ EXPECT_EQ(maps.Get(6), maps.Get(4)->GetNextRealMap());
+
+ EXPECT_EQ(0x6000UL, maps.Get(5)->start());
+ EXPECT_EQ(maps.Get(4), maps.Get(5)->prev_map());
+ EXPECT_EQ(maps.Get(6), maps.Get(5)->next_map());
+ EXPECT_EQ(nullptr, maps.Get(5)->GetPrevRealMap());
+ EXPECT_EQ(nullptr, maps.Get(5)->GetNextRealMap());
+
+ EXPECT_EQ(0x7000UL, maps.Get(6)->start());
+ EXPECT_EQ(maps.Get(5), maps.Get(6)->prev_map());
+ EXPECT_EQ(maps.Get(7), maps.Get(6)->next_map());
+ EXPECT_EQ(maps.Get(4), maps.Get(6)->GetPrevRealMap());
+ EXPECT_EQ(nullptr, maps.Get(6)->GetNextRealMap());
+
+ EXPECT_EQ(0x8000UL, maps.Get(7)->start());
+ EXPECT_EQ(maps.Get(6), maps.Get(7)->prev_map());
+ EXPECT_EQ(nullptr, maps.Get(7)->next_map());
+ EXPECT_EQ(nullptr, maps.Get(7)->GetPrevRealMap());
+ EXPECT_EQ(nullptr, maps.Get(7)->GetNextRealMap());
+}
+
+TEST(MapsTest, sort_empty) {
+ Maps maps;
+
+ maps.Sort();
+
+ EXPECT_EQ(0ULL, maps.Total());
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryBufferTest.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
index ffd6817..91b20dd 100644
--- a/libunwindstack/tests/MemoryBufferTest.cpp
+++ b/libunwindstack/tests/MemoryBufferTest.cpp
@@ -103,7 +103,14 @@
ASSERT_TRUE(memory_->Resize(256));
ASSERT_TRUE(memory_->Resize(1024));
+}
+extern "C" void __hwasan_init() __attribute__((weak));
+
+TEST_F(MemoryBufferTest, Resize_too_large) {
+ if (&__hwasan_init != 0) {
+ GTEST_SKIP() << "Tests fails hwasan allocation size too large check.";
+ }
ASSERT_FALSE(memory_->Resize(SIZE_MAX));
}
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
index 3bd3e4d..ae0c61b 100644
--- a/libunwindstack/tests/MemoryCacheTest.cpp
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -21,7 +21,7 @@
#include <gtest/gtest.h>
#include "MemoryCache.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
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 @@
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/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 2d4f141..7a374eb 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -21,8 +21,8 @@
#include <gtest/gtest.h>
-#include "MemoryFake.h"
#include "MemoryRange.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryRangesTest.cpp b/libunwindstack/tests/MemoryRangesTest.cpp
index e4e9fc4..cf3250a 100644
--- a/libunwindstack/tests/MemoryRangesTest.cpp
+++ b/libunwindstack/tests/MemoryRangesTest.cpp
@@ -20,8 +20,8 @@
#include <gtest/gtest.h>
-#include "MemoryFake.h"
#include "MemoryRange.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -86,4 +86,10 @@
}
}
+TEST_F(MemoryRangesTest, duplicate_last_addr) {
+ MemoryRanges ranges;
+ ASSERT_TRUE(ranges.Insert(new MemoryRange(nullptr, 0x1000, 0x2000, 0x1000)));
+ ASSERT_FALSE(ranges.Insert(new MemoryRange(nullptr, 0x2000, 0x1000, 0x2000)));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 621893b..ed58eb9 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -32,8 +32,9 @@
#include "MemoryRemote.h"
-#include "MemoryFake.h"
+#include "PidUtils.h"
#include "TestUtils.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -49,7 +50,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -59,7 +60,7 @@
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 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -88,7 +89,7 @@
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 @@
// 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 @@
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 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -171,7 +172,7 @@
ASSERT_EQ(0, munmap(src, pagesize));
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_overflow) {
@@ -184,7 +185,7 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -192,7 +193,7 @@
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 @@
ASSERT_LT(0, pid);
TestScopedPidReaper reap(pid);
- ASSERT_TRUE(TestAttach(pid));
+ ASSERT_TRUE(Attach(pid));
MemoryRemote remote(pid);
@@ -212,7 +213,7 @@
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 @@
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 @@
ASSERT_EQ(0xCC, dst[i]);
}
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
TEST(MemoryRemoteTest, read_munmap_hole) {
@@ -270,7 +271,7 @@
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 @@
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 @@
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 @@
ASSERT_EQ(sizeof(value), bytes);
ASSERT_EQ(0xfcfcfcfcU, value);
- ASSERT_TRUE(TestDetach(pid));
+ ASSERT_TRUE(Detach(pid));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
index 8a8eb24..268e536 100644
--- a/libunwindstack/tests/MemoryTest.cpp
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -24,7 +24,7 @@
#include <unwindstack/Memory.h>
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/MemoryThreadCacheTest.cpp b/libunwindstack/tests/MemoryThreadCacheTest.cpp
index 2c499bf..0ecfba9 100644
--- a/libunwindstack/tests/MemoryThreadCacheTest.cpp
+++ b/libunwindstack/tests/MemoryThreadCacheTest.cpp
@@ -22,7 +22,7 @@
#include <gtest/gtest.h>
#include "MemoryCache.h"
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -164,17 +164,23 @@
}
}
-TEST_F(MemoryThreadCacheTest, read_uncached_due_to_error) {
+static void ExhaustPthreadKeys(std::vector<pthread_key_t>* keys) {
// Use up all of the keys to force the next attempt to create one to fail.
static constexpr size_t kMaxKeysToCreate = 10000;
- std::vector<pthread_key_t> keys(kMaxKeysToCreate);
+ keys->resize(kMaxKeysToCreate);
for (size_t i = 0; i < kMaxKeysToCreate; i++) {
- if (pthread_key_create(&keys[i], nullptr) != 0) {
- keys.resize(i);
+ if (pthread_key_create(&(*keys)[i], nullptr) != 0) {
+ keys->resize(i);
break;
}
}
- ASSERT_NE(kMaxKeysToCreate, keys.size()) << "Cannot exist pthread keys.";
+ ASSERT_NE(0U, keys->size()) << "No keys created.";
+ ASSERT_LT(keys->size(), kMaxKeysToCreate) << "Cannot use up pthread keys.";
+}
+
+TEST_F(MemoryThreadCacheTest, read_uncached_due_to_error) {
+ std::vector<pthread_key_t> keys;
+ ASSERT_NO_FATAL_FAILURE(ExhaustPthreadKeys(&keys));
MemoryFake* fake = new MemoryFake;
MemoryThreadCache memory(fake);
@@ -199,4 +205,17 @@
}
}
+TEST_F(MemoryThreadCacheTest, clear_cache_when_no_cache) {
+ std::vector<pthread_key_t> keys;
+ ASSERT_NO_FATAL_FAILURE(ExhaustPthreadKeys(&keys));
+
+ MemoryFake* fake = new MemoryFake;
+ MemoryThreadCache memory(fake);
+ memory.Clear();
+
+ for (pthread_key_t& key : keys) {
+ pthread_key_delete(key);
+ }
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsRemoteTest.cpp b/libunwindstack/tests/RegsRemoteTest.cpp
new file mode 100644
index 0000000..2427501
--- /dev/null
+++ b/libunwindstack/tests/RegsRemoteTest.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Regs.h>
+
+#include "PidUtils.h"
+
+namespace unwindstack {
+
+class RegsRemoteTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ if ((pid_ = fork()) == 0) {
+ volatile bool run = true;
+ while (!run) {
+ }
+ exit(1);
+ }
+ ASSERT_TRUE(pid_ != -1);
+ ASSERT_TRUE(Attach(pid_));
+ }
+
+ void TearDown() override {
+ if (pid_ == -1) {
+ return;
+ }
+ EXPECT_TRUE(Detach(pid_));
+ kill(pid_, SIGKILL);
+ waitpid(pid_, nullptr, 0);
+ }
+
+ pid_t pid_ = -1;
+};
+
+TEST_F(RegsRemoteTest, remote_get) {
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid_));
+#if defined(__arm__)
+ ASSERT_EQ(ARCH_ARM, regs->Arch());
+#elif defined(__aarch64__)
+ ASSERT_EQ(ARCH_ARM64, regs->Arch());
+#elif defined(__i386__)
+ ASSERT_EQ(ARCH_X86, regs->Arch());
+#elif defined(__x86_64__)
+ ASSERT_EQ(ARCH_X86_64, regs->Arch());
+#else
+ ASSERT_EQ(nullptr, regs.get());
+#endif
+}
+
+TEST_F(RegsRemoteTest, remote_get_arch) {
+#if defined(__arm__)
+ ASSERT_EQ(ARCH_ARM, Regs::RemoteGetArch(pid_));
+#elif defined(__aarch64__)
+ ASSERT_EQ(ARCH_ARM64, Regs::RemoteGetArch(pid_));
+#elif defined(__i386__)
+ ASSERT_EQ(ARCH_X86, Regs::RemoteGetArch(pid_));
+#elif defined(__x86_64__)
+ ASSERT_EQ(ARCH_X86_64, Regs::RemoteGetArch(pid_));
+#else
+ ASSERT_EQ(ARCH_NONE, Regs::RemoteGetArch(pid_));
+#endif
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index eac12ca..77e930f 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -32,7 +32,7 @@
#include <unwindstack/RegsX86.h>
#include <unwindstack/RegsX86_64.h>
-#include "MemoryFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 716a847..7c10d9e 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -16,6 +16,8 @@
#include <stdint.h>
+#include <memory>
+
#include <gtest/gtest.h>
#include <unwindstack/Elf.h>
@@ -29,8 +31,8 @@
#include <unwindstack/RegsMips64.h>
#include "ElfFake.h"
-#include "MemoryFake.h"
#include "RegsFake.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
@@ -169,27 +171,27 @@
}
TEST_F(RegsTest, elf_invalid) {
- MapInfo map_info(nullptr, nullptr, 0x1000, 0x2000, 0, 0, "");
+ auto map_info = MapInfo::Create(0x1000, 0x2000, 0, 0, "");
Elf* invalid_elf = new Elf(nullptr);
- map_info.set_elf(invalid_elf);
+ map_info->set_elf(invalid_elf);
- EXPECT_EQ(0x500U, invalid_elf->GetRelPc(0x1500, &map_info));
+ EXPECT_EQ(0x500U, invalid_elf->GetRelPc(0x1500, map_info.get()));
EXPECT_EQ(2U, GetPcAdjustment(0x500U, invalid_elf, ARCH_ARM));
EXPECT_EQ(2U, GetPcAdjustment(0x511U, invalid_elf, ARCH_ARM));
- EXPECT_EQ(0x600U, invalid_elf->GetRelPc(0x1600, &map_info));
+ EXPECT_EQ(0x600U, invalid_elf->GetRelPc(0x1600, map_info.get()));
EXPECT_EQ(4U, GetPcAdjustment(0x600U, invalid_elf, ARCH_ARM64));
- EXPECT_EQ(0x700U, invalid_elf->GetRelPc(0x1700, &map_info));
+ EXPECT_EQ(0x700U, invalid_elf->GetRelPc(0x1700, map_info.get()));
EXPECT_EQ(1U, GetPcAdjustment(0x700U, invalid_elf, ARCH_X86));
- EXPECT_EQ(0x800U, invalid_elf->GetRelPc(0x1800, &map_info));
+ EXPECT_EQ(0x800U, invalid_elf->GetRelPc(0x1800, map_info.get()));
EXPECT_EQ(1U, GetPcAdjustment(0x800U, invalid_elf, ARCH_X86_64));
- EXPECT_EQ(0x900U, invalid_elf->GetRelPc(0x1900, &map_info));
+ EXPECT_EQ(0x900U, invalid_elf->GetRelPc(0x1900, map_info.get()));
EXPECT_EQ(8U, GetPcAdjustment(0x900U, invalid_elf, ARCH_MIPS));
- EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(0x1a00, &map_info));
+ EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(0x1a00, map_info.get()));
EXPECT_EQ(8U, GetPcAdjustment(0xa00U, invalid_elf, ARCH_MIPS64));
}
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index 89147a2..c4f6f9b 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -34,8 +34,8 @@
#include <unwindstack/Memory.h>
-#include "MemoryFake.h"
#include "Symbols.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/TestLocal.cpp b/libunwindstack/tests/TestLocal.cpp
index fa0baff..25bd42f 100644
--- a/libunwindstack/tests/TestLocal.cpp
+++ b/libunwindstack/tests/TestLocal.cpp
@@ -14,26 +14,30 @@
* limitations under the License.
*/
-#include <unwindstack/LocalUnwinder.h>
+#include <stdint.h>
+#include <stdlib.h>
-#include <vector>
+#include "TestUtils.h"
-extern "C" void TestlibLevel4(void* unwinder_data, void* frame_data) {
- unwindstack::LocalUnwinder* unwinder =
- reinterpret_cast<unwindstack::LocalUnwinder*>(unwinder_data);
- std::vector<unwindstack::LocalFrameData>* frame_info =
- reinterpret_cast<std::vector<unwindstack::LocalFrameData>*>(frame_data);
- unwinder->Unwind(frame_info, 256);
+// 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.
+//
+// In an effort to defend against the compiler:
+// 1. The loop iteration variable is volatile.
+// 2. A call to this function should be wrapped in TestUtils::DoNotOptimize().
+extern "C" int BusyWait() {
+ for (size_t i = 0; i < 1000000;) {
+ unwindstack::DoNotOptimize(i++);
+ }
+ return 0;
}
-extern "C" void TestlibLevel3(void* unwinder_data, void* frame_data) {
- TestlibLevel4(unwinder_data, frame_data);
-}
-
-extern "C" void TestlibLevel2(void* unwinder_data, void* frame_data) {
- TestlibLevel3(unwinder_data, frame_data);
-}
-
-extern "C" void TestlibLevel1(void* unwinder_data, void* frame_data) {
- TestlibLevel2(unwinder_data, frame_data);
+// 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 ecc2d45..1f748ba 100644
--- a/libunwindstack/tests/TestUtils.cpp
+++ b/libunwindstack/tests/TestUtils.cpp
@@ -14,11 +14,16 @@
* limitations under the License.
*/
+#include <dlfcn.h>
#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) {
@@ -42,4 +47,17 @@
}
}
+void* GetTestLibHandle() {
+ std::string testlib(testing::internal::GetArgvs()[0]);
+ auto const value = testlib.find_last_of('/');
+ if (value != std::string::npos) {
+ testlib = testlib.substr(0, value + 1);
+ } else {
+ testlib = "";
+ }
+ testlib += "libunwindstack_local.so";
+
+ return dlopen(testlib.c_str(), RTLD_NOW);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/TestUtils.h b/libunwindstack/tests/TestUtils.h
index 0685006..19cd693 100644
--- a/libunwindstack/tests/TestUtils.h
+++ b/libunwindstack/tests/TestUtils.h
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
-#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#pragma once
#include <signal.h>
-#include <sys/ptrace.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <unistd.h>
namespace unwindstack {
@@ -37,34 +35,15 @@
pid_t pid_;
};
-inline bool TestQuiescePid(pid_t pid) {
- siginfo_t si;
- bool ready = false;
- // Wait for up to 5 seconds.
- for (size_t i = 0; i < 5000; i++) {
- if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
- ready = true;
- break;
- }
- usleep(1000);
- }
- return ready;
-}
-
-inline bool TestAttach(pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
- return false;
- }
-
- return TestQuiescePid(pid);
-}
-
-inline bool TestDetach(pid_t pid) {
- return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-}
-
void TestCheckForLeaks(void (*unwind_func)(void*), void* data);
-} // namespace unwindstack
+void* GetTestLibHandle();
-#endif // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+// TODO(b/148307629): Once we incorporate google benchmark library into
+// GoogleTest, we can call benchmark::DoNotOptimize here instead.
+template <class Tp>
+static inline void DoNotOptimize(Tp const& value) {
+ asm volatile("" : : "r,m"(value) : "memory");
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index c834d0d..0d6fa1d 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -14,218 +14,108 @@
* limitations under the License.
*/
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <sys/mman.h>
-#include <unistd.h>
#include <gtest/gtest.h>
+#include <cstddef>
+#include <fstream>
+#include <memory>
+#include <sstream>
#include <string>
-#include <unordered_map>
-#include <vector>
-#include <android-base/file.h>
-
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/MachineArm.h>
-#include <unwindstack/MachineArm64.h>
-#include <unwindstack/MachineX86.h>
-#include <unwindstack/MachineX86_64.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/RegsArm.h>
+#include <unwindstack/Arch.h>
+#include <unwindstack/Memory.h>
#include <unwindstack/RegsArm64.h>
-#include <unwindstack/RegsX86.h>
-#include <unwindstack/RegsX86_64.h>
#include <unwindstack/Unwinder.h>
-#include "ElfTestUtils.h"
-#include "MemoryFake.h"
-#include "MemoryOffline.h"
#include "TestUtils.h"
+#include "utils/MemoryFake.h"
+#include "utils/OfflineUnwindUtils.h"
+// This collection of tests exercises Unwinder::Unwind for offline unwinds.
+//
+// See `libunwindstack/utils/OfflineUnwindUtils.h` for more info on offline unwinds
+// and b/192012600 for additional information regarding offline unwind benchmarks.
namespace unwindstack {
-
-static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
- MemoryOffline* memory = new MemoryOffline;
- ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
- parts->Add(memory);
-}
+namespace {
class UnwindOfflineTest : public ::testing::Test {
+ public:
+ bool GetExpectedSamplesFrameInfo(
+ std::string* expected_frame_info, std::string* error_msg,
+ const std::string& sample_name = OfflineUnwindUtils::kSingleSample) {
+ const std::string* a_frame_info_path = offline_utils_.GetFrameInfoFilepath(sample_name);
+ if (a_frame_info_path == nullptr) {
+ std::stringstream err_stream;
+ err_stream << "Unable to get frame info filepath for invalid sample name " << sample_name
+ << ".\n";
+ *error_msg = err_stream.str();
+ return false;
+ }
+
+ std::ifstream in(*a_frame_info_path);
+ std::stringstream buffer;
+ buffer << in.rdbuf();
+ *expected_frame_info = buffer.str();
+ return true;
+ }
+
+ void ConsecutiveUnwindTest(const std::vector<UnwindSampleInfo>& sample_infos) {
+ std::string error_msg;
+ if (!offline_utils_.Init(sample_infos, &error_msg)) FAIL() << error_msg;
+
+ for (const auto& sample_info : sample_infos) {
+ const std::string& sample_name = sample_info.offline_files_dir;
+ // Need to change to sample directory for Unwinder to properly init ELF objects.
+ // See more info at OfflineUnwindUtils::ChangeToSampleDirectory.
+ if (!offline_utils_.ChangeToSampleDirectory(&error_msg, sample_name)) FAIL() << error_msg;
+
+ Unwinder unwinder =
+ Unwinder(128, offline_utils_.GetMaps(sample_name), offline_utils_.GetRegs(sample_name),
+ offline_utils_.GetProcessMemory(sample_name));
+ if (sample_info.memory_flag == ProcessMemoryFlag::kIncludeJitMemory) {
+ unwinder.SetJitDebug(offline_utils_.GetJitDebug(sample_name));
+ }
+ unwinder.Unwind();
+
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg, sample_name))
+ FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg, sample_name))
+ FAIL() << error_msg;
+
+ std::string actual_frame_info = DumpFrames(unwinder);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << actual_frame_info;
+ EXPECT_EQ(expected_frame_info, actual_frame_info);
+ }
+ }
+
protected:
- void TearDown() override {
- if (cwd_ != nullptr) {
- ASSERT_EQ(0, chdir(cwd_));
- }
- free(cwd_);
- }
+ void TearDown() override { offline_utils_.ReturnToCurrentWorkingDirectory(); }
- void Init(const char* file_dir, ArchEnum arch, bool add_stack = true) {
- dir_ = TestGetFileDirectory() + "offline/" + file_dir;
-
- std::string data;
- ASSERT_TRUE(android::base::ReadFileToString((dir_ + "maps.txt"), &data));
-
- maps_.reset(new BufferMaps(data.c_str()));
- ASSERT_TRUE(maps_->Parse());
-
- if (add_stack) {
- std::string stack_name(dir_ + "stack.data");
- struct stat st;
- if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
- std::unique_ptr<MemoryOffline> stack_memory(new MemoryOffline);
- ASSERT_TRUE(stack_memory->Init((dir_ + "stack.data").c_str(), 0));
- process_memory_.reset(stack_memory.release());
- } else {
- std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
- for (size_t i = 0;; i++) {
- stack_name = dir_ + "stack" + std::to_string(i) + ".data";
- if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
- ASSERT_TRUE(i != 0) << "No stack data files found.";
- break;
- }
- AddMemory(stack_name, stack_memory.get());
- }
- process_memory_.reset(stack_memory.release());
- }
- }
-
- switch (arch) {
- case ARCH_ARM: {
- RegsArm* regs = new RegsArm;
- regs_.reset(regs);
- ReadRegs<uint32_t>(regs, arm_regs_);
- break;
- }
- case ARCH_ARM64: {
- RegsArm64* regs = new RegsArm64;
- regs_.reset(regs);
- ReadRegs<uint64_t>(regs, arm64_regs_);
- break;
- }
- case ARCH_X86: {
- RegsX86* regs = new RegsX86;
- regs_.reset(regs);
- ReadRegs<uint32_t>(regs, x86_regs_);
- break;
- }
- case ARCH_X86_64: {
- RegsX86_64* regs = new RegsX86_64;
- regs_.reset(regs);
- ReadRegs<uint64_t>(regs, x86_64_regs_);
- break;
- }
- default:
- ASSERT_TRUE(false) << "Unknown arch " << std::to_string(arch);
- }
- cwd_ = getcwd(nullptr, 0);
- // Make dir_ an absolute directory.
- if (dir_.empty() || dir_[0] != '/') {
- dir_ = std::string(cwd_) + '/' + dir_;
- }
- ASSERT_EQ(0, chdir(dir_.c_str()));
-
- if (process_memory_ == nullptr) {
- process_memory_.reset(new MemoryFake);
- }
- }
-
- template <typename AddressType>
- void ReadRegs(RegsImpl<AddressType>* regs,
- const std::unordered_map<std::string, uint32_t>& name_to_reg) {
- FILE* fp = fopen((dir_ + "regs.txt").c_str(), "r");
- ASSERT_TRUE(fp != nullptr);
- while (!feof(fp)) {
- uint64_t value;
- char reg_name[100];
- ASSERT_EQ(2, fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value));
- std::string name(reg_name);
- if (!name.empty()) {
- // Remove the : from the end.
- name.resize(name.size() - 1);
- }
- auto entry = name_to_reg.find(name);
- ASSERT_TRUE(entry != name_to_reg.end()) << "Unknown register named " << name;
- (*regs)[entry->second] = value;
- }
- fclose(fp);
- }
-
- static std::unordered_map<std::string, uint32_t> arm_regs_;
- static std::unordered_map<std::string, uint32_t> arm64_regs_;
- static std::unordered_map<std::string, uint32_t> x86_regs_;
- static std::unordered_map<std::string, uint32_t> x86_64_regs_;
-
- char* cwd_ = nullptr;
- std::string dir_;
- std::unique_ptr<Regs> regs_;
- std::unique_ptr<Maps> maps_;
- std::shared_ptr<Memory> process_memory_;
+ OfflineUnwindUtils offline_utils_;
};
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm_regs_ = {
- {"r0", ARM_REG_R0}, {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2}, {"r3", ARM_REG_R3},
- {"r4", ARM_REG_R4}, {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6}, {"r7", ARM_REG_R7},
- {"r8", ARM_REG_R8}, {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
- {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR}, {"pc", ARM_REG_PC},
-};
-
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::arm64_regs_ = {
- {"x0", ARM64_REG_R0}, {"x1", ARM64_REG_R1}, {"x2", ARM64_REG_R2},
- {"x3", ARM64_REG_R3}, {"x4", ARM64_REG_R4}, {"x5", ARM64_REG_R5},
- {"x6", ARM64_REG_R6}, {"x7", ARM64_REG_R7}, {"x8", ARM64_REG_R8},
- {"x9", ARM64_REG_R9}, {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
- {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
- {"x15", ARM64_REG_R15}, {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
- {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
- {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
- {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
- {"x27", ARM64_REG_R27}, {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
- {"sp", ARM64_REG_SP}, {"lr", ARM64_REG_LR}, {"pc", ARM64_REG_PC},
- {"pst", ARM64_REG_PSTATE},
-};
-
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_regs_ = {
- {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
- {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
- {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
-};
-
-std::unordered_map<std::string, uint32_t> UnwindOfflineTest::x86_64_regs_ = {
- {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
- {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8}, {"r9", X86_64_REG_R9},
- {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
- {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
- {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
- {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
-};
-
-static std::string DumpFrames(Unwinder& unwinder) {
- std::string str;
- for (size_t i = 0; i < unwinder.NumFrames(); i++) {
- str += unwinder.FormatFrame(i) + "\n";
- }
- return str;
-}
-
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "straddle_arm/", .arch = ARCH_ARM}, &error_msg))
+ FAIL() << error_msg;
- std::unique_ptr<Regs> regs_copy(regs_->Clone());
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Regs* regs = offline_utils_.GetRegs();
+ std::unique_ptr<Regs> regs_copy(regs->Clone());
+ Unwinder unwinder(128, offline_utils_.GetMaps(), regs, offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0001a9f8 libc.so (abort+64)\n"
- " #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6)\n"
- " #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748)\n"
- " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
EXPECT_EQ(0xe9c866f8U, unwinder.frames()[0].sp);
EXPECT_EQ(0xf2da0a1bU, unwinder.frames()[1].pc);
@@ -241,7 +131,7 @@
unwinder.Unwind();
frame_info = DumpFrames(unwinder);
- ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 0001a9f8 libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
" #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
@@ -253,20 +143,23 @@
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("gnu_debugdata_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "gnu_debugdata_arm/", .arch = ARCH_ARM},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(2U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0006dc49 libandroid_runtime.so "
- "(android::AndroidRuntime::javaThreadShell(void*)+80)\n"
- " #01 pc 0006dce5 libandroid_runtime.so "
- "(android::AndroidRuntime::javaCreateThreadEtc(int (*)(void*), void*, char const*, int, "
- "unsigned int, void**))\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
EXPECT_EQ(0xf1f6dce5U, unwinder.frames()[1].pc);
@@ -274,23 +167,23 @@
}
TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("straddle_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "straddle_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0000000000429fd8 libunwindstack_test (SignalInnerFunction+24)\n"
- " #01 pc 000000000042a078 libunwindstack_test (SignalMiddleFunction+8)\n"
- " #02 pc 000000000042a08c libunwindstack_test (SignalOuterFunction+8)\n"
- " #03 pc 000000000042d8fc libunwindstack_test "
- "(unwindstack::RemoteThroughSignal(int, unsigned int)+20)\n"
- " #04 pc 000000000042d8d8 libunwindstack_test "
- "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+32)\n"
- " #05 pc 0000000000455d70 libunwindstack_test (testing::Test::Run()+392)\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
EXPECT_EQ(0x64d09d5078U, unwinder.frames()[1].pc);
@@ -306,166 +199,26 @@
}
TEST_F(UnwindOfflineTest, jit_debug_x86) {
- ASSERT_NO_FATAL_FAILURE(Init("jit_debug_x86/", ARCH_X86));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "jit_debug_x86/",
+ .arch = ARCH_X86,
+ .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+ &error_msg))
+ FAIL() << error_msg;
- MemoryOfflineParts* memory = new MemoryOfflineParts;
- AddMemory(dir_ + "descriptor.data", memory);
- AddMemory(dir_ + "stack.data", memory);
- for (size_t i = 0; i < 7; i++) {
- AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
- AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
- }
- process_memory_.reset(memory);
-
- std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs_->Arch(), process_memory_);
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(jit_debug.get());
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
+ unwinder.SetJitDebug(offline_utils_.GetJitDebug());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00068fb8 libarttestd.so (art::CauseSegfault()+72)\n"
- " #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
- " #02 pc 000021a8 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
- "boolean)+136)\n"
- " #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
- " #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
- " #05 pc 00146ab5 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+885)\n"
- " #06 pc 0039cf0d libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
- " #07 pc 00392552 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+354)\n"
- " #08 pc 0039399a libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+234)\n"
- " #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
- " #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #11 pc 0000fe03 anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
- " #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
- " #13 pc 00146ab5 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+885)\n"
- " #14 pc 0039cf0d libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
- " #15 pc 00392552 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+354)\n"
- " #16 pc 0039399a libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+234)\n"
- " #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
- " #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #19 pc 0000fd3b anonymous:ee74c000 (int Main.compare(java.lang.Object, "
- "java.lang.Object)+107)\n"
- " #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
- " #21 pc 00146ab5 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+885)\n"
- " #22 pc 0039cf0d libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
- " #23 pc 00392552 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+354)\n"
- " #24 pc 0039399a libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+234)\n"
- " #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
- " #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #27 pc 0000fbdb anonymous:ee74c000 (int "
- "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
- "java.util.Comparator)+331)\n"
- " #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
- " #29 pc 00146acb libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+907)\n"
- " #30 pc 0039cf0d libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
- " #31 pc 00392552 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+354)\n"
- " #32 pc 0039399a libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+234)\n"
- " #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
- " #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #35 pc 0000f624 anonymous:ee74c000 (boolean Main.foo()+164)\n"
- " #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
- " #37 pc 00146ab5 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+885)\n"
- " #38 pc 0039cf0d libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
- " #39 pc 00392552 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+354)\n"
- " #40 pc 0039399a libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+234)\n"
- " #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
- " #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #43 pc 0000eedb anonymous:ee74c000 (void Main.runPrimary()+59)\n"
- " #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
- " #45 pc 00146ab5 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+885)\n"
- " #46 pc 0039cf0d libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
- " #47 pc 00392552 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+354)\n"
- " #48 pc 0039399a libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+234)\n"
- " #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
- " #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #51 pc 0000ac21 anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
- " #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
- " #53 pc 00146acb libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+907)\n"
- " #54 pc 0039cf0d libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+653)\n"
- " #55 pc 00392552 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+354)\n"
- " #56 pc 0039399a libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+234)\n"
- " #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
- " #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
- " #60 pc 00146acb libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+907)\n"
- " #61 pc 005aac95 libartd.so "
- "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
- "art::ArgArray*, art::JValue*, char const*)+85)\n"
- " #62 pc 005aab5a libartd.so "
- "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
- "_jmethodID*, char*)+362)\n"
- " #63 pc 0048a3dd libartd.so "
- "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+125)\n"
- " #64 pc 0018448c libartd.so "
- "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, char*, "
- "art::Primitive::Type, art::InvokeType)+1964)\n"
- " #65 pc 0017cf06 libartd.so "
- "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, char*)+70)\n"
- " #66 pc 00001d8c dalvikvm32 "
- "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+60)\n"
- " #67 pc 00001a80 dalvikvm32 (main+1312)\n"
- " #68 pc 00018275 libc.so\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xeb89bfb8U, unwinder.frames()[0].pc);
EXPECT_EQ(0xffeb5280U, unwinder.frames()[0].sp);
EXPECT_EQ(0xeb89af00U, unwinder.frames()[1].pc);
@@ -607,174 +360,26 @@
}
TEST_F(UnwindOfflineTest, jit_debug_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "jit_debug_arm/",
+ .arch = ARCH_ARM,
+ .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+ &error_msg))
+ FAIL() << error_msg;
- MemoryOfflineParts* memory = new MemoryOfflineParts;
- AddMemory(dir_ + "descriptor.data", memory);
- AddMemory(dir_ + "descriptor1.data", memory);
- AddMemory(dir_ + "stack.data", memory);
- for (size_t i = 0; i < 7; i++) {
- AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
- AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
- }
- process_memory_.reset(memory);
-
- std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs_->Arch(), process_memory_);
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(jit_debug.get());
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
+ unwinder.SetJitDebug(offline_utils_.GetJitDebug());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
- " #01 pc 0000212d 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
- "boolean)+92)\n"
- " #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
- " #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #04 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
- " #05 pc 000bf7a9 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+864)\n"
- " #06 pc 00247833 libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
- " #07 pc 0022e935 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+244)\n"
- " #08 pc 0022f71d libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+128)\n"
- " #09 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
- " #10 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
- " #11 pc 00011c31 anonymous:e2796000 (int Main.compare(Main, Main)+64)\n"
- " #12 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #13 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
- " #14 pc 000bf7a9 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+864)\n"
- " #15 pc 00247833 libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
- " #16 pc 0022e935 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+244)\n"
- " #17 pc 0022f71d libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+128)\n"
- " #18 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
- " #19 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
- " #20 pc 00011b77 anonymous:e2796000 (int Main.compare(java.lang.Object, "
- "java.lang.Object)+118)\n"
- " #21 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #22 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
- " #23 pc 000bf7a9 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+864)\n"
- " #24 pc 00247833 libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
- " #25 pc 0022e935 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+244)\n"
- " #26 pc 0022f71d libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+128)\n"
- " #27 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
- " #28 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
- " #29 pc 00011a29 anonymous:e2796000 (int "
- "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
- "java.util.Comparator)+304)\n"
- " #30 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #31 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
- " #32 pc 000bf7bb libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+882)\n"
- " #33 pc 00247833 libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
- " #34 pc 0022e935 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+244)\n"
- " #35 pc 0022f71d libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+128)\n"
- " #36 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
- " #37 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
- " #38 pc 0001139b anonymous:e2796000 (boolean Main.foo()+178)\n"
- " #39 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #40 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
- " #41 pc 000bf7a9 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+864)\n"
- " #42 pc 00247833 libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
- " #43 pc 0022e935 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+244)\n"
- " #44 pc 0022f71d libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+128)\n"
- " #45 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
- " #46 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
- " #47 pc 00010aa7 anonymous:e2796000 (void Main.runPrimary()+70)\n"
- " #48 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #49 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
- " #50 pc 000bf7a9 libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+864)\n"
- " #51 pc 00247833 libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
- " #52 pc 0022e935 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+244)\n"
- " #53 pc 0022f71d libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+128)\n"
- " #54 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
- " #55 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
- " #56 pc 0000ba99 anonymous:e2796000 (void Main.main(java.lang.String[])+144)\n"
- " #57 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #58 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
- " #59 pc 000bf7bb libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+882)\n"
- " #60 pc 00247833 libartd.so "
- "(art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, "
- "art::ShadowFrame*, unsigned short, art::JValue*)+382)\n"
- " #61 pc 0022e935 libartd.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+244)\n"
- " #62 pc 0022f71d libartd.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+128)\n"
- " #63 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
- " #64 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
- " #65 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
- " #66 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
- " #67 pc 000bf7bb libartd.so "
- "(art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char "
- "const*)+882)\n"
- " #68 pc 003b292d libartd.so "
- "(art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, "
- "art::ArgArray*, art::JValue*, char const*)+52)\n"
- " #69 pc 003b26c3 libartd.so "
- "(art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, "
- "_jmethodID*, std::__va_list)+210)\n"
- " #70 pc 00308411 libartd.so "
- "(art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+76)\n"
- " #71 pc 000e6a9f libartd.so "
- "(art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, "
- "std::__va_list, art::Primitive::Type, art::InvokeType)+1486)\n"
- " #72 pc 000e19b9 libartd.so "
- "(art::CheckJNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+40)\n"
- " #73 pc 0000159f dalvikvm32 "
- "(_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+30)\n"
- " #74 pc 00001349 dalvikvm32 (main+896)\n"
- " #75 pc 000850c9 libc.so\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xdfe66a5eU, unwinder.frames()[0].pc);
EXPECT_EQ(0xff85d180U, unwinder.frames()[0].sp);
EXPECT_EQ(0xe044712dU, unwinder.frames()[1].pc);
@@ -930,12 +535,17 @@
}
struct LeakType {
- LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory)
- : maps(maps), regs(regs), process_memory(process_memory) {}
+ LeakType(Maps* maps, Regs* regs, std::shared_ptr<Memory>& process_memory,
+ size_t expected_num_frames)
+ : maps(maps),
+ regs(regs),
+ process_memory(process_memory),
+ expected_num_frames(expected_num_frames) {}
Maps* maps;
Regs* regs;
std::shared_ptr<Memory>& process_memory;
+ size_t expected_num_frames;
};
static void OfflineUnwind(void* data) {
@@ -947,23 +557,23 @@
Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
unwinder.SetJitDebug(jit_debug.get());
unwinder.Unwind();
- ASSERT_EQ(76U, unwinder.NumFrames());
+ ASSERT_EQ(leak_data->expected_num_frames, unwinder.NumFrames());
}
TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
- ASSERT_NO_FATAL_FAILURE(Init("jit_debug_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "jit_debug_arm/",
+ .arch = ARCH_ARM,
+ .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+ &error_msg))
+ FAIL() << error_msg;
- MemoryOfflineParts* memory = new MemoryOfflineParts;
- AddMemory(dir_ + "descriptor.data", memory);
- AddMemory(dir_ + "descriptor1.data", memory);
- AddMemory(dir_ + "stack.data", memory);
- for (size_t i = 0; i < 7; i++) {
- AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
- AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
- }
- process_memory_.reset(memory);
+ std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
- LeakType data(maps_.get(), regs_.get(), process_memory_);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ LeakType data(offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory,
+ expected_num_frames);
TestCheckForLeaks(OfflineUnwind, &data);
}
@@ -971,20 +581,23 @@
// fallback to iterating over the cies/fdes and ignore the eh_frame_hdr.
// No .gnu_debugdata section in the elf file, so no symbols.
TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "bad_eh_frame_hdr_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0000000000000550 waiter64\n"
- " #01 pc 0000000000000568 waiter64\n"
- " #02 pc 000000000000057c waiter64\n"
- " #03 pc 0000000000000590 waiter64\n"
- " #04 pc 00000000000a8e98 libc.so (__libc_init+88)\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x60a9fdf550U, unwinder.frames()[0].pc);
EXPECT_EQ(0x7fdd141990U, unwinder.frames()[0].sp);
EXPECT_EQ(0x60a9fdf568U, unwinder.frames()[1].pc);
@@ -1000,20 +613,23 @@
// The elf has bad eh_frame unwind information for the pcs. If eh_frame
// is used first, the unwind will not match the expected output.
TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
- ASSERT_NO_FATAL_FAILURE(Init("debug_frame_first_x86/", ARCH_X86));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "debug_frame_first_x86/", .arch = ARCH_X86},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00000685 waiter (call_level3+53)\n"
- " #01 pc 000006b7 waiter (call_level2+23)\n"
- " #02 pc 000006d7 waiter (call_level1+23)\n"
- " #03 pc 000006f7 waiter (main+23)\n"
- " #04 pc 00018275 libc.so\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x56598685U, unwinder.frames()[0].pc);
EXPECT_EQ(0xffcf9e38U, unwinder.frames()[0].sp);
EXPECT_EQ(0x565986b7U, unwinder.frames()[1].pc);
@@ -1028,20 +644,23 @@
// Make sure that a pc that is at the beginning of an fde unwinds correctly.
TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
- ASSERT_NO_FATAL_FAILURE(Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "eh_frame_hdr_begin_x86_64/", .arch = ARCH_X86_64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(5U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0000000000000a80 unwind_test64 (calling3)\n"
- " #01 pc 0000000000000dd9 unwind_test64 (calling2+633)\n"
- " #02 pc 000000000000121e unwind_test64 (calling1+638)\n"
- " #03 pc 00000000000013ed unwind_test64 (main+13)\n"
- " #04 pc 00000000000202b0 libc.so\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x561550b17a80U, unwinder.frames()[0].pc);
EXPECT_EQ(0x7ffcc8596ce8U, unwinder.frames()[0].sp);
EXPECT_EQ(0x561550b17dd9U, unwinder.frames()[1].pc);
@@ -1055,71 +674,26 @@
}
TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("art_quick_osr_stub_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "art_quick_osr_stub_arm/",
+ .arch = ARCH_ARM,
+ .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+ &error_msg))
+ FAIL() << error_msg;
- MemoryOfflineParts* memory = new MemoryOfflineParts;
- AddMemory(dir_ + "descriptor.data", memory);
- AddMemory(dir_ + "stack.data", memory);
- for (size_t i = 0; i < 2; i++) {
- AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
- AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
- }
- process_memory_.reset(memory);
-
- std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs_->Arch(), process_memory_);
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(jit_debug.get());
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
+ unwinder.SetJitDebug(offline_utils_.GetJitDebug());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(25U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0000c788 <anonymous:d0250000> "
- "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
- " #01 pc 0000cdd5 <anonymous:d0250000> "
- "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
- " #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
- " #03 pc 002657a5 libart.so "
- "(art::jit::Jit::MaybeDoOnStackReplacement(art::Thread*, art::ArtMethod*, unsigned int, int, "
- "art::JValue*)+876)\n"
- " #04 pc 004021a7 libart.so (MterpMaybeDoOnStackReplacement+86)\n"
- " #05 pc 00412474 libart.so (ExecuteMterpImpl+66164)\n"
- " #06 pc cd8365b0 <unknown>\n" // symbol in dex file
- " #07 pc 001d7f1b libart.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+374)\n"
- " #08 pc 001dc593 libart.so "
- "(art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, "
- "art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+154)\n"
- " #09 pc 001f4d01 libart.so "
- "(bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, "
- "art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+732)\n"
- " #10 pc 003fe427 libart.so (MterpInvokeInterface+1354)\n"
- " #11 pc 00405b94 libart.so (ExecuteMterpImpl+14740)\n"
- " #12 pc 7004873e <unknown>\n" // symbol in dex file
- " #13 pc 001d7f1b libart.so "
- "(art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, "
- "art::ShadowFrame&, art::JValue, bool)+374)\n"
- " #14 pc 001dc4d5 libart.so "
- "(art::interpreter::EnterInterpreterFromEntryPoint(art::Thread*, art::CodeItemDataAccessor "
- "const&, art::ShadowFrame*)+92)\n"
- " #15 pc 003f25ab libart.so (artQuickToInterpreterBridge+970)\n"
- " #16 pc 00417aff libart.so (art_quick_to_interpreter_bridge+30)\n"
- " #17 pc 00413575 libart.so (art_quick_invoke_stub_internal+68)\n"
- " #18 pc 00418531 libart.so (art_quick_invoke_stub+236)\n"
- " #19 pc 000b468d libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned "
- "int, art::JValue*, char const*)+136)\n"
- " #20 pc 00362f49 libart.so "
- "(art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable "
- "const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char "
- "const*)+52)\n"
- " #21 pc 00363cd9 libart.so "
- "(art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, "
- "_jobject*, _jmethodID*, jvalue*)+332)\n"
- " #22 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)\n"
- " #23 pc 00062925 libc.so (__pthread_start(void*)+22)\n"
- " #24 pc 0001de39 libc.so (__start_thread+24)\n",
- frame_info);
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
@@ -1173,31 +747,28 @@
}
TEST_F(UnwindOfflineTest, jit_map_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("jit_map_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "jit_map_arm/", .arch = ARCH_ARM}, &error_msg))
+ FAIL() << error_msg;
- maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
- "jit_map0.so", 0);
- maps_->Add(0xd025cd98, 0xd025cff4, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
- "jit_map1.so", 0);
- maps_->Sort();
+ Maps* maps = offline_utils_.GetMaps();
+ maps->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+ "jit_map0.so", 0);
+ maps->Add(0xd025cd98, 0xd025cff4, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+ "jit_map1.so", 0);
+ maps->Sort();
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, maps, offline_utils_.GetRegs(), offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00000000 jit_map0.so "
- "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
- " #01 pc 0000003d jit_map1.so "
- "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
- " #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
-
- " #03 pc 003851dd libart.so (art::Thread::CreateCallback(void*)+868)\n"
- " #04 pc 00062925 libc.so (__pthread_start(void*)+22)\n"
- " #05 pc 0001de39 libc.so (__start_thread+24)\n",
- frame_info);
-
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
@@ -1213,39 +784,22 @@
}
TEST_F(UnwindOfflineTest, offset_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("offset_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "offset_arm/", .arch = ARCH_ARM}, &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(19U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0032bfa0 libunwindstack_test (SignalInnerFunction+40)\n"
- " #01 pc 0032bfeb libunwindstack_test (SignalMiddleFunction+2)\n"
- " #02 pc 0032bff3 libunwindstack_test (SignalOuterFunction+2)\n"
- " #03 pc 0032fed3 libunwindstack_test "
- "(unwindstack::SignalCallerHandler(int, siginfo*, void*)+26)\n"
- " #04 pc 0002652c libc.so (__restore)\n"
- " #05 pc 00000000 <unknown>\n"
- " #06 pc 0032c2d9 libunwindstack_test (InnerFunction+736)\n"
- " #07 pc 0032cc4f libunwindstack_test (MiddleFunction+42)\n"
- " #08 pc 0032cc81 libunwindstack_test (OuterFunction+42)\n"
- " #09 pc 0032e547 libunwindstack_test "
- "(unwindstack::RemoteThroughSignal(int, unsigned int)+270)\n"
- " #10 pc 0032ed99 libunwindstack_test "
- "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+16)\n"
- " #11 pc 00354453 libunwindstack_test (testing::Test::Run()+154)\n"
- " #12 pc 00354de7 libunwindstack_test (testing::TestInfo::Run()+194)\n"
- " #13 pc 00355105 libunwindstack_test (testing::TestCase::Run()+180)\n"
- " #14 pc 0035a215 libunwindstack_test "
- "(testing::internal::UnitTestImpl::RunAllTests()+664)\n"
- " #15 pc 00359f4f libunwindstack_test (testing::UnitTest::Run()+110)\n"
- " #16 pc 0034d3db libunwindstack_test (main+38)\n"
- " #17 pc 00092c0d libc.so (__libc_init+48)\n"
- " #18 pc 0004202f libunwindstack_test (_start_main+38)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x2e55fa0U, unwinder.frames()[0].pc);
EXPECT_EQ(0xf43d2cccU, unwinder.frames()[0].sp);
EXPECT_EQ(0x2e55febU, unwinder.frames()[1].pc);
@@ -1289,24 +843,23 @@
// Test using a non-zero load bias library that has the fde entries
// encoded as 0xb, which is not set as pc relative.
TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("debug_frame_load_bias_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "debug_frame_load_bias_arm/", .arch = ARCH_ARM},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(8U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0005138c libc.so (__ioctl+8)\n"
- " #01 pc 0002140f libc.so (ioctl+30)\n"
- " #02 pc 00039535 libbinder.so (android::IPCThreadState::talkWithDriver(bool)+204)\n"
- " #03 pc 00039633 libbinder.so (android::IPCThreadState::getAndExecuteCommand()+10)\n"
- " #04 pc 00039b57 libbinder.so (android::IPCThreadState::joinThreadPool(bool)+38)\n"
- " #05 pc 00000c21 mediaserver (main+104)\n"
- " #06 pc 00084b89 libc.so (__libc_init+48)\n"
- " #07 pc 00000b77 mediaserver (_start_main+38)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xf0be238cU, unwinder.frames()[0].pc);
EXPECT_EQ(0xffd4a638U, unwinder.frames()[0].sp);
EXPECT_EQ(0xf0bb240fU, unwinder.frames()[1].pc);
@@ -1326,25 +879,23 @@
}
TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "shared_lib_in_apk_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
- " #01 pc 000000000005426c linker64 "
- "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
- " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\n"
- " #03 pc 00000000000846f4 libc.so (abort+172)\n"
- " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
- " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk!libfeature_support_angle.so (offset 0x4000) "
- "(ANGLEGetUtilityAPI+56)\n"
- " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
@@ -1363,27 +914,31 @@
}
TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init(
+ {.offline_files_dir = "shared_lib_in_apk_memory_only_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
// Add the memory that represents the shared library.
- MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory_.get());
- AddMemory(dir_ + "lib_mem.data", memory);
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
+ MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory.get());
+ const std::string* offline_files_path = offline_utils_.GetOfflineFilesPath();
+ if (offline_files_path == nullptr) FAIL() << "GetOfflineFilesPath() failed.";
+
+ if (!AddMemory(*offline_files_path + "lib_mem.data", memory, &error_msg)) FAIL() << error_msg;
+
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory);
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 000000000014ccbc linker64 (__dl_syscall+28)\n"
- " #01 pc 000000000005426c linker64 "
- "(__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1128)\n"
- " #02 pc 00000000000008c0 vdso.so (__kernel_rt_sigreturn)\n"
- " #03 pc 00000000000846f4 libc.so (abort+172)\n"
- " #04 pc 0000000000084ad4 libc.so (__assert2+36)\n"
- " #05 pc 000000000003d5b4 ANGLEPrebuilt.apk (offset 0x21d5000)\n"
- " #06 pc 000000000007fe68 libc.so (__libc_init)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x7e82c4fcbcULL, unwinder.frames()[0].pc);
EXPECT_EQ(0x7df8ca3bf0ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0x7e82b5726cULL, unwinder.frames()[1].pc);
@@ -1402,29 +957,24 @@
}
TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init(
+ {.offline_files_dir = "shared_lib_in_apk_single_map_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(13U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00000000000814bc libc.so (syscall+28)\n"
- " #01 pc 00000000008cdf5c test.apk (offset 0x5000)\n"
- " #02 pc 00000000008cde9c test.apk (offset 0x5000)\n"
- " #03 pc 00000000008cdd70 test.apk (offset 0x5000)\n"
- " #04 pc 00000000008ce408 test.apk (offset 0x5000)\n"
- " #05 pc 00000000008ce8d8 test.apk (offset 0x5000)\n"
- " #06 pc 00000000008ce814 test.apk (offset 0x5000)\n"
- " #07 pc 00000000008bcf60 test.apk (offset 0x5000)\n"
- " #08 pc 0000000000133024 test.apk (offset 0x5000)\n"
- " #09 pc 0000000000134ad0 test.apk (offset 0x5000)\n"
- " #10 pc 0000000000134b64 test.apk (offset 0x5000)\n"
- " #11 pc 00000000000e406c libc.so (__pthread_start(void*)+36)\n"
- " #12 pc 0000000000085e18 libc.so (__start_thread+64)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x7cbe0b14bcULL, unwinder.frames()[0].pc);
EXPECT_EQ(0x7be4f077d0ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0x7be6715f5cULL, unwinder.frames()[1].pc);
@@ -1454,26 +1004,42 @@
}
TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("invalid_elf_offset_arm/", ARCH_ARM, false));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "invalid_elf_offset_arm/",
+ .arch = ARCH_ARM,
+ .memory_flag = ProcessMemoryFlag::kNoMemory},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(1U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(" #00 pc 00aa7508 invalid.apk (offset 0x12e4000)\n", frame_info);
EXPECT_EQ(0xc898f508, unwinder.frames()[0].pc);
EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
}
TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
- ASSERT_NO_FATAL_FAILURE(Init("load_bias_ro_rx_x86_64/", ARCH_X86_64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "load_bias_ro_rx_x86_64/", .arch = ARCH_X86_64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+
std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
" #00 pc 00000000000e9dd4 libc.so (__write+20)\n"
" #01 pc 000000000007ab9c libc.so (_IO_file_write+44)\n"
@@ -1544,28 +1110,24 @@
}
TEST_F(UnwindOfflineTest, load_bias_different_section_bias_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("load_bias_different_section_bias_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init(
+ {.offline_files_dir = "load_bias_different_section_bias_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(12U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00000000000d59bc linker64 (__dl_syscall+28)\n"
- " #01 pc 00000000000554e8 linker64 (__dl__ZL24debuggerd_signal_handleriP7siginfoPv+1148)\n"
- " #02 pc 00000000000008c0 vdso (__kernel_rt_sigreturn)\n"
- " #03 pc 000000000007f3e8 libc.so (abort+168)\n"
- " #04 pc 00000000000459fc test (std::__ndk1::__throw_bad_cast()+4)\n"
- " #05 pc 0000000000056d80 test (testing::Test::Run()+88)\n"
- " #06 pc 000000000005724c test (testing::TestInfo::Run()+112)\n"
- " #07 pc 0000000000057558 test (testing::TestSuite::Run()+116)\n"
- " #08 pc 000000000005bffc test (testing::internal::UnitTestImpl::RunAllTests()+464)\n"
- " #09 pc 000000000005bd9c test (testing::UnitTest::Run()+116)\n"
- " #10 pc 00000000000464e4 test (main+144)\n"
- " #11 pc 000000000007aa34 libc.so (__libc_init+108)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x7112cb99bcULL, unwinder.frames()[0].pc);
EXPECT_EQ(0x7112bdbbf0ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0x7112c394e8ULL, unwinder.frames()[1].pc);
@@ -1593,27 +1155,23 @@
}
TEST_F(UnwindOfflineTest, eh_frame_bias_x86) {
- ASSERT_NO_FATAL_FAILURE(Init("eh_frame_bias_x86/", ARCH_X86));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "eh_frame_bias_x86/", .arch = ARCH_X86},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(11U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc ffffe430 vdso.so (__kernel_vsyscall+16)\n"
- " #01 pc 00082a4b libc.so (__epoll_pwait+43)\n"
- " #02 pc 000303a3 libc.so (epoll_pwait+115)\n"
- " #03 pc 000303ed libc.so (epoll_wait+45)\n"
- " #04 pc 00010ea2 tombstoned (epoll_dispatch+226)\n"
- " #05 pc 0000c5e7 tombstoned (event_base_loop+1095)\n"
- " #06 pc 0000c193 tombstoned (event_base_dispatch+35)\n"
- " #07 pc 00005c77 tombstoned (main+884)\n"
- " #08 pc 00015f66 libc.so (__libc_init+102)\n"
- " #09 pc 0000360e tombstoned (_start+98)\n"
- " #10 pc 00000001 <unknown>\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xffffe430ULL, unwinder.frames()[0].pc);
EXPECT_EQ(0xfffe1a30ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0xeb585a4bULL, unwinder.frames()[1].pc);
@@ -1639,37 +1197,23 @@
}
TEST_F(UnwindOfflineTest, signal_load_bias_arm) {
- ASSERT_NO_FATAL_FAILURE(Init("signal_load_bias_arm/", ARCH_ARM));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "signal_load_bias_arm/", .arch = ARCH_ARM},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 0029ef9e libunwindstack_unit_test (SignalInnerFunction+10)\n"
- " #01 pc 0029efa7 libunwindstack_unit_test (SignalMiddleFunction+2)\n"
- " #02 pc 0029efaf libunwindstack_unit_test (SignalOuterFunction+2)\n"
- " #03 pc 002a280b libunwindstack_unit_test (unwindstack::SignalCallerHandler(int, "
- "siginfo*, void*)+10)\n"
- " #04 pc 00058bd4 libc.so (__restore)\n"
- " #05 pc 0029f01e libunwindstack_unit_test (InnerFunction+106)\n"
- " #06 pc 0029f633 libunwindstack_unit_test (MiddleFunction+16)\n"
- " #07 pc 0029f64b libunwindstack_unit_test (OuterFunction+16)\n"
- " #08 pc 002a1711 libunwindstack_unit_test (unwindstack::RemoteThroughSignal(int, unsigned "
- "int)+260)\n"
- " #09 pc 002a1603 libunwindstack_unit_test "
- "(unwindstack::UnwindTest_remote_through_signal_Test::TestBody()+10)\n"
- " #10 pc 002c8fe3 libunwindstack_unit_test (testing::Test::Run()+130)\n"
- " #11 pc 002c9b25 libunwindstack_unit_test (testing::TestInfo::Run()+184)\n"
- " #12 pc 002c9e27 libunwindstack_unit_test (testing::TestSuite::Run()+202)\n"
- " #13 pc 002d193d libunwindstack_unit_test "
- "(testing::internal::UnitTestImpl::RunAllTests()+660)\n"
- " #14 pc 002d160b libunwindstack_unit_test (testing::UnitTest::Run()+134)\n"
- " #15 pc 002de035 libunwindstack_unit_test (IsolateMain+680)\n"
- " #16 pc 00058155 libc.so (__libc_init+68)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0xb6955f9eULL, unwinder.frames()[0].pc);
EXPECT_EQ(0xf2790ce8ULL, unwinder.frames()[0].sp);
EXPECT_EQ(0xb6955fa7ULL, unwinder.frames()[1].pc);
@@ -1707,25 +1251,22 @@
}
TEST_F(UnwindOfflineTest, empty_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("empty_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "empty_arm64/", .arch = ARCH_ARM64}, &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(7U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00000000000963a4 libc.so (__ioctl+4)\n"
- " #01 pc 000000000005344c libc.so (ioctl+140)\n"
- " #02 pc 0000000000050ce4 libbinder.so "
- "(android::IPCThreadState::talkWithDriver(bool)+308)\n"
- " #03 pc 0000000000050e98 libbinder.so "
- "(android::IPCThreadState::getAndExecuteCommand()+24)\n"
- " #04 pc 00000000000516ac libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)\n"
- " #05 pc 00000000000443b0 netd (main+1056)\n"
- " #06 pc 0000000000045594 libc.so (__libc_init+108)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x72a02203a4U, unwinder.frames()[0].pc);
EXPECT_EQ(0x7ffb6c0b50U, unwinder.frames()[0].sp);
EXPECT_EQ(0x72a01dd44cU, unwinder.frames()[1].pc);
@@ -1746,40 +1287,22 @@
// that the signal handler match does not occur and it uses the
// fde to do the unwind.
TEST_F(UnwindOfflineTest, signal_fde_x86) {
- ASSERT_NO_FATAL_FAILURE(Init("signal_fde_x86/", ARCH_X86));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "signal_fde_x86/", .arch = ARCH_X86}, &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(20U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 007914d9 libunwindstack_test (SignalInnerFunction+25)\n"
- " #01 pc 007914fc libunwindstack_test (SignalMiddleFunction+28)\n"
- " #02 pc 0079152c libunwindstack_test (SignalOuterFunction+28)\n"
- " #03 pc 0079af62 libunwindstack_test (unwindstack::SignalCallerHandler(int, siginfo*, "
- "void*)+50)\n"
- " #04 pc 00058fb0 libc.so (__restore)\n"
- " #05 pc 00000000 <unknown>\n"
- " #06 pc 0079161a libunwindstack_test (InnerFunction+218)\n"
- " #07 pc 007923aa libunwindstack_test (MiddleFunction+42)\n"
- " #08 pc 007923ea libunwindstack_test (OuterFunction+42)\n"
- " #09 pc 00797444 libunwindstack_test (unwindstack::RemoteThroughSignal(int, unsigned "
- "int)+868)\n"
- " #10 pc 007985b8 libunwindstack_test "
- "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+56)\n"
- " #11 pc 00817a19 libunwindstack_test\n"
- " #12 pc 008178c5 libunwindstack_test (testing::Test::Run()+277)\n"
- " #13 pc 00818d3e libunwindstack_test (testing::TestInfo::Run()+318)\n"
- " #14 pc 008198b4 libunwindstack_test (testing::TestSuite::Run()+436)\n"
- " #15 pc 00828cb0 libunwindstack_test "
- "(testing::internal::UnitTestImpl::RunAllTests()+1216)\n"
- " #16 pc 0082870f libunwindstack_test (testing::UnitTest::Run()+367)\n"
- " #17 pc 0084031e libunwindstack_test (IsolateMain+2334)\n"
- " #18 pc 0083f9e9 libunwindstack_test (main+41)\n"
- " #19 pc 00050646 libc.so (__libc_init+118)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x5ae0d4d9U, unwinder.frames()[0].pc);
EXPECT_EQ(0xecb37188U, unwinder.frames()[0].sp);
EXPECT_EQ(0x5ae0d4fcU, unwinder.frames()[1].pc);
@@ -1826,38 +1349,23 @@
// that the signal handler match does not occur and it uses the
// fde to do the unwind.
TEST_F(UnwindOfflineTest, signal_fde_x86_64) {
- ASSERT_NO_FATAL_FAILURE(Init("signal_fde_x86_64/", ARCH_X86_64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "signal_fde_x86_64/", .arch = ARCH_X86_64},
+ &error_msg))
+ FAIL() << error_msg;
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(18U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 000000000058415b libunwindstack_test (SignalInnerFunction+11)\n"
- " #01 pc 0000000000584168 libunwindstack_test (SignalMiddleFunction+8)\n"
- " #02 pc 0000000000584178 libunwindstack_test (SignalOuterFunction+8)\n"
- " #03 pc 000000000058ac77 libunwindstack_test (unwindstack::SignalCallerHandler(int, "
- "siginfo*, void*)+23)\n"
- " #04 pc 0000000000057d10 libc.so (__restore_rt)\n"
- " #05 pc 0000000000000000 <unknown>\n"
- " #06 pc 0000000000584244 libunwindstack_test (InnerFunction+196)\n"
- " #07 pc 0000000000584b44 libunwindstack_test (MiddleFunction+20)\n"
- " #08 pc 0000000000584b64 libunwindstack_test (OuterFunction+20)\n"
- " #09 pc 0000000000588457 libunwindstack_test (unwindstack::RemoteThroughSignal(int, "
- "unsigned int)+583)\n"
- " #10 pc 0000000000588f67 libunwindstack_test "
- "(unwindstack::UnwindTest_remote_through_signal_with_invalid_func_Test::TestBody()+23)\n"
- " #11 pc 00000000005d9c38 libunwindstack_test (testing::Test::Run()+216)\n"
- " #12 pc 00000000005daf9a libunwindstack_test (testing::TestInfo::Run()+266)\n"
- " #13 pc 00000000005dba46 libunwindstack_test (testing::TestSuite::Run()+390)\n"
- " #14 pc 00000000005ea4c6 libunwindstack_test "
- "(testing::internal::UnitTestImpl::RunAllTests()+1190)\n"
- " #15 pc 00000000005e9f61 libunwindstack_test (testing::UnitTest::Run()+337)\n"
- " #16 pc 0000000000600155 libunwindstack_test (IsolateMain+2037)\n"
- " #17 pc 000000000004e405 libc.so (__libc_init+101)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x5bb41271e15bU, unwinder.frames()[0].pc);
EXPECT_EQ(0x707eb5aa8320U, unwinder.frames()[0].sp);
EXPECT_EQ(0x5bb41271e168U, unwinder.frames()[1].pc);
@@ -1897,45 +1405,25 @@
}
TEST_F(UnwindOfflineTest, pauth_pc_arm64) {
- ASSERT_NO_FATAL_FAILURE(Init("pauth_pc_arm64/", ARCH_ARM64));
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "pauth_pc_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
- static_cast<RegsArm64*>(regs_.get())->SetPACMask(0x007fff8000000000ULL);
+ static_cast<RegsArm64*>(offline_utils_.GetRegs())->SetPACMask(0x007fff8000000000ULL);
- Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
unwinder.Unwind();
- std::string frame_info(DumpFrames(unwinder));
- ASSERT_EQ(26U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
- EXPECT_EQ(
- " #00 pc 00000000000404a8 toybox (do_print+28)\n"
- " #01 pc 0000000000040270 toybox (do_find+5072)\n"
- " #02 pc 000000000002c640 toybox (dirtree_handle_callback+40)\n"
- " #03 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #04 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #05 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #06 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #07 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #08 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #09 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #10 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #11 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #12 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #13 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #14 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #15 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #16 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #17 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #18 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #19 pc 000000000002c588 toybox (dirtree_recurse+200)\n"
- " #20 pc 000000000002c6a8 toybox (dirtree_handle_callback+144)\n"
- " #21 pc 000000000003ee54 toybox (find_main+272)\n"
- " #22 pc 0000000000034834 toybox (toy_exec_which+88)\n"
- " #23 pc 00000000000342cc toybox (toybox_main+148)\n"
- " #24 pc 00000000000348b4 toybox (main+120)\n"
- " #25 pc 00000000000499d8 libc.so "
- "(__libc_init+112)\n",
- frame_info);
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
EXPECT_EQ(0x5c390884a8U, unwinder.frames()[0].pc);
EXPECT_EQ(0x7ff3511750U, unwinder.frames()[0].sp);
EXPECT_EQ(0x5c39088270U, unwinder.frames()[1].pc);
@@ -1990,4 +1478,185 @@
EXPECT_EQ(0x7ff3511e70U, unwinder.frames()[25].sp);
}
+TEST_F(UnwindOfflineTest, profiler_like_multi_process) {
+ ConsecutiveUnwindTest(std::vector<UnwindSampleInfo>{
+ {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "jit_debug_arm/",
+ .arch = ARCH_ARM,
+ .memory_flag = ProcessMemoryFlag::kIncludeJitMemory},
+ {.offline_files_dir = "photos_reset_arm64/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "youtube_compiled_arm64/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "yt_music_arm64/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/", .arch = ARCH_ARM64}});
+}
+
+TEST_F(UnwindOfflineTest, profiler_like_single_process_multi_thread) {
+ ConsecutiveUnwindTest(std::vector<UnwindSampleInfo>{
+ {.offline_files_dir = "maps_compiled_arm64/28656_oat_odex_jar/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "maps_compiled_arm64/28613_main-thread/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "maps_compiled_arm64/28644/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "maps_compiled_arm64/28648/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "maps_compiled_arm64/28667/", .arch = ARCH_ARM64}});
+}
+
+TEST_F(UnwindOfflineTest, profiler_like_single_thread_diverse_pcs) {
+ ConsecutiveUnwindTest(std::vector<UnwindSampleInfo>{
+ {.offline_files_dir = "bluetooth_arm64/pc_1/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "bluetooth_arm64/pc_2/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "bluetooth_arm64/pc_3/", .arch = ARCH_ARM64},
+ {.offline_files_dir = "bluetooth_arm64/pc_4/", .arch = ARCH_ARM64}});
+}
+
+static void VerifyApkRORX(Unwinder& unwinder) {
+ EXPECT_EQ(0x7426d2e030U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7fe740cc90U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7426d2e08cU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7fe740ccd0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7426d2e0b8U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7fe740ccf0U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7426d2e0e4U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fe740cd10U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x603b0c5154U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fe740cd30U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x76b6df0b10U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7fe740cdb0U, unwinder.frames()[5].sp);
+}
+
+TEST_F(UnwindOfflineTest, apk_rorx_arm64) {
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "apk_rorx_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
+
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
+ unwinder.Unwind();
+
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
+
+ VerifyApkRORX(unwinder);
+}
+
+TEST_F(UnwindOfflineTest, apk_rorx_unreadable_arm64) {
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "apk_rorx_unreadable_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
+
+ // Create a process memory object that holds the apk data in memory
+ // along with the stack data.
+ MemoryOffline* stack_memory = new MemoryOffline;
+ ASSERT_TRUE(stack_memory->Init("stack.data", 0));
+
+ MemoryOffline* apk_memory = new MemoryOffline;
+ auto info1 = offline_utils_.GetMaps()->Find(0x7426d2d000);
+ ASSERT_TRUE(info1 != nullptr);
+ auto info2 = offline_utils_.GetMaps()->Find(0x7426d2e000);
+ ASSERT_TRUE(info2 != nullptr);
+ ASSERT_TRUE(
+ apk_memory->Init("fake.apk", info1->offset(), info1->start(), info2->end() - info1->start()));
+
+ std::unique_ptr<MemoryOfflineParts> parts(new MemoryOfflineParts);
+ parts->Add(stack_memory);
+ parts->Add(apk_memory);
+
+ std::shared_ptr<Memory> process_memory(parts.release());
+
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory);
+ unwinder.Unwind();
+
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
+
+ VerifyApkRORX(unwinder);
+}
+
+static void VerifyApkRX(Unwinder& unwinder) {
+ EXPECT_EQ(0x7cb0e6266cU, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7fe563be90U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7cb0e626c0U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7fe563bed0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7cb0e626ecU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7fe563bef0U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7cb0e62718U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fe563bf10U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x5e004f0154U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fe563bf30U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7f41124b10U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7fe563bfb0U, unwinder.frames()[5].sp);
+}
+
+TEST_F(UnwindOfflineTest, apk_rx_arm64) {
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "apk_rx_arm64/", .arch = ARCH_ARM64}, &error_msg))
+ FAIL() << error_msg;
+
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
+ offline_utils_.GetProcessMemory());
+ unwinder.Unwind();
+
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
+
+ VerifyApkRX(unwinder);
+}
+
+TEST_F(UnwindOfflineTest, apk_rx_unreadable_arm64) {
+ std::string error_msg;
+ if (!offline_utils_.Init({.offline_files_dir = "apk_rx_unreadable_arm64/", .arch = ARCH_ARM64},
+ &error_msg))
+ FAIL() << error_msg;
+
+ // Create a process memory object that holds the apk data in memory
+ // along with the stack data.
+ MemoryOffline* stack_memory = new MemoryOffline;
+ ASSERT_TRUE(stack_memory->Init("stack.data", 0));
+
+ MemoryOffline* apk_memory = new MemoryOffline;
+ auto info = offline_utils_.GetMaps()->Find(0x7cb0e62000);
+ ASSERT_TRUE(info != nullptr);
+ ASSERT_TRUE(
+ apk_memory->Init("fake.apk", info->offset(), info->start(), info->end() - info->start()));
+
+ std::unique_ptr<MemoryOfflineParts> parts(new MemoryOfflineParts);
+ parts->Add(stack_memory);
+ parts->Add(apk_memory);
+
+ std::shared_ptr<Memory> process_memory(parts.release());
+
+ Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory);
+ unwinder.Unwind();
+
+ size_t expected_num_frames;
+ if (!offline_utils_.GetExpectedNumFrames(&expected_num_frames, &error_msg)) FAIL() << error_msg;
+ std::string expected_frame_info;
+ if (!GetExpectedSamplesFrameInfo(&expected_frame_info, &error_msg)) FAIL() << error_msg;
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(expected_num_frames, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(expected_frame_info, frame_info);
+
+ VerifyApkRX(unwinder);
+}
+
+} // namespace
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index e42125d..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 {
@@ -52,15 +53,17 @@
TEST_TYPE_REMOTE_WITH_INVALID_CALL,
};
-static std::atomic_bool g_ready;
static volatile bool g_ready_for_remote;
static volatile bool g_signal_ready_for_remote;
-static std::atomic_bool g_finish;
+// In order to avoid the compiler not emitting the unwind entries for
+// the InnerFunction code that loops waiting for g_finish, always make
+// g_finish a volatile instead of an atomic. This issue was only ever
+// observerd on the arm architecture.
+static volatile bool g_finish;
static std::atomic_uintptr_t g_ucontext;
static std::atomic_int g_waiters;
static void ResetGlobals() {
- g_ready = false;
g_ready_for_remote = false;
g_signal_ready_for_remote = false;
g_finish = false;
@@ -76,7 +79,7 @@
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
- while (!g_finish.load()) {
+ while (!g_finish) {
}
}
@@ -109,8 +112,9 @@
return std::string(
"Unwind completed without finding all frames\n"
- " Looking for function: ") +
- function_names.front() + "\n" + "Unwind data:\n" + unwind;
+ " Unwinder error: ") +
+ unwinder->LastErrorCodeString() + "\n" +
+ " Looking for function: " + function_names.front() + "\n" + "Unwind data:\n" + unwind;
}
static void VerifyUnwindFrames(Unwinder* unwinder,
@@ -125,6 +129,14 @@
}
ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
+
+ // Verify that the load bias of every map with a MapInfo is has been initialized.
+ for (auto& frame : unwinder->frames()) {
+ if (frame.map_info == nullptr) {
+ continue;
+ }
+ ASSERT_NE(UINT64_MAX, frame.map_info->GetLoadBias()) << "Frame " << frame.num << " failed";
+ }
}
static void VerifyUnwind(Unwinder* unwinder, std::vector<const char*> expected_function_names) {
@@ -150,7 +162,7 @@
switch (test_type) {
case TEST_TYPE_LOCAL_WAIT_FOR_FINISH: {
g_waiters++;
- while (!g_finish.load()) {
+ while (!g_finish) {
}
break;
}
@@ -158,7 +170,6 @@
case TEST_TYPE_REMOTE:
case TEST_TYPE_REMOTE_WITH_INVALID_CALL: {
g_ready_for_remote = true;
- g_ready = true;
if (test_type == TEST_TYPE_REMOTE_WITH_INVALID_CALL) {
void (*crash_func)() = nullptr;
crash_func();
@@ -226,33 +237,15 @@
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 < 1000; i++) {
- if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
- ASSERT_TRUE(TestQuiescePid(pid))
- << "Waiting for process to quiesce failed: " << strerror(errno);
-
- MemoryRemote memory(pid);
- // Read the remote value to see if we are ready.
- bool value;
- if (memory.ReadFully(addr, &value, sizeof(value)) && value) {
- *completed = true;
- }
- if (!*completed || !leave_attached) {
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
- }
- if (*completed) {
- break;
- }
- } else {
- ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
+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) {
+ return PID_RUN_PASS;
}
- usleep(5000);
- }
+ return PID_RUN_KEEP_GOING;
+ });
}
TEST_F(UnwindTest, remote) {
@@ -264,9 +257,7 @@
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());
@@ -275,8 +266,7 @@
VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
- << "ptrace detach failed with unexpected error: " << strerror(errno);
+ ASSERT_TRUE(Detach(pid));
}
TEST_F(UnwindTest, unwind_from_pid_remote) {
@@ -288,9 +278,7 @@
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);
@@ -300,10 +288,7 @@
VerifyUnwind(&unwinder, kFunctionOrder);
- // Verify that calling the same object works again.
-
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
- << "ptrace detach failed with unexpected error: " << strerror(errno);
+ ASSERT_TRUE(Detach(pid));
}
static void RemoteCheckForLeaks(void (*unwind_func)(void*)) {
@@ -315,14 +300,11 @@
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_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
- << "ptrace detach failed with unexpected error: " << strerror(errno);
+ ASSERT_TRUE(Detach(pid));
}
static void RemoteUnwind(void* data) {
@@ -368,8 +350,8 @@
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 @@
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 @@
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,8 +411,7 @@
VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
- << "ptrace detach failed with unexpected error: " << strerror(errno);
+ ASSERT_TRUE(Detach(pid));
}
TEST_F(UnwindTest, remote_through_signal) {
@@ -468,8 +446,8 @@
size_t frames[kNumConcurrentThreads];
for (size_t i = 0; i < kNumConcurrentThreads; i++) {
std::thread* thread = new std::thread([i, &frames, &maps, &process_memory, &wait]() {
- while (wait)
- ;
+ while (wait) {
+ }
std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
RegsGetLocal(regs.get());
@@ -496,8 +474,8 @@
OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
});
- while (tid.load() == 0)
- ;
+ while (tid.load() == 0) {
+ }
ThreadUnwinder unwinder(512);
ASSERT_TRUE(unwinder.Init());
@@ -508,6 +486,34 @@
thread.join();
}
+TEST_F(UnwindTest, thread_unwind_copy_regs) {
+ ResetGlobals();
+
+ std::atomic_int tid(0);
+ std::thread thread([&tid]() {
+ tid = android::base::GetThreadId();
+ OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
+ });
+
+ while (tid.load() == 0) {
+ }
+
+ ThreadUnwinder unwinder(512);
+ ASSERT_TRUE(unwinder.Init());
+ std::unique_ptr<Regs> initial_regs;
+ unwinder.UnwindWithSignal(SIGRTMIN, tid, &initial_regs);
+ ASSERT_TRUE(initial_regs != nullptr);
+ // Verify the initial registers match the first frame pc/sp.
+ ASSERT_TRUE(unwinder.NumFrames() != 0);
+ auto initial_frame = unwinder.frames()[0];
+ ASSERT_EQ(initial_regs->pc(), initial_frame.pc);
+ ASSERT_EQ(initial_regs->sp(), initial_frame.sp);
+ VerifyUnwindFrames(&unwinder, kFunctionOrder);
+
+ g_finish = true;
+ thread.join();
+}
+
TEST_F(UnwindTest, thread_unwind_with_external_maps) {
ResetGlobals();
@@ -517,8 +523,8 @@
OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
});
- while (tid.load() == 0)
- ;
+ while (tid.load() == 0) {
+ }
LocalMaps maps;
ASSERT_TRUE(maps.Parse());
@@ -543,28 +549,44 @@
EXPECT_EQ(ERROR_UNSUPPORTED, unwinder.LastErrorCode());
}
+TEST_F(UnwindTest, thread_unwind_cur_thread) {
+ std::thread thread([]() {
+ ThreadUnwinder unwinder(512);
+ ASSERT_TRUE(unwinder.Init());
+ unwinder.UnwindWithSignal(SIGRTMIN, android::base::GetThreadId());
+ EXPECT_EQ(0U, unwinder.NumFrames());
+ EXPECT_EQ(ERROR_UNSUPPORTED, unwinder.LastErrorCode());
+ });
+ thread.join();
+}
+
+TEST_F(UnwindTest, thread_unwind_cur_pid_from_thread) {
+ std::thread thread([]() {
+ ThreadUnwinder unwinder(512);
+ ASSERT_TRUE(unwinder.Init());
+ unwinder.UnwindWithSignal(SIGRTMIN, getpid());
+ EXPECT_NE(0U, unwinder.NumFrames());
+ EXPECT_NE(ERROR_UNSUPPORTED, unwinder.LastErrorCode());
+ });
+ thread.join();
+}
+
static std::thread* CreateUnwindThread(std::atomic_int& tid, ThreadUnwinder& unwinder,
std::atomic_bool& start_unwinding,
std::atomic_int& unwinders) {
return new std::thread([&tid, &unwinder, &start_unwinding, &unwinders]() {
- while (!start_unwinding.load())
- ;
+ while (!start_unwinding.load()) {
+ }
ThreadUnwinder thread_unwinder(512, &unwinder);
- thread_unwinder.UnwindWithSignal(SIGRTMIN, tid);
-#if defined(__arm__)
- // On arm, there is a chance of winding up in case that doesn't unwind.
- // Identify that case and allow unwinding in that case.
- for (size_t i = 0; i < 10; i++) {
- auto frames = thread_unwinder.frames();
- if (frames.size() > 1 && frames[frames.size() - 1].pc < 1000 &&
- frames[frames.size() - 2].function_name == "InnerFunction") {
- thread_unwinder.UnwindWithSignal(SIGRTMIN, tid);
- } else {
+ // Allow the unwind to timeout since this will be doing multiple
+ // unwinds at once.
+ for (size_t i = 0; i < 3; i++) {
+ thread_unwinder.UnwindWithSignal(SIGRTMIN, tid);
+ if (thread_unwinder.LastErrorCode() != ERROR_THREAD_TIMEOUT) {
break;
}
}
-#endif
VerifyUnwindFrames(&thread_unwinder, kFunctionOrder);
++unwinders;
});
@@ -580,8 +602,8 @@
OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH);
});
- while (g_waiters.load() != 1)
- ;
+ while (g_waiters.load() != 1) {
+ }
ThreadUnwinder unwinder(512);
ASSERT_TRUE(unwinder.Init());
@@ -594,8 +616,8 @@
}
start_unwinding = true;
- while (unwinders.load() != kNumThreads)
- ;
+ while (unwinders.load() != kNumThreads) {
+ }
for (auto* thread : threads) {
thread->join();
@@ -620,8 +642,8 @@
threads.push_back(thread);
}
- while (g_waiters.load() != kNumThreads)
- ;
+ while (g_waiters.load() != kNumThreads) {
+ }
ThreadUnwinder unwinder(512);
ASSERT_TRUE(unwinder.Init());
@@ -634,8 +656,8 @@
}
start_unwinding = true;
- while (unwinders.load() != kNumThreads)
- ;
+ while (unwinders.load() != kNumThreads) {
+ }
for (auto* thread : unwinder_threads) {
thread->join();
@@ -670,8 +692,8 @@
threads.push_back(thread);
}
- while (g_waiters.load() != kNumThreads)
- ;
+ while (g_waiters.load() != kNumThreads) {
+ }
ThreadUnwinder unwinder(512, &maps);
ASSERT_TRUE(unwinder.Init());
@@ -684,8 +706,8 @@
}
start_unwinding = true;
- while (unwinders.load() != kNumThreads)
- ;
+ while (unwinders.load() != kNumThreads) {
+ }
for (auto* thread : unwinder_threads) {
thread->join();
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index 2884a40..2da3ee1 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -39,8 +39,8 @@
#include "ElfFake.h"
#include "ElfTestUtils.h"
-#include "MemoryFake.h"
-#include "RegsFake.h"
+#include "utils/MemoryFake.h"
+#include "utils/RegsFake.h"
namespace unwindstack {
@@ -49,8 +49,8 @@
static MapInfo* AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
const char* name, Elf* elf = nullptr) {
std::string str_name(name);
- maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
- MapInfo* map_info = maps_->Find(start);
+ maps_->Add(start, end, offset, flags, name);
+ MapInfo* map_info = maps_->Find(start).get();
if (elf != nullptr) {
map_info->set_elf(elf);
}
@@ -99,9 +99,7 @@
AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
- map_info =
- AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
- map_info->set_load_bias(0);
+ AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
@@ -139,7 +137,7 @@
map_info =
AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
- map_info->set_load_bias(0);
+ map_info->set_elf_start_offset(0x1000);
elf = new ElfFake(new MemoryFake);
interface = new ElfInterfaceFake(nullptr);
@@ -208,7 +206,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -219,13 +216,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -234,13 +233,15 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[2];
EXPECT_EQ(2U, frame->num);
@@ -249,13 +250,15 @@
EXPECT_EQ(0x10020U, frame->sp);
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
@@ -274,7 +277,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -285,13 +287,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -300,13 +304,15 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[2];
EXPECT_EQ(2U, frame->num);
@@ -315,13 +321,15 @@
EXPECT_EQ(0x10020U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, non_zero_load_bias) {
@@ -335,7 +343,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -346,13 +353,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake_load_bias.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xa5000U, frame->map_start);
- EXPECT_EQ(0xa6000U, frame->map_end);
- EXPECT_EQ(0x5000U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake_load_bias.so", frame->map_info->name());
+ EXPECT_EQ("/fake/fake_load_bias.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xa5000U, frame->map_info->start());
+ EXPECT_EQ(0xa6000U, frame->map_info->end());
+ EXPECT_EQ(0x5000U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
}
TEST_F(UnwinderTest, non_zero_elf_offset) {
@@ -366,7 +375,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -377,13 +385,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake_offset.oat", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xa7000U, frame->map_start);
- EXPECT_EQ(0xa8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake_offset.oat", frame->map_info->name());
+ EXPECT_EQ("/fake/fake_offset.oat", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xa7000U, frame->map_info->start());
+ EXPECT_EQ(0xa8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
}
TEST_F(UnwinderTest, non_zero_map_offset) {
@@ -397,7 +407,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -408,45 +417,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
- EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
- EXPECT_EQ(0x1d000U, frame->map_exact_offset);
- EXPECT_EQ(0x43000U, frame->map_start);
- EXPECT_EQ(0x44000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
-}
-
-TEST_F(UnwinderTest, disable_embedded_soname) {
- ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
-
- regs_.set_pc(0x43000);
- regs_.set_sp(0x10000);
- ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
-
- Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
- unwinder.SetEmbeddedSoname(false);
- unwinder.Unwind();
- EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
- EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
-
- ASSERT_EQ(1U, unwinder.NumFrames());
-
- auto* frame = &unwinder.frames()[0];
- EXPECT_EQ(0U, frame->num);
- EXPECT_EQ(0U, frame->rel_pc);
- EXPECT_EQ(0x43000U, frame->pc);
- EXPECT_EQ(0x10000U, frame->sp);
- EXPECT_EQ("Frame0", frame->function_name);
- EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
- EXPECT_EQ(0x1d000U, frame->map_exact_offset);
- EXPECT_EQ(0x43000U, frame->map_start);
- EXPECT_EQ(0x44000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake.apk", frame->map_info->name());
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0x1d000U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0x1d000U, frame->map_info->offset());
+ EXPECT_EQ(0x43000U, frame->map_info->start());
+ EXPECT_EQ(0x44000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
// Verify that no attempt to continue after the step indicates it is done.
@@ -467,7 +446,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -478,13 +456,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
// Verify the maximum frames to save.
@@ -501,25 +481,27 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(20U, unwinder.NumFrames());
for (size_t i = 0; i < 20; i++) {
auto* frame = &unwinder.frames()[i];
EXPECT_EQ(i, frame->num);
- EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i;
- EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i;
- EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i;
- EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
- EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
- EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
- EXPECT_EQ(0U, frame->map_elf_start_offset) << "Failed at frame " << i;
- EXPECT_EQ(0U, frame->map_exact_offset) << "Failed at frame " << i;
- EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
- EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
- EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i;
+ SCOPED_TRACE(testing::Message() << "Failed at frame " << i);
+ EXPECT_EQ(i * 0x100, frame->rel_pc);
+ EXPECT_EQ(0x1000 + i * 0x100, frame->pc);
+ EXPECT_EQ(0x10000 + 0x10 * i, frame->sp);
+ EXPECT_EQ("Frame" + std::to_string(i), frame->function_name);
+ EXPECT_EQ(i, frame->function_offset);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
}
@@ -545,7 +527,6 @@
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -556,13 +537,15 @@
EXPECT_EQ(0x10050U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -571,13 +554,15 @@
EXPECT_EQ(0x10060U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x20000U, frame->map_start);
- EXPECT_EQ(0x22000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x20000U, frame->map_info->start());
+ EXPECT_EQ(0x22000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[2];
EXPECT_EQ(2U, frame->num);
@@ -586,12 +571,14 @@
EXPECT_EQ(0x10070U, frame->sp);
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
- EXPECT_EQ("/fake/libanother.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x23000U, frame->map_start);
- EXPECT_EQ(0x24000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/libanother.so", frame->map_info->name());
+ EXPECT_EQ("/fake/libanother.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x23000U, frame->map_info->start());
+ EXPECT_EQ(0x24000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
// Verify SP in a non-existant map is okay.
@@ -608,7 +595,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -619,12 +605,14 @@
EXPECT_EQ(0x63000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -633,12 +621,14 @@
EXPECT_EQ(0x50020U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x20000U, frame->map_start);
- EXPECT_EQ(0x22000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x20000U, frame->map_info->start());
+ EXPECT_EQ(0x22000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
// Verify PC in a device stops the unwind.
@@ -657,7 +647,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
}
@@ -678,7 +667,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
}
@@ -694,7 +682,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -705,13 +692,7 @@
EXPECT_EQ(0x13000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0U, frame->map_start);
- EXPECT_EQ(0U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(0, frame->map_flags);
+ ASSERT_TRUE(frame->map_info == nullptr);
}
// Verify that a speculative frame is added.
@@ -732,7 +713,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -743,13 +723,7 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0U, frame->map_start);
- EXPECT_EQ(0U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(0, frame->map_flags);
+ ASSERT_TRUE(frame->map_info == nullptr);
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -758,13 +732,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[2];
EXPECT_EQ(2U, frame->num);
@@ -773,13 +749,15 @@
EXPECT_EQ(0x10020U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/fake/libanother.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x23000U, frame->map_start);
- EXPECT_EQ(0x24000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/libanother.so", frame->map_info->name());
+ EXPECT_EQ("/fake/libanother.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x23000U, frame->map_info->start());
+ EXPECT_EQ(0x24000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
// Verify that a speculative frame is added then removed because no other
@@ -799,7 +777,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -810,13 +787,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x20000U, frame->map_start);
- EXPECT_EQ(0x22000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x20000U, frame->map_info->start());
+ EXPECT_EQ(0x22000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -825,13 +804,7 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0U, frame->map_start);
- EXPECT_EQ(0U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(0, frame->map_flags);
+ ASSERT_TRUE(frame->map_info == nullptr);
}
// Verify that a speculative frame is added and left if there are only
@@ -850,7 +823,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -861,13 +833,7 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0U, frame->map_start);
- EXPECT_EQ(0U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(0, frame->map_flags);
+ ASSERT_TRUE(frame->map_info == nullptr);
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -876,13 +842,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
// Verify that a speculative frame does not cause a crash when it wasn't
@@ -899,11 +867,35 @@
unwinder.Unwind(&skip_names);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(0U, unwinder.NumFrames());
}
+// Verify that a speculative frame mapping to invalid map doesn't hide error
+// for the previous frame.
+TEST_F(UnwinderTest, speculative_frame_to_invalid_map_not_hide_prev_error) {
+ regs_.set_pc(0x100000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetReturnAddress(0x4);
+ regs_.FakeSetReturnAddressValid(true);
+
+ Unwinder unwinder(64, maps_.get(), ®s_, process_memory_);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_INVALID_ELF, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x300U, frame->rel_pc);
+ EXPECT_EQ(0x100000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ ASSERT_TRUE(frame->map_info != nullptr);
+}
+
// Verify that an unwind stops when a frame is in given suffix.
TEST_F(UnwinderTest, map_ignore_suffixes) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
@@ -923,11 +915,10 @@
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
// Make sure the elf was not initialized.
- MapInfo* map_info = maps_->Find(0x53000);
+ MapInfo* map_info = maps_->Find(0x53000).get();
ASSERT_TRUE(map_info != nullptr);
EXPECT_TRUE(map_info->elf() == nullptr);
@@ -938,13 +929,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -953,13 +946,15 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_name);
- EXPECT_EQ(0x1d000U, frame->map_elf_start_offset);
- EXPECT_EQ(0x1d000U, frame->map_exact_offset);
- EXPECT_EQ(0x43000U, frame->map_start);
- EXPECT_EQ(0x44000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake.apk", frame->map_info->name());
+ EXPECT_EQ("/fake/fake.apk!lib_fake.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0x1d000U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0x1d000U, frame->map_info->offset());
+ EXPECT_EQ(0x43000U, frame->map_info->start());
+ EXPECT_EQ(0x44000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
// Verify that an unwind stops when the sp and pc don't change.
@@ -983,7 +978,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -994,13 +988,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -1009,13 +1005,15 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x33000U, frame->map_start);
- EXPECT_EQ(0x34000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/compressed.so", frame->map_info->name());
+ EXPECT_EQ("/fake/compressed.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x33000U, frame->map_info->start());
+ EXPECT_EQ(0x34000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[2];
EXPECT_EQ(2U, frame->num);
@@ -1024,13 +1022,15 @@
EXPECT_EQ(0x10020U, frame->sp);
EXPECT_EQ("Frame2", frame->function_name);
EXPECT_EQ(2U, frame->function_offset);
- EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x33000U, frame->map_start);
- EXPECT_EQ(0x34000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/compressed.so", frame->map_info->name());
+ EXPECT_EQ("/fake/compressed.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x33000U, frame->map_info->start());
+ EXPECT_EQ(0x34000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, dex_pc_in_map) {
@@ -1043,7 +1043,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1054,13 +1053,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xa3000U, frame->map_start);
- EXPECT_EQ(0xa4000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_info->name());
+ EXPECT_EQ("/fake/fake.vdex", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xa3000U, frame->map_info->start());
+ EXPECT_EQ(0xa4000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -1069,13 +1070,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, dex_pc_in_map_non_zero_offset) {
@@ -1088,7 +1091,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1099,13 +1101,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake.apk", frame->map_name);
- EXPECT_EQ(0x1000U, frame->map_elf_start_offset);
- EXPECT_EQ(0x1000U, frame->map_exact_offset);
- EXPECT_EQ(0xd0000U, frame->map_start);
- EXPECT_EQ(0xd1000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake.apk", frame->map_info->name());
+ EXPECT_EQ("/fake/fake.apk", frame->map_info->GetFullName());
+ EXPECT_EQ(0x1000U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0x1000U, frame->map_info->offset());
+ EXPECT_EQ(0xd0000U, frame->map_info->start());
+ EXPECT_EQ(0xd1000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -1114,13 +1118,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, dex_pc_not_in_map) {
@@ -1133,7 +1139,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1144,13 +1149,7 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0U, frame->map_start);
- EXPECT_EQ(0U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(0, frame->map_flags);
+ ASSERT_TRUE(frame->map_info == nullptr);
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -1159,13 +1158,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, dex_pc_not_in_map_valid_dex_files) {
@@ -1180,7 +1181,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1191,13 +1191,7 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0U, frame->map_start);
- EXPECT_EQ(0U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(0, frame->map_flags);
+ ASSERT_TRUE(frame->map_info == nullptr);
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -1206,13 +1200,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, dex_pc_multiple_frames) {
@@ -1228,7 +1224,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -1239,13 +1234,14 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xa3000U, frame->map_start);
- EXPECT_EQ(0xa4000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xa3000U, frame->map_info->start());
+ EXPECT_EQ(0xa4000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
frame = &unwinder.frames()[1];
EXPECT_EQ(1U, frame->num);
@@ -1254,13 +1250,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x1000U, frame->map_start);
- EXPECT_EQ(0x8000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x1000U, frame->map_info->start());
+ EXPECT_EQ(0x8000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
frame = &unwinder.frames()[2];
EXPECT_EQ(2U, frame->num);
@@ -1269,13 +1267,15 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("Frame1", frame->function_name);
EXPECT_EQ(1U, frame->function_offset);
- EXPECT_EQ("/fake/compressed.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0x33000U, frame->map_start);
- EXPECT_EQ(0x34000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/compressed.so", frame->map_info->name());
+ EXPECT_EQ("/fake/compressed.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0x33000U, frame->map_info->start());
+ EXPECT_EQ(0x34000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_info->flags());
}
TEST_F(UnwinderTest, dex_pc_max_frames) {
@@ -1288,7 +1288,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1299,16 +1298,18 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/fake.vdex", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xa3000U, frame->map_start);
- EXPECT_EQ(0xa4000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/fake/fake.vdex", frame->map_info->name());
+ EXPECT_EQ("/fake/fake.vdex", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xa3000U, frame->map_info->start());
+ EXPECT_EQ(0xa4000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
}
-TEST_F(UnwinderTest, elf_from_memory_not_file) {
+TEST_F(UnwinderTest, elf_file_not_readable) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
regs_.set_pc(0xc0050);
@@ -1319,7 +1320,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_TRUE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1330,13 +1330,16 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/fake/unreadable.so", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xc0000U, frame->map_start);
- EXPECT_EQ(0xc1000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_TRUE(frame->map_info->ElfFileNotReadable());
+ EXPECT_EQ("/fake/unreadable.so", frame->map_info->name());
+ EXPECT_EQ("/fake/unreadable.so", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xc0000U, frame->map_info->start());
+ EXPECT_EQ(0xc1000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
}
TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) {
@@ -1350,7 +1353,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1361,13 +1363,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("[vdso]", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xc1000U, frame->map_start);
- EXPECT_EQ(0xc2000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("[vdso]", frame->map_info->name());
+ EXPECT_EQ("[vdso]", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xc1000U, frame->map_info->start());
+ EXPECT_EQ(0xc2000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
}
TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) {
@@ -1381,7 +1385,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1392,13 +1395,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xc2000U, frame->map_start);
- EXPECT_EQ(0xc3000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("", frame->map_info->name());
+ EXPECT_EQ("", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xc2000U, frame->map_info->start());
+ EXPECT_EQ(0xc3000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
}
TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
@@ -1412,7 +1417,6 @@
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
EXPECT_EQ(WARNING_NONE, unwinder.warnings());
- EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1423,13 +1427,15 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("Frame0", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/memfd:/jit-cache", frame->map_name);
- EXPECT_EQ(0U, frame->map_elf_start_offset);
- EXPECT_EQ(0U, frame->map_exact_offset);
- EXPECT_EQ(0xc3000U, frame->map_start);
- EXPECT_EQ(0xc4000U, frame->map_end);
- EXPECT_EQ(0U, frame->map_load_bias);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_flags);
+ ASSERT_TRUE(frame->map_info != nullptr);
+ EXPECT_EQ("/memfd:/jit-cache", frame->map_info->name());
+ EXPECT_EQ("/memfd:/jit-cache", frame->map_info->GetFullName());
+ EXPECT_EQ(0U, frame->map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame->map_info->offset());
+ EXPECT_EQ(0xc3000U, frame->map_info->start());
+ EXPECT_EQ(0xc4000U, frame->map_info->end());
+ EXPECT_EQ(0U, frame->map_info->GetLoadBias());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame->map_info->flags());
}
// Verify format frame code.
@@ -1449,18 +1455,16 @@
frame.sp = 0x1000;
frame.function_name = "function";
frame.function_offset = 100;
- frame.map_name = "/fake/libfake.so";
- frame.map_elf_start_offset = 0x2000;
- frame.map_start = 0x3000;
- frame.map_end = 0x6000;
- frame.map_flags = PROT_READ;
+ auto map_info = MapInfo::Create(0x3000, 0x6000, 0, PROT_READ, "/fake/libfake.so");
+ map_info->set_elf_start_offset(0x2000);
+ frame.map_info = map_info;
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (offset 0x2000) (function+100)",
unwinder64.FormatFrame(frame));
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (offset 0x2000) (function+100)",
unwinder32.FormatFrame(frame));
- frame.map_elf_start_offset = 0;
+ map_info->set_elf_start_offset(0);
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)",
unwinder64.FormatFrame(frame));
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame));
@@ -1479,12 +1483,11 @@
EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", unwinder64.FormatFrame(frame));
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", unwinder32.FormatFrame(frame));
- frame.map_name = "";
+ map_info->name() = "";
EXPECT_EQ(" #01 pc 0000000000001000 <anonymous:3000>", unwinder64.FormatFrame(frame));
EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", unwinder32.FormatFrame(frame));
- frame.map_start = 0;
- frame.map_end = 0;
+ frame.map_info = nullptr;
EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", unwinder64.FormatFrame(frame));
EXPECT_EQ(" #01 pc 00001000 <unknown>", unwinder32.FormatFrame(frame));
}
@@ -1501,11 +1504,9 @@
frame.sp = 0x1000;
frame.function_name = "function";
frame.function_offset = 100;
- frame.map_name = "/fake/libfake.so";
- frame.map_elf_start_offset = 0;
- frame.map_start = 0x3000;
- frame.map_end = 0x6000;
- frame.map_flags = PROT_READ;
+ frame.map_info = MapInfo::Create(0x3000, 0x6000, 0, PROT_READ, "/fake/libfake.so");
+ SharedString* build_id = new SharedString(std::string{0x46, 0x41, 0x4b, 0x45});
+ frame.map_info->set_build_id(build_id);
EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder.FormatFrame(frame));
unwinder.SetDisplayBuildID(true);
@@ -1611,13 +1612,15 @@
frame = unwinder.BuildFrameFromPcOnly(0x100310);
EXPECT_EQ(0x10030eU, frame.pc);
EXPECT_EQ(0x60eU, frame.rel_pc);
- EXPECT_EQ("/fake/jit.so", frame.map_name);
- EXPECT_EQ(0x100U, frame.map_elf_start_offset);
- EXPECT_EQ(0x200U, frame.map_exact_offset);
- EXPECT_EQ(0x100000U, frame.map_start);
- EXPECT_EQ(0x101000U, frame.map_end);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
- EXPECT_EQ(0x300U, frame.map_load_bias);
+ ASSERT_TRUE(frame.map_info != nullptr);
+ EXPECT_EQ("/fake/jit.so", frame.map_info->name());
+ EXPECT_EQ("/fake/jit.so", frame.map_info->GetFullName());
+ EXPECT_EQ(0x100U, frame.map_info->elf_start_offset());
+ EXPECT_EQ(0x200U, frame.map_info->offset());
+ EXPECT_EQ(0x100000U, frame.map_info->start());
+ EXPECT_EQ(0x101000U, frame.map_info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_info->flags());
+ EXPECT_EQ(0U, frame.map_info->GetLoadBias());
EXPECT_EQ("", frame.function_name);
EXPECT_EQ(0U, frame.function_offset);
}
@@ -1633,13 +1636,15 @@
frame = unwinder.BuildFrameFromPcOnly(0x1010);
EXPECT_EQ(0x100cU, frame.pc);
EXPECT_EQ(0xcU, frame.rel_pc);
- EXPECT_EQ("/system/fake/libc.so", frame.map_name);
- EXPECT_EQ(0U, frame.map_elf_start_offset);
- EXPECT_EQ(0U, frame.map_exact_offset);
- EXPECT_EQ(0x1000U, frame.map_start);
- EXPECT_EQ(0x8000U, frame.map_end);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
- EXPECT_EQ(0U, frame.map_load_bias);
+ ASSERT_TRUE(frame.map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame.map_info->GetFullName());
+ EXPECT_EQ(0U, frame.map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame.map_info->offset());
+ EXPECT_EQ(0x1000U, frame.map_info->start());
+ EXPECT_EQ(0x8000U, frame.map_info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_info->flags());
+ EXPECT_EQ(0U, frame.map_info->GetLoadBias());
EXPECT_EQ("", frame.function_name);
EXPECT_EQ(0U, frame.function_offset);
@@ -1650,13 +1655,15 @@
frame = unwinder.BuildFrameFromPcOnly(0x1010);
EXPECT_EQ(0x100cU, frame.pc);
EXPECT_EQ(0xcU, frame.rel_pc);
- EXPECT_EQ("/system/fake/libc.so", frame.map_name);
- EXPECT_EQ(0U, frame.map_elf_start_offset);
- EXPECT_EQ(0U, frame.map_exact_offset);
- EXPECT_EQ(0x1000U, frame.map_start);
- EXPECT_EQ(0x8000U, frame.map_end);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
- EXPECT_EQ(0U, frame.map_load_bias);
+ ASSERT_TRUE(frame.map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame.map_info->GetFullName());
+ EXPECT_EQ(0U, frame.map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame.map_info->offset());
+ EXPECT_EQ(0x1000U, frame.map_info->start());
+ EXPECT_EQ(0x8000U, frame.map_info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_info->flags());
+ EXPECT_EQ(0U, frame.map_info->GetLoadBias());
EXPECT_EQ("", frame.function_name);
EXPECT_EQ(0U, frame.function_offset);
@@ -1666,13 +1673,15 @@
frame = unwinder.BuildFrameFromPcOnly(0x1010);
EXPECT_EQ(0x100cU, frame.pc);
EXPECT_EQ(0xcU, frame.rel_pc);
- EXPECT_EQ("/system/fake/libc.so", frame.map_name);
- EXPECT_EQ(0U, frame.map_elf_start_offset);
- EXPECT_EQ(0U, frame.map_exact_offset);
- EXPECT_EQ(0x1000U, frame.map_start);
- EXPECT_EQ(0x8000U, frame.map_end);
- EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
- EXPECT_EQ(0U, frame.map_load_bias);
+ ASSERT_TRUE(frame.map_info != nullptr);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_info->name());
+ EXPECT_EQ("/system/fake/libc.so", frame.map_info->GetFullName());
+ EXPECT_EQ(0U, frame.map_info->elf_start_offset());
+ EXPECT_EQ(0U, frame.map_info->offset());
+ EXPECT_EQ(0x1000U, frame.map_info->start());
+ EXPECT_EQ(0x8000U, frame.map_info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_info->flags());
+ EXPECT_EQ(0U, frame.map_info->GetLoadBias());
EXPECT_EQ("Frame0", frame.function_name);
EXPECT_EQ(10U, frame.function_offset);
}
@@ -1737,21 +1746,22 @@
FrameData frame = unwinder.BuildFrameFromPcOnly(0x100310);
EXPECT_EQ(0x10030eU, frame.pc);
EXPECT_EQ(0x60eU, frame.rel_pc);
- EXPECT_EQ("/fake/jit.so", frame.map_name);
- EXPECT_EQ(0x100U, frame.map_elf_start_offset);
- EXPECT_EQ(0x200U, frame.map_exact_offset);
- EXPECT_EQ(0x100000U, frame.map_start);
- EXPECT_EQ(0x101000U, frame.map_end);
- EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
- EXPECT_EQ(0U, frame.map_load_bias);
+ ASSERT_TRUE(frame.map_info != nullptr);
+ EXPECT_EQ("/fake/jit.so", frame.map_info->name());
+ EXPECT_EQ("/fake/jit.so", frame.map_info->GetFullName());
+ EXPECT_EQ(0x100U, frame.map_info->elf_start_offset());
+ EXPECT_EQ(0x200U, frame.map_info->offset());
+ EXPECT_EQ(0x100000U, frame.map_info->start());
+ EXPECT_EQ(0x101000U, frame.map_info->end());
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_info->flags());
+ EXPECT_EQ(0U, frame.map_info->GetLoadBias());
EXPECT_EQ("FakeJitFunction", frame.function_name);
EXPECT_EQ(0xeU, frame.function_offset);
}
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());
@@ -1771,6 +1781,22 @@
ASSERT_DEATH(CreateJitDebug(ARCH_UNKNOWN, process_memory), "");
}
+TEST_F(UnwinderTest, unwinder_from_pid_with_external_maps) {
+ LocalMaps map;
+ ASSERT_TRUE(map.Parse());
+
+ UnwinderFromPid unwinder1(10, getpid(), &map);
+ unwinder1.SetArch(Regs::CurrentArch());
+ ASSERT_EQ(&map, unwinder1.GetMaps());
+ ASSERT_TRUE(unwinder1.Init());
+ ASSERT_EQ(&map, unwinder1.GetMaps());
+
+ UnwinderFromPid unwinder2(10, getpid(), Regs::CurrentArch(), &map);
+ ASSERT_EQ(&map, unwinder2.GetMaps());
+ ASSERT_TRUE(unwinder2.Init());
+ ASSERT_EQ(&map, unwinder2.GetMaps());
+}
+
TEST_F(UnwinderDeathTest, set_dex_files_error) {
Maps maps;
std::shared_ptr<Memory> process_memory(new MemoryFake);
diff --git a/libunwindstack/tests/VerifyBionicTerminationTest.cpp b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
index 37fd89b..ff105c4 100644
--- a/libunwindstack/tests/VerifyBionicTerminationTest.cpp
+++ b/libunwindstack/tests/VerifyBionicTerminationTest.cpp
@@ -69,9 +69,10 @@
static void VerifyReturnAddress(const FrameData& frame) {
// Now go and find information about the register data and verify that the relative pc results in
// an undefined register.
- Elf elf(Memory::CreateFileMemory(frame.map_name, 0).release());
- ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_name.c_str();
- ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_name.c_str() << " is not valid.";
+ Elf elf(Memory::CreateFileMemory(frame.map_info->name(), 0).release());
+ ASSERT_TRUE(frame.map_info != nullptr);
+ ASSERT_TRUE(elf.Init()) << "Failed to init elf object from " << frame.map_info->name().c_str();
+ ASSERT_TRUE(elf.valid()) << "Elf " << frame.map_info->name().c_str() << " is not valid.";
ElfInterface* interface = elf.interface();
// Only check the eh_frame and the debug_frame since the undefined register
@@ -108,8 +109,9 @@
const std::vector<FrameData>& frames = unwinder.frames();
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
const FrameData& frame = frames[i];
- if (frame.function_name == "__libc_init" && !frame.map_name.empty() &&
- std::string("libc.so") == basename(frame.map_name.c_str())) {
+ if (frame.function_name == "__libc_init" && frame.map_info != nullptr &&
+ !frame.map_info->name().empty() &&
+ std::string("libc.so") == basename(frame.map_info->name().c_str())) {
ASSERT_EQ(unwinder.NumFrames(), i + 1) << "__libc_init is not last frame.";
ASSERT_NO_FATAL_FAILURE(VerifyReturnAddress(frame));
found = true;
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
deleted file mode 100644
index 09ba495..0000000
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
deleted file mode 100644
index 39c9025..0000000
--- a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
deleted file mode 100644
index 78449bf..0000000
--- a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64 b/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
deleted file mode 100644
index 81bda1d..0000000
--- a/libunwindstack/tests/files/offline/bad_eh_frame_hdr_arm64/waiter64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so b/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
deleted file mode 100644
index 9c78790..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_first_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter b/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
deleted file mode 100644
index b1fc024..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_first_x86/waiter
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
deleted file mode 100644
index 4b7bf44..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libbinder.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
deleted file mode 100644
index 013858e..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver b/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
deleted file mode 100644
index 9e4a83f..0000000
--- a/libunwindstack/tests/files/offline/debug_frame_load_bias_arm/mediaserver
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
deleted file mode 100644
index f3eb615..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned b/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
deleted file mode 100644
index aefdb6b..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/tombstoned
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so b/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
deleted file mode 100644
index c71dcfb..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_bias_x86/vdso.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
deleted file mode 100644
index 46b6f45..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64 b/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
deleted file mode 100644
index ab0ef8f..0000000
--- a/libunwindstack/tests/files/offline/eh_frame_hdr_begin_x86_64/unwind_test64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so b/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
deleted file mode 100644
index f30384c..0000000
--- a/libunwindstack/tests/files/offline/empty_arm64/libbinder.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/libc.so b/libunwindstack/tests/files/offline/empty_arm64/libc.so
deleted file mode 100644
index b05dcaf..0000000
--- a/libunwindstack/tests/files/offline/empty_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/empty_arm64/netd b/libunwindstack/tests/files/offline/empty_arm64/netd
deleted file mode 100644
index 8a72e94..0000000
--- a/libunwindstack/tests/files/offline/empty_arm64/netd
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so b/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
deleted file mode 100644
index e4283e6..0000000
--- a/libunwindstack/tests/files/offline/gnu_debugdata_arm/libandroid_runtime.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
deleted file mode 100644
index def299e..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/dalvikvm32
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libart.so b/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
deleted file mode 100644
index 0527893..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libart.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
deleted file mode 100644
index 8559056..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libartd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
deleted file mode 100644
index 06dbf10..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libarttestd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm/libc.so b/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
deleted file mode 100644
index 9894e66..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
deleted file mode 100644
index 76ffad9..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/dalvikvm32
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
deleted file mode 100644
index 92ed991..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/libartd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
deleted file mode 100644
index 5efae02..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/libarttestd.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
deleted file mode 100644
index 9c78790..0000000
--- a/libunwindstack/tests/files/offline/jit_debug_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
deleted file mode 100644
index e667883..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
deleted file mode 100644
index 9a1d714..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libart.so b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
deleted file mode 100644
index 09ba495..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/libart.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libc.so b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
deleted file mode 100644
index 39c9025..0000000
--- a/libunwindstack/tests/files/offline/jit_map_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
deleted file mode 100644
index 7bb7156..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64 b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
deleted file mode 100644
index 00a3896..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/linker64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
deleted file mode 100644
index 3a75b8f..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso b/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
deleted file mode 100644
index 4940916..0000000
--- a/libunwindstack/tests/files/offline/load_bias_different_section_bias_arm64/vdso
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
deleted file mode 100644
index 63383d0..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
deleted file mode 100644
index a30e599..0000000
--- a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libc.so b/libunwindstack/tests/files/offline/offset_arm/libc.so
deleted file mode 100644
index 9f5c8ca..0000000
--- a/libunwindstack/tests/files/offline/offset_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test b/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
deleted file mode 100644
index 7a30bfa..0000000
--- a/libunwindstack/tests/files/offline/offset_arm/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/libc.so b/libunwindstack/tests/files/offline/pauth_pc_arm64/libc.so
deleted file mode 100644
index def2b02..0000000
--- a/libunwindstack/tests/files/offline/pauth_pc_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/pauth_pc_arm64/toybox b/libunwindstack/tests/files/offline/pauth_pc_arm64/toybox
deleted file mode 100644
index 1d33935..0000000
--- a/libunwindstack/tests/files/offline/pauth_pc_arm64/toybox
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
deleted file mode 100644
index 20008fd..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
deleted file mode 100644
index b90933b..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/linker64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
deleted file mode 100644
index 205ebd4..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_arm64/vdso.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
deleted file mode 100644
index 20008fd..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64 b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
deleted file mode 100644
index b90933b..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/linker64
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
deleted file mode 100644
index 205ebd4..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_memory_only_arm64/vdso.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so b/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
deleted file mode 100644
index cac1dd9..0000000
--- a/libunwindstack/tests/files/offline/shared_lib_in_apk_single_map_arm64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/libc.so b/libunwindstack/tests/files/offline/signal_fde_x86/libc.so
deleted file mode 100644
index 5c882e4..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test b/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test
deleted file mode 100644
index 8dcff67..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so b/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so
deleted file mode 100644
index cea7336..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86_64/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test b/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test
deleted file mode 100644
index c48e84e..0000000
--- a/libunwindstack/tests/files/offline/signal_fde_x86_64/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so b/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
deleted file mode 100644
index f046624..0000000
--- a/libunwindstack/tests/files/offline/signal_load_bias_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test b/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
deleted file mode 100644
index f460dd6..0000000
--- a/libunwindstack/tests/files/offline/signal_load_bias_arm/libunwindstack_unit_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libbase.so b/libunwindstack/tests/files/offline/straddle_arm/libbase.so
deleted file mode 100644
index d1f16ee..0000000
--- a/libunwindstack/tests/files/offline/straddle_arm/libbase.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm/libc.so b/libunwindstack/tests/files/offline/straddle_arm/libc.so
deleted file mode 100644
index 4dc19ca..0000000
--- a/libunwindstack/tests/files/offline/straddle_arm/libc.so
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test b/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
deleted file mode 100644
index 092fc3a..0000000
--- a/libunwindstack/tests/files/offline/straddle_arm64/libunwindstack_test
+++ /dev/null
Binary files differ
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
index fd3499a..8486418 100644
--- a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
-#define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
+#pragma once
#include <elf.h>
#include <sys/mman.h>
@@ -36,7 +35,7 @@
#include <unwindstack/RegsX86_64.h>
#include "../ElfFake.h"
-#include "../MemoryFake.h"
+#include "utils/MemoryFake.h"
#include "fuzzer/FuzzedDataProvider.h"
@@ -81,4 +80,3 @@
std::shared_ptr<unwindstack::Memory> memory,
uint max_libraries, uint max_library_length,
unwindstack::ArchEnum arch);
-#endif // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
index f926ce7..41913f1 100644
--- a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp
@@ -23,9 +23,9 @@
#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
-#include "../MemoryFake.h"
#include "UnwinderComponentCreator.h"
#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/MemoryFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tools/share_common_elfs.sh b/libunwindstack/tools/share_common_elfs.sh
new file mode 100755
index 0000000..d6e79a1
--- /dev/null
+++ b/libunwindstack/tools/share_common_elfs.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.
+
+### Usage: shares_common_elfs.sh [-m] [-a] <path/to/dump/directory/or/elf>
+###
+### This script is used to eliminate copies of the same ELF file(s) across
+### subdirectories of 'offline_files/' by placing common ELF(s) in
+### 'offline_files/common'. The build id is appended to each ELF name to allow
+### for different versions of the same shared library. Unless -a is used, the
+### path must be a valid ELF file.
+###
+### Options:
+### -m
+### If the ELF(s) does not currently reside in the 'common' directory, move
+### the elf to 'common' and rename it.
+### -a
+### Eliminate copies for all ELFs in a directory. The path specified point
+### to a directory and NOT an individual ELF file.
+
+usage() {
+ grep '^###' $0 | sed -e 's/^###//'
+ exit 1
+}
+
+update_elf() {
+ local elf_path="$1"
+ local move_if_not_in_common="$2"
+
+ local basename=$(basename "$elf_path")
+ local maps_path=$(dirname "$elf_path")"/maps.txt"
+ local build_id=$(readelf -n "$elf_path" | grep -oP 'Build ID: \K([\w\d]+)')
+ local new_elf_name="${basename}_${build_id}"
+ local common_dir="${ANDROID_BUILD_TOP}/system/unwinding/libunwindstack/offline_files/common/"
+
+ if [[ -f "${common_dir}${new_elf_name}" ]]; then
+ # If the ELF is already found in the common directory, delete the local copy.
+ git rm "$elf_path"
+ elif [[ $move_if_not_in_common == true ]]; then
+ # If the ELF is not found in the common directory and the `move_if_not_common`
+ # flag was set, move the local ELF to the common directory.
+ git mv "$elf_path" "${common_dir}${new_elf_name}"
+ else
+ # The ELF was not found in the common directory so exit this function.
+ return 0
+ fi
+
+ # Replace the name of the elf we just deleted/moved to the relative
+ # path to that ELF in the common directory.
+ local elf_dir_path=$(dirname "$elf_path")
+ local rel_path_to_common=$(realpath --relative-to="$elf_dir_path" "$common_dir")
+ sed -i -e "s/${basename}/${rel_path_to_common}\/${new_elf_name}/g" "$maps_path"
+}
+
+is_an_elf() {
+ if [[ $(head -c 4 $1 | cut -c2-) == "ELF" ]]; then
+ echo true
+ else
+ echo false
+ fi
+}
+
+main () {
+ set -e # abort the script if some error occurs.
+ local move_if_not_in_common=false
+ local update_all_elfs=false
+ while getopts ":hma" arg; do
+ case $arg in
+ m)
+ move_if_not_in_common=true
+ ;;
+ a)
+ update_all_elfs=true
+ ;;
+ h | *)
+ usage
+ ;;
+ esac
+ done
+ path=${@:$OPTIND:1}
+ if [[ -z $path ]]; then
+ usage
+ fi
+
+ if [[ $update_all_elfs == true ]]; then
+ if [[ ! -d $path || "${path: -1}" != "/" ]]; then
+ echo "$path is not a valid path to a directory." >&2
+ usage
+ fi
+ for elf_path in "${path}"*; do
+ if [[ $(is_an_elf $elf_path) == true ]]; then
+ update_elf $elf_path $move_if_not_in_common
+ fi
+ done
+ else
+ if [[ ! -f $path ]]; then
+ echo "$path is not a valid path to a file." >&2
+ usage
+ elif [[ $(is_an_elf $path) == false ]]; then
+ echo "$path is not a valid ELF file." >&2
+ else
+ update_elf $path $move_if_not_in_common
+ fi
+ fi
+}
+
+main "$@"
\ No newline at end of file
diff --git a/libunwindstack/tools/strip.py b/libunwindstack/tools/strip.py
new file mode 100755
index 0000000..bb47de7
--- /dev/null
+++ b/libunwindstack/tools/strip.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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.
+#
+
+import argparse, subprocess, re, os, glob, array, gzip
+
+DESCRIPTION = "This tool reduces ELF size using stripping and compression"
+
+STRIP_SECTIONS = [".text", ".rodata"]
+
+READELF_FORMAT = """
+ \s+(?P<index>[0-9\[\] ]+)
+ \s+(?P<name>[a-z_.]+)
+ \s+(?P<type>[A-Z_]+)
+ \s+(?P<address>[0-9a-f]+)
+ \s+(?P<offset>[0-9a-f]+)
+ \s+(?P<size>[0-9a-f]+)
+"""
+
+def strip(path):
+ proc = subprocess.run(["readelf", "--file-header", "--sections", path],
+ stdout=subprocess.PIPE, universal_newlines=True)
+ assert(proc.returncode == 0) # readelf command failed
+ sections = {m["name"] : m for m in re.finditer(READELF_FORMAT, proc.stdout, re.VERBOSE)}
+ for name in STRIP_SECTIONS:
+ if name == ".text" and os.path.basename(path) in ["vdso", "vdso.so", "libc.so"]:
+ continue # Stripping these libraries breaks signal handler unwinding.
+ section = sections.get(name)
+ if not section:
+ print("Warning: {} not found in {}".format(name, path))
+ if section and section["type"] != "NOBITS":
+ offset, size = int(section["offset"], 16), int(section["size"], 16) & ~1
+ with open(path, "r+b") as f:
+ f.seek(offset)
+ data = array.array('H') # 16-bit unsigned integer array.
+ data.frombytes(f.read(size))
+ # Preserve top bits for thumb so that we can still determine instruction size.
+ is_thumb = (name == ".text" and re.search("Machine:\s+ARM", proc.stdout))
+ for i in range(len(data)):
+ data[i] = 0xffff if is_thumb and (data[i] & 0xe000) == 0xe000 else 0
+ f.seek(offset)
+ f.write(data.tobytes())
+
+ # gzip-compress the file to take advantage of the zeroed sections.
+ with open(path, 'rb') as src, gzip.open(path + ".gz", 'wb') as dst:
+ dst.write(src.read())
+ os.remove(path)
+
+def main():
+ parser = argparse.ArgumentParser(description=DESCRIPTION)
+ parser.add_argument('target', nargs='+', help="ELF file or whole directory to strip")
+ args = parser.parse_args()
+
+ for path in args.target:
+ if os.path.isdir(path):
+ for path in glob.glob(os.path.join(path, "**/*"), recursive=True):
+ if os.path.isfile(path) and open(path, "rb").read(4) == b"\x7FELF":
+ strip(path)
+ else:
+ strip(path)
+
+if __name__ == '__main__':
+ main()
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 4d3a5f4..4a2bf65 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -14,20 +14,14 @@
* limitations under the License.
*/
+#include <cstdio>
#define _GNU_SOURCE 1
-#include <errno.h>
#include <inttypes.h>
-#include <signal.h>
-#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <algorithm>
+#include <cstdlib>
+#include <filesystem>
#include <memory>
#include <string>
#include <unordered_map>
@@ -36,13 +30,21 @@
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/Unwinder.h>
+#include "utils/ProcessTracer.h"
+#include <android-base/file.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
+namespace {
+constexpr pid_t kMinPid = 1;
+constexpr int kAllCmdOptionsParsed = -1;
+
struct map_info_t {
uint64_t start;
uint64_t end;
@@ -51,26 +53,46 @@
std::string name;
};
-static bool Attach(pid_t pid) {
- if (ptrace(PTRACE_SEIZE, pid, 0, 0) == -1) {
+int usage(int exit_code) {
+ fprintf(stderr, "USAGE: unwind_for_offline [-t] [-e FILE] [-f[FILE]] <PID>\n\n");
+ fprintf(stderr, "OPTIONS:\n");
+ fprintf(stderr, "-t\n");
+ fprintf(stderr, " Dump offline snapshot for all threads of <PID>.\n");
+ fprintf(stderr, "-e FILE\n");
+ fprintf(stderr, " If FILE is a valid ELF file included in /proc/<PID>/maps,\n");
+ fprintf(stderr, " unwind_for_offline will wait until the current frame (PC)\n");
+ fprintf(stderr, " lies within the .so file given by FILE. FILE should be\n");
+ fprintf(stderr, " base name of the path (the component following the final\n");
+ fprintf(stderr, " '/') rather than the fully qualified path.\n");
+ fprintf(stderr, "-f [FILE]\n");
+ fprintf(stderr, " Write info (e.g. frames and stack range) logs to a file\n");
+ fprintf(stderr, " rather than to the stdout/stderr. If FILE is not\n");
+ fprintf(stderr, " specified, the output file will be named 'output.txt'.\n");
+ return exit_code;
+}
+
+bool EnsureProcInDesiredElf(const std::string& elf_name, unwindstack::ProcessTracer& proc) {
+ if (proc.UsesSharedLibrary(proc.pid(), elf_name)) {
+ printf("Confirmed pid %d does use %s. Waiting for PC to lie within %s...\n", proc.pid(),
+ elf_name.c_str(), elf_name.c_str());
+ if (!proc.StopInDesiredElf(elf_name)) return false;
+ } else {
+ fprintf(stderr, "Process %d does not use library %s.\n", proc.pid(), elf_name.c_str());
return false;
}
+ return true;
+}
- if (ptrace(PTRACE_INTERRUPT, pid, 0, 0) == -1) {
- ptrace(PTRACE_DETACH, pid, 0, 0);
+bool CreateAndChangeDumpDir(std::filesystem::path thread_dir, pid_t tid, bool is_main_thread) {
+ std::string dir_name = std::to_string(tid);
+ if (is_main_thread) dir_name += "_main-thread";
+ thread_dir /= dir_name;
+ if (!std::filesystem::create_directory(thread_dir)) {
+ fprintf(stderr, "Failed to create directory for tid %d\n", tid);
return false;
}
-
- // Allow at least 1 second to attach properly.
- for (size_t i = 0; i < 1000; i++) {
- siginfo_t si;
- if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
- return true;
- }
- usleep(1000);
- }
- printf("%d: Failed to stop.\n", pid);
- return false;
+ std::filesystem::current_path(thread_dir);
+ return true;
}
bool SaveRegs(unwindstack::Regs* regs) {
@@ -86,7 +108,8 @@
return true;
}
-bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks) {
+bool SaveStack(pid_t pid, const std::vector<std::pair<uint64_t, uint64_t>>& stacks,
+ FILE* output_fp) {
for (size_t i = 0; i < stacks.size(); i++) {
std::string file_name;
if (stacks.size() != 1) {
@@ -101,11 +124,11 @@
std::vector<uint8_t> buffer(sp_end - sp_start);
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
if (!process_memory->Read(sp_start, buffer.data(), buffer.size())) {
- printf("Unable to read stack data.\n");
+ fprintf(stderr, "Unable to read stack data.\n");
return false;
}
- printf("Saving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
+ fprintf(output_fp, "\nSaving the stack 0x%" PRIx64 "-0x%" PRIx64 "\n", sp_start, sp_end);
std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(file_name.c_str(), "w+"), &fclose);
if (fp == nullptr) {
@@ -115,14 +138,15 @@
size_t bytes = fwrite(&sp_start, 1, sizeof(sp_start), fp.get());
if (bytes != sizeof(sp_start)) {
- printf("Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n", sizeof(sp_start),
- bytes);
+ fprintf(stderr, "Failed to write sp_start data: sizeof(sp_start) %zu, written %zu\n",
+ sizeof(sp_start), bytes);
return false;
}
bytes = fwrite(buffer.data(), 1, buffer.size(), fp.get());
if (bytes != buffer.size()) {
- printf("Failed to write all stack data: stack size %zu, written %zu\n", buffer.size(), bytes);
+ fprintf(stderr, "Failed to write all stack data: stack size %zu, written %zu\n",
+ buffer.size(), bytes);
return false;
}
}
@@ -135,7 +159,8 @@
if (info->name.empty()) {
cur_name = android::base::StringPrintf("anonymous_%" PRIx64, info->start);
} else {
- cur_name = android::base::StringPrintf("%s_%" PRIx64, basename(info->name.c_str()), info->start);
+ cur_name = android::base::StringPrintf(
+ "%s_%" PRIx64, android::base::Basename(info->name).c_str(), info->start);
}
std::vector<uint8_t> buffer(info->end - info->start);
@@ -143,7 +168,8 @@
// map, so read all that is readable.
size_t bytes = memory->Read(info->start, buffer.data(), buffer.size());
if (bytes == 0) {
- printf("Cannot read data from address %" PRIx64 " length %zu\n", info->start, buffer.size());
+ fprintf(stderr, "Cannot read data from address %" PRIx64 " length %zu\n", info->start,
+ buffer.size());
return false;
}
@@ -155,7 +181,8 @@
size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
if (bytes_written != bytes) {
- printf("Failed to write all data to file: bytes read %zu, written %zu\n", bytes, bytes_written);
+ fprintf(stderr, "Failed to write all data to file: bytes read %zu, written %zu\n", bytes,
+ bytes_written);
return false;
}
@@ -166,7 +193,7 @@
}
bool CopyElfFromFile(map_info_t* info, bool* file_copied) {
- std::string cur_name = basename(info->name.c_str());
+ std::string cur_name = android::base::Basename(info->name);
if (*file_copied) {
info->name = cur_name;
return true;
@@ -188,8 +215,8 @@
while ((bytes = fread(buffer.data(), 1, buffer.size(), fp.get())) > 0) {
size_t bytes_written = fwrite(buffer.data(), 1, bytes, output.get());
if (bytes_written != bytes) {
- printf("Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
- bytes_written);
+ fprintf(stderr, "Bytes written doesn't match bytes read: read %zu, written %zu\n", bytes,
+ bytes_written);
return false;
}
}
@@ -225,29 +252,33 @@
return;
}
- printf("Cannot save memory or file for map ");
+ fprintf(stderr, "Cannot save memory or file for map ");
if (!info->name.empty()) {
- printf("%s\n", info->name.c_str());
+ fprintf(stderr, "%s\n", info->name.c_str());
} else {
- printf("anonymous:%" PRIx64 "\n", info->start);
+ fprintf(stderr, "anonymous:%" PRIx64 "\n", info->start);
}
}
-int SaveData(pid_t pid) {
- unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(pid);
+bool SaveData(pid_t tid, const std::filesystem::path& cwd, bool is_main_thread, FILE* output_fp) {
+ fprintf(output_fp, "-------------------- tid = %d %s--------------------\n", tid,
+ is_main_thread ? "(main thread) " : "--------------");
+ unwindstack::Regs* regs = unwindstack::Regs::RemoteGet(tid);
if (regs == nullptr) {
- printf("Unable to get remote reg data.\n");
- return 1;
+ fprintf(stderr, "Unable to get remote reg data.\n");
+ return false;
}
+ if (!CreateAndChangeDumpDir(cwd, tid, is_main_thread)) return false;
+
// Save the current state of the registers.
if (!SaveRegs(regs)) {
- return 1;
+ return false;
}
// Do an unwind so we know how much of the stack to save, and what
// elf files are involved.
- unwindstack::UnwinderFromPid unwinder(1024, pid);
+ unwindstack::UnwinderFromPid unwinder(1024, tid);
unwinder.SetRegs(regs);
uint64_t sp = regs->sp();
unwinder.Unwind();
@@ -256,7 +287,7 @@
std::vector<std::pair<uint64_t, uint64_t>> stacks;
unwindstack::Maps* maps = unwinder.GetMaps();
uint64_t sp_map_start = 0;
- unwindstack::MapInfo* map_info = maps->Find(sp);
+ auto map_info = maps->Find(sp);
if (map_info != nullptr) {
stacks.emplace_back(std::make_pair(sp, map_info->end()));
sp_map_start = map_info->start();
@@ -269,35 +300,34 @@
sp_map_start = map_info->start();
}
- if (maps_by_start.count(frame.map_start) == 0) {
- map_info = maps->Find(frame.map_start);
+ if (maps_by_start.count(frame.map_info->start()) == 0) {
if (map_info == nullptr) {
continue;
}
- auto info = FillInAndGetMapInfo(maps_by_start, map_info);
+ auto info = FillInAndGetMapInfo(maps_by_start, map_info.get());
bool file_copied = false;
SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
// If you are using a a linker that creates two maps (one read-only, one
// read-executable), it's necessary to capture the previous map
// information if needed.
- unwindstack::MapInfo* prev_map = map_info->prev_map();
+ auto prev_map = map_info->prev_map();
if (prev_map != nullptr && map_info->offset() != 0 && prev_map->offset() == 0 &&
prev_map->flags() == PROT_READ && map_info->name() == prev_map->name() &&
maps_by_start.count(prev_map->start()) == 0) {
- info = FillInAndGetMapInfo(maps_by_start, prev_map);
+ info = FillInAndGetMapInfo(maps_by_start, prev_map.get());
SaveMapInformation(unwinder.GetProcessMemory(), info, &file_copied);
}
}
}
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
- printf("%s\n", unwinder.FormatFrame(i).c_str());
+ fprintf(output_fp, "%s\n", unwinder.FormatFrame(i).c_str());
}
- if (!SaveStack(pid, stacks)) {
- return 1;
+ if (!SaveStack(tid, stacks, output_fp)) {
+ return false;
}
std::vector<std::pair<uint64_t, map_info_t>> sorted_maps(maps_by_start.begin(),
@@ -305,8 +335,8 @@
std::sort(sorted_maps.begin(), sorted_maps.end(),
[](auto& a, auto& b) { return a.first < b.first; });
- std::unique_ptr<FILE, decltype(&fclose)> fp(fopen("maps.txt", "w+"), &fclose);
- if (fp == nullptr) {
+ std::unique_ptr<FILE, decltype(&fclose)> map_fp(fopen("maps.txt", "w+"), &fclose);
+ if (map_fp == nullptr) {
perror("Failed to create maps.txt");
return false;
}
@@ -323,32 +353,89 @@
if (map.flags & PROT_EXEC) {
perms[2] = 'x';
}
- fprintf(fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end, perms,
- map.offset);
+ fprintf(map_fp.get(), "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " 00:00 0", map.start, map.end,
+ perms, map.offset);
if (!map.name.empty()) {
- fprintf(fp.get(), " %s", map.name.c_str());
+ fprintf(map_fp.get(), " %s", map.name.c_str());
}
- fprintf(fp.get(), "\n");
+ fprintf(map_fp.get(), "\n");
}
- return 0;
+ fprintf(output_fp, "------------------------------------------------------------------\n");
+ return true;
}
+} // namespace
int main(int argc, char** argv) {
- if (argc != 2) {
- printf("Usage: unwind_for_offline <PID>\n");
- return 1;
+ if (argc < 2) return usage(EXIT_FAILURE);
+
+ bool dump_threads = false;
+ std::string elf_name;
+ std::unique_ptr<FILE, decltype(&fclose)> output_fp(nullptr, &fclose);
+ int opt;
+ while ((opt = getopt(argc, argv, ":te:f::")) != kAllCmdOptionsParsed) {
+ switch (opt) {
+ case 't': {
+ dump_threads = true;
+ break;
+ }
+ case 'e': {
+ elf_name = optarg;
+ if (elf_name == "-f") {
+ fprintf(stderr, "Missing argument for option e.\n");
+ return usage(EXIT_FAILURE);
+ }
+ break;
+ }
+ case 'f': {
+ const std::string& output_filename = optarg != nullptr ? optarg : "output.txt";
+ if (optind == argc - 2) {
+ fprintf(stderr, "Ensure there is no space between '-f' and the filename provided.\n");
+ return usage(EXIT_FAILURE);
+ }
+ output_fp.reset(fopen(output_filename.c_str(), "a"));
+ break;
+ }
+ case '?': {
+ if (isprint(optopt))
+ fprintf(stderr, "Unknown option `-%c'.\n", optopt);
+ else
+ fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
+ return usage(EXIT_FAILURE);
+ }
+ case ':': {
+ fprintf(stderr, "Missing arg for option %c.\n", optopt);
+ return usage(EXIT_FAILURE);
+ }
+ default: {
+ return usage(EXIT_FAILURE);
+ }
+ }
+ }
+ if (optind != argc - 1) return usage(EXIT_FAILURE);
+
+ pid_t pid;
+ if (!android::base::ParseInt(argv[optind], &pid, kMinPid, std::numeric_limits<pid_t>::max()))
+ return usage(EXIT_FAILURE);
+
+ unwindstack::ProcessTracer proc(pid, dump_threads);
+ if (!proc.Stop()) return EXIT_FAILURE;
+ if (!elf_name.empty()) {
+ if (!EnsureProcInDesiredElf(elf_name, proc)) return EXIT_FAILURE;
+ }
+ if (!output_fp) output_fp.reset(stdout);
+ std::filesystem::path cwd = std::filesystem::current_path();
+
+ if (!proc.Attach(proc.pid())) return EXIT_FAILURE;
+ if (!SaveData(proc.pid(), cwd, /*is_main_thread=*/proc.IsTracingThreads(), output_fp.get()))
+ return EXIT_FAILURE;
+ if (!proc.Detach(proc.pid())) return EXIT_FAILURE;
+ for (const pid_t& tid : proc.tids()) {
+ if (!proc.Attach(tid)) return EXIT_FAILURE;
+ if (!SaveData(tid, cwd, /*is_main_thread=*/false, output_fp.get())) return EXIT_FAILURE;
+ if (!proc.Detach(tid)) return EXIT_FAILURE;
}
- pid_t pid = atoi(argv[1]);
- if (!Attach(pid)) {
- printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
- return 1;
- }
-
- int return_code = SaveData(pid);
-
- ptrace(PTRACE_DETACH, pid, 0, 0);
-
- return return_code;
+ printf("\nSuccess!\n");
+ return EXIT_SUCCESS;
}
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index 65b65aa..adc93b4 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -103,9 +103,6 @@
}
int GetElfInfo(const char* file, uint64_t offset) {
- // Send all log messages to stdout.
- log_to_stdout(true);
-
Elf elf(Memory::CreateFileMemory(file, offset).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", file);
diff --git a/libunwindstack/tools/unwind_reg_info.cpp b/libunwindstack/tools/unwind_reg_info.cpp
index 3675f96..da34dc8 100644
--- a/libunwindstack/tools/unwind_reg_info.cpp
+++ b/libunwindstack/tools/unwind_reg_info.cpp
@@ -154,7 +154,6 @@
ArmExidx arm(nullptr, interface->memory(), nullptr);
- log_to_stdout(true);
arm.set_log(ARM_LOG_BY_REG);
arm.set_log_skip_execution(true);
arm.set_log_indent(1);
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index 2506fc1..a56ed5f 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -56,9 +56,6 @@
}
}
- // Send all log messages to stdout.
- unwindstack::log_to_stdout(true);
-
unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(argv[1], 0).release());
if (!elf.Init() || !elf.valid()) {
printf("%s is not a valid elf file.\n", argv[1]);
diff --git a/libunwindstack/utils/DwarfSectionImplFake.h b/libunwindstack/utils/DwarfSectionImplFake.h
new file mode 100644
index 0000000..54ed995
--- /dev/null
+++ b/libunwindstack/utils/DwarfSectionImplFake.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <unwindstack/DwarfSection.h>
+#include <unwindstack/Memory.h>
+
+namespace unwindstack {
+
+template <typename TypeParam>
+class DwarfSectionImplFake : public DwarfSectionImpl<TypeParam> {
+ public:
+ DwarfSectionImplFake(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
+ virtual ~DwarfSectionImplFake() = default;
+
+ bool Init(uint64_t, uint64_t, int64_t) override { return false; }
+
+ void GetFdes(std::vector<const DwarfFde*>*) override {}
+
+ const DwarfFde* GetFdeFromPc(uint64_t) override { return nullptr; }
+
+ uint64_t GetCieOffsetFromFde32(uint32_t) { return 0; }
+
+ uint64_t GetCieOffsetFromFde64(uint64_t) { return 0; }
+
+ uint64_t AdjustPcFromFde(uint64_t) override { return 0; }
+
+ void FakeSetCachedCieLocRegs(uint64_t offset, const DwarfLocations& loc_regs) {
+ this->cie_loc_regs_[offset] = loc_regs;
+ }
+ void FakeClearError() { this->last_error_.code = DWARF_ERROR_NONE; }
+};
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryFake.cpp b/libunwindstack/utils/MemoryFake.cpp
similarity index 97%
rename from libunwindstack/tests/MemoryFake.cpp
rename to libunwindstack/utils/MemoryFake.cpp
index 5695dfc..573285b 100644
--- a/libunwindstack/tests/MemoryFake.cpp
+++ b/libunwindstack/utils/MemoryFake.cpp
@@ -41,7 +41,7 @@
if (value != data_.end()) {
value->second = src[i];
} else {
- data_.insert({ addr, src[i] });
+ data_.insert({addr, src[i]});
}
}
}
@@ -58,4 +58,4 @@
return size;
}
-} // namespace unwindstack
+} // namespace unwindstack
\ No newline at end of file
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/utils/MemoryFake.h
similarity index 76%
rename from libunwindstack/tests/MemoryFake.h
rename to libunwindstack/utils/MemoryFake.h
index 20610a5..b7adfd9 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/utils/MemoryFake.h
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
+#pragma once
#include <stdint.h>
#include <string>
-#include <vector>
#include <unordered_map>
+#include <vector>
#include <unwindstack/Memory.h>
@@ -38,21 +37,13 @@
void SetMemoryBlock(uint64_t addr, size_t length, uint8_t value);
- void SetData8(uint64_t addr, uint8_t value) {
- SetMemory(addr, &value, sizeof(value));
- }
+ void SetData8(uint64_t addr, uint8_t value) { SetMemory(addr, &value, sizeof(value)); }
- void SetData16(uint64_t addr, uint16_t value) {
- SetMemory(addr, &value, sizeof(value));
- }
+ void SetData16(uint64_t addr, uint16_t value) { SetMemory(addr, &value, sizeof(value)); }
- void SetData32(uint64_t addr, uint32_t value) {
- SetMemory(addr, &value, sizeof(value));
- }
+ void SetData32(uint64_t addr, uint32_t value) { SetMemory(addr, &value, sizeof(value)); }
- void SetData64(uint64_t addr, uint64_t value) {
- SetMemory(addr, &value, sizeof(value));
- }
+ void SetData64(uint64_t addr, uint64_t value) { SetMemory(addr, &value, sizeof(value)); }
void SetMemory(uint64_t addr, std::vector<uint8_t> values) {
SetMemory(addr, values.data(), values.size());
@@ -80,5 +71,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/utils/OfflineUnwindUtils.cpp b/libunwindstack/utils/OfflineUnwindUtils.cpp
new file mode 100644
index 0000000..8cc544d
--- /dev/null
+++ b/libunwindstack/utils/OfflineUnwindUtils.cpp
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2021 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 <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <cstddef>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <zlib.h>
+
+#include <unwindstack/Arch.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineX86.h>
+#include <unwindstack/MachineX86_64.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
+#include <unwindstack/RegsX86_64.h>
+#include <unwindstack/Unwinder.h>
+
+#include "Check.h"
+#include "MemoryOffline.h"
+#include "utils/MemoryFake.h"
+
+#include "OfflineUnwindUtils.h"
+
+namespace unwindstack {
+
+void DecompressFiles(const std::string& directory) {
+ namespace fs = std::filesystem;
+ for (const auto& file : fs::recursive_directory_iterator(directory)) {
+ fs::path src_path = file.path();
+ if (src_path.extension() == ".gz") {
+ fs::path dst_path = fs::path(src_path).replace_extension(); // Remove .gz extension.
+ if (!fs::exists(dst_path) || fs::last_write_time(src_path) > fs::last_write_time(dst_path)) {
+ gzFile src = gzopen(src_path.c_str(), "rb");
+ CHECK(src != nullptr);
+ fs::path tmp_path = fs::path(src_path).replace_extension("." + std::to_string(getpid()));
+ std::ofstream tmp(tmp_path); // Temporary file to avoid races between unit tests.
+ char buffer[1024];
+ int size;
+ while ((size = gzread(src, buffer, sizeof(buffer))) > 0) {
+ tmp.write(buffer, size);
+ }
+ tmp.close();
+ gzclose(src);
+ fs::rename(tmp_path, dst_path);
+ }
+ }
+ }
+}
+
+void CreateLinks(const std::string& directory) {
+ namespace fs = std::filesystem;
+ for (const auto& file : fs::recursive_directory_iterator(directory)) {
+ fs::path src_path = file.path();
+ if (fs::is_regular_file(src_path) && src_path.filename() == "links.txt") {
+ std::string contents;
+ if (!android::base::ReadFileToString(src_path.c_str(), &contents)) {
+ errx(1, "Unable to read file: %s", src_path.c_str());
+ }
+ fs::path parent_path = src_path.parent_path();
+ std::vector<std::string> lines(android::base::Split(contents, "\n"));
+ for (auto line : lines) {
+ std::string trimmed_line(android::base::Trim(line));
+ if (trimmed_line.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> values(android::base::Split(trimmed_line, " "));
+ if (values.size() != 2) {
+ errx(1, "Invalid line in %s: line %s", src_path.c_str(), line.c_str());
+ }
+
+ // Create the symlink if it doesn't already exist.
+ fs::path target(parent_path);
+ target /= fs::path(values[0]);
+ fs::path source(parent_path);
+ source /= fs::path(values[1]);
+ if (!fs::exists(source)) {
+ // Ignore any errors, if this is running at the same time
+ // in multiple processes, then this might fail.
+ std::error_code ec;
+ fs::create_symlink(target, source, ec);
+ }
+ }
+ }
+ }
+}
+
+std::string GetOfflineFilesDirectory() {
+ std::string path = android::base::GetExecutableDirectory() + "/offline_files/";
+ DecompressFiles(path);
+ CreateLinks(path);
+ return path;
+}
+
+std::string DumpFrames(const Unwinder& unwinder) {
+ std::string str;
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ str += unwinder.FormatFrame(i) + "\n";
+ }
+ return str;
+}
+
+bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string* error_msg) {
+ MemoryOffline* memory = new MemoryOffline;
+ if (!memory->Init(file_name.c_str(), 0)) {
+ std::stringstream err_stream;
+ err_stream << "Failed to add stack '" << file_name << "' to stack memory.";
+ *error_msg = err_stream.str();
+ return false;
+ }
+ parts->Add(memory);
+
+ return true;
+}
+
+Regs* OfflineUnwindUtils::GetRegs(const std::string& initial_sample_name) const {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ std::string error_msg;
+ if (!IsValidUnwindSample(sample_name, &error_msg)) {
+ std::cerr << error_msg;
+ return nullptr;
+ }
+ return samples_.at(sample_name).regs.get();
+}
+
+Maps* OfflineUnwindUtils::GetMaps(const std::string& initial_sample_name) const {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ std::string error_msg;
+ if (!IsValidUnwindSample(sample_name, &error_msg)) {
+ std::cerr << error_msg;
+ return nullptr;
+ }
+ return samples_.at(sample_name).maps.get();
+}
+
+std::shared_ptr<Memory> OfflineUnwindUtils::GetProcessMemory(
+ const std::string& initial_sample_name) const {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ std::string error_msg;
+ if (!IsValidUnwindSample(sample_name, &error_msg)) {
+ std::cerr << error_msg;
+ return nullptr;
+ }
+ return samples_.at(sample_name).process_memory;
+}
+
+JitDebug* OfflineUnwindUtils::GetJitDebug(const std::string& initial_sample_name) const {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ std::string error_msg;
+ if (!IsValidUnwindSample(sample_name, &error_msg)) {
+ std::cerr << error_msg;
+ return nullptr;
+ }
+ return samples_.at(sample_name).jit_debug.get();
+}
+
+const std::string* OfflineUnwindUtils::GetOfflineFilesPath(
+ const std::string& initial_sample_name) const {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ std::string error_msg;
+ if (!IsValidUnwindSample(sample_name, &error_msg)) {
+ std::cerr << error_msg;
+ return nullptr;
+ }
+ return &samples_.at(sample_name).offline_files_path;
+}
+
+const std::string* OfflineUnwindUtils::GetFrameInfoFilepath(
+ const std::string& initial_sample_name) const {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ std::string error_msg;
+ if (!IsValidUnwindSample(sample_name, &error_msg)) {
+ std::cerr << error_msg;
+ return nullptr;
+ }
+ return &samples_.at(sample_name).frame_info_filepath;
+}
+
+bool OfflineUnwindUtils::Init(const std::vector<UnwindSampleInfo>& sample_infos,
+ std::string* error_msg) {
+ // Save the current path so the caller can switch back to it later.
+ cwd_ = std::filesystem::current_path();
+
+ // Fill in the unwind samples.
+ std::stringstream err_stream;
+ for (const auto& sample_info : sample_infos) {
+ std::string offline_files_full_path =
+ GetOfflineFilesDirectory() + sample_info.offline_files_dir;
+ if (!std::filesystem::exists(offline_files_full_path)) {
+ err_stream << "Offline files directory '" << offline_files_full_path << "' does not exist.";
+ *error_msg = err_stream.str();
+ return false;
+ }
+ std::string frame_info_filepath = offline_files_full_path + sample_info.frame_info_filename;
+
+ std::string map_buffer;
+ if (!android::base::ReadFileToString((offline_files_full_path + "maps.txt"), &map_buffer)) {
+ err_stream << "Failed to read from '" << offline_files_full_path << "maps.txt' into memory.";
+ *error_msg = err_stream.str();
+ return false;
+ }
+
+ // CreateMaps, CreatRegs, and Create*Memory may need to be called later by the client. So we
+ // need to create the sample now in case the flags are set to call these methods in Init.
+ const std::string& sample_name = sample_info.offline_files_dir;
+ samples_.emplace(sample_name, (UnwindSample){
+ std::move(offline_files_full_path),
+ std::move(frame_info_filepath), std::move(map_buffer),
+ nullptr, // regs
+ nullptr, // maps
+ std::make_shared<MemoryFake>(), // process_memory
+ nullptr, // jit_debug
+ });
+ UnwindSample& sample = samples_.at(sample_name);
+
+ if (sample_info.create_maps) {
+ if (!CreateMaps(error_msg, sample_name)) return false;
+ }
+ if (!CreateRegs(sample_info.arch, error_msg, sample_name)) return false;
+
+ switch (sample_info.memory_flag) {
+ case ProcessMemoryFlag::kNone: {
+ if (!CreateProcessMemory(error_msg, sample_name)) return false;
+ break;
+ }
+ case ProcessMemoryFlag::kIncludeJitMemory: {
+ if (!CreateProcessMemory(error_msg, sample_name)) return false;
+ sample.jit_debug = CreateJitDebug(sample.regs->Arch(), sample.process_memory);
+ break;
+ }
+ case ProcessMemoryFlag::kNoMemory: {
+ break;
+ }
+ default: {
+ std::stringstream err_stream;
+ err_stream << "Unknown memory type for sample '" << sample_name << "'.";
+ *error_msg = err_stream.str();
+ return false;
+ }
+ }
+ }
+ initted_ = true;
+ return true;
+}
+
+bool OfflineUnwindUtils::Init(const UnwindSampleInfo& sample_info, std::string* error_msg) {
+ if (Init(std::vector<UnwindSampleInfo>{sample_info}, error_msg)) {
+ if (!ChangeToSampleDirectory(error_msg)) return false;
+ return true;
+ }
+ return false;
+}
+
+bool OfflineUnwindUtils::ChangeToSampleDirectory(std::string* error_msg,
+ const std::string& initial_sample_name) const {
+ if (!initted_) {
+ *error_msg =
+ "Cannot change to sample directory because OfflineUnwindUtils::Init has not been called.";
+ return false;
+ }
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+
+ std::filesystem::current_path(std::filesystem::path(samples_.at(sample_name).offline_files_path));
+ return true;
+}
+
+bool OfflineUnwindUtils::GetExpectedNumFrames(size_t* expected_num_frames, std::string* error_msg,
+ const std::string& initial_sample_name) const {
+ if (!initted_) {
+ *error_msg =
+ "Cannot get expected number of frames of a sample because OfflineUnwindUtils::Init has not "
+ "been called.";
+ return false;
+ }
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+
+ const std::string& sample_frames_path = samples_.at(sample_name).frame_info_filepath;
+ if (!std::filesystem::exists(sample_frames_path)) {
+ std::stringstream err_stream;
+ err_stream << "Offline files directory '" << sample_frames_path << "' does not exist.";
+ *error_msg = err_stream.str();
+ return false;
+ }
+
+ std::ifstream in(sample_frames_path);
+ in.unsetf(std::ios_base::skipws); // Ensure that we do not skip newlines.
+ *expected_num_frames =
+ std::count(std::istream_iterator<char>(in), std::istream_iterator<char>(), '\n');
+
+ return true;
+}
+
+bool OfflineUnwindUtils::CreateMaps(std::string* error_msg,
+ const std::string& initial_sample_name) {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+ UnwindSample& sample = samples_.at(sample_name);
+
+ sample.maps.reset(new BufferMaps(sample.map_buffer.c_str()));
+ if (!sample.maps->Parse()) {
+ *error_msg = "Failed to parse offline maps.";
+ return false;
+ }
+ return true;
+}
+
+bool OfflineUnwindUtils::CreateProcessMemory(std::string* error_msg,
+ const std::string& initial_sample_name) {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+ UnwindSample& sample = samples_.at(sample_name);
+
+ // Construct process memory from all descriptor, stack, entry, and jit files
+ auto memory = std::make_unique<MemoryOfflineParts>();
+ bool data_files_found = false;
+ for (const auto& file : std::filesystem::directory_iterator(sample.offline_files_path)) {
+ std::string filename = file.path().string();
+ if (std::regex_match(filename,
+ std::regex("^(.+)\\/(descriptor|stack|entry|jit)(\\d*)\\.data$"))) {
+ data_files_found = true;
+ if (!AddMemory(filename, memory.get(), error_msg)) return false;
+ }
+ }
+ if (!data_files_found) {
+ *error_msg = "No memory (stack, JIT, etc.) data files found.";
+ return false;
+ }
+
+ sample.process_memory.reset(memory.release());
+ return true;
+}
+
+namespace {
+template <typename AddressType>
+bool ReadRegs(RegsImpl<AddressType>* regs,
+ const std::unordered_map<std::string, uint32_t>& name_to_reg, std::string* error_msg,
+ const std::string& offline_files_path) {
+ std::stringstream err_stream;
+ FILE* fp = fopen((offline_files_path + "regs.txt").c_str(), "r");
+ if (fp == nullptr) {
+ err_stream << "Error opening file '" << offline_files_path << "regs.txt': " << strerror(errno);
+ *error_msg = err_stream.str();
+ return false;
+ }
+
+ while (!feof(fp)) {
+ uint64_t value;
+ char reg_name[100];
+ if (fscanf(fp, "%s %" SCNx64 "\n", reg_name, &value) != 2) {
+ err_stream << "Failed to read in register name/values from '" << offline_files_path
+ << "regs.txt'.";
+ *error_msg = err_stream.str();
+ return false;
+ }
+ std::string name(reg_name);
+ if (!name.empty()) {
+ // Remove the : from the end.
+ name.resize(name.size() - 1);
+ }
+ auto entry = name_to_reg.find(name);
+ if (entry == name_to_reg.end()) {
+ err_stream << "Unknown register named " << reg_name;
+ *error_msg = err_stream.str();
+ return false;
+ }
+ (*regs)[entry->second] = value;
+ }
+ fclose(fp);
+ return true;
+}
+} // namespace
+
+bool OfflineUnwindUtils::CreateRegs(ArchEnum arch, std::string* error_msg,
+ const std::string& initial_sample_name) {
+ const std::string& sample_name = GetAdjustedSampleName(initial_sample_name);
+ if (!IsValidUnwindSample(sample_name, error_msg)) return false;
+ auto& regs = samples_.at(sample_name).regs;
+ const auto& offline_files_path = samples_.at(sample_name).offline_files_path;
+
+ switch (arch) {
+ case ARCH_ARM: {
+ RegsArm* regs_impl = new RegsArm;
+ regs.reset(regs_impl);
+ if (!ReadRegs<uint32_t>(regs_impl, arm_regs_, error_msg, offline_files_path)) return false;
+ break;
+ }
+ case ARCH_ARM64: {
+ RegsArm64* regs_impl = new RegsArm64;
+ regs.reset(regs_impl);
+ if (!ReadRegs<uint64_t>(regs_impl, arm64_regs_, error_msg, offline_files_path)) return false;
+ break;
+ }
+ case ARCH_X86: {
+ RegsX86* regs_impl = new RegsX86;
+ regs.reset(regs_impl);
+ if (!ReadRegs<uint32_t>(regs_impl, x86_regs_, error_msg, offline_files_path)) return false;
+ break;
+ }
+ case ARCH_X86_64: {
+ RegsX86_64* regs_impl = new RegsX86_64;
+ regs.reset(regs_impl);
+ if (!ReadRegs<uint64_t>(regs_impl, x86_64_regs_, error_msg, offline_files_path)) return false;
+ break;
+ }
+ default:
+ *error_msg = "Unknown architechture " + std::to_string(arch);
+ return false;
+ }
+
+ return true;
+}
+
+const std::string& OfflineUnwindUtils::GetAdjustedSampleName(
+ const std::string& initial_sample_name) const {
+ // Only return the first entry in the sample map if this is the single unwind use case.
+ // Otherwise return the inputted sample name so we can check if that is a valid sample name.
+ if (initial_sample_name == kSingleSample && samples_.size() == 1) {
+ return samples_.begin()->first;
+ }
+ return initial_sample_name;
+}
+
+bool OfflineUnwindUtils::IsValidUnwindSample(const std::string& sample_name,
+ std::string* error_msg) const {
+ if (samples_.find(sample_name) == samples_.end()) {
+ std::stringstream err_stream;
+ err_stream << "Invalid sample name (offline file directory) '" << sample_name << "'.";
+ if (sample_name == kSingleSample) {
+ err_stream << " An explicit sample name must be provided for the multiple unwind use case "
+ "of OfflineUnwindUtils (i.e. should not use the default sample name).";
+ }
+ *error_msg = err_stream.str();
+ return false;
+ }
+ return true;
+}
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::arm_regs_ = {
+ {"r0", ARM_REG_R0}, {"r1", ARM_REG_R1}, {"r2", ARM_REG_R2}, {"r3", ARM_REG_R3},
+ {"r4", ARM_REG_R4}, {"r5", ARM_REG_R5}, {"r6", ARM_REG_R6}, {"r7", ARM_REG_R7},
+ {"r8", ARM_REG_R8}, {"r9", ARM_REG_R9}, {"r10", ARM_REG_R10}, {"r11", ARM_REG_R11},
+ {"ip", ARM_REG_R12}, {"sp", ARM_REG_SP}, {"lr", ARM_REG_LR}, {"pc", ARM_REG_PC},
+};
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::arm64_regs_ = {
+ {"x0", ARM64_REG_R0}, {"x1", ARM64_REG_R1}, {"x2", ARM64_REG_R2},
+ {"x3", ARM64_REG_R3}, {"x4", ARM64_REG_R4}, {"x5", ARM64_REG_R5},
+ {"x6", ARM64_REG_R6}, {"x7", ARM64_REG_R7}, {"x8", ARM64_REG_R8},
+ {"x9", ARM64_REG_R9}, {"x10", ARM64_REG_R10}, {"x11", ARM64_REG_R11},
+ {"x12", ARM64_REG_R12}, {"x13", ARM64_REG_R13}, {"x14", ARM64_REG_R14},
+ {"x15", ARM64_REG_R15}, {"x16", ARM64_REG_R16}, {"x17", ARM64_REG_R17},
+ {"x18", ARM64_REG_R18}, {"x19", ARM64_REG_R19}, {"x20", ARM64_REG_R20},
+ {"x21", ARM64_REG_R21}, {"x22", ARM64_REG_R22}, {"x23", ARM64_REG_R23},
+ {"x24", ARM64_REG_R24}, {"x25", ARM64_REG_R25}, {"x26", ARM64_REG_R26},
+ {"x27", ARM64_REG_R27}, {"x28", ARM64_REG_R28}, {"x29", ARM64_REG_R29},
+ {"sp", ARM64_REG_SP}, {"lr", ARM64_REG_LR}, {"pc", ARM64_REG_PC},
+ {"pst", ARM64_REG_PSTATE},
+};
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::x86_regs_ = {
+ {"eax", X86_REG_EAX}, {"ebx", X86_REG_EBX}, {"ecx", X86_REG_ECX},
+ {"edx", X86_REG_EDX}, {"ebp", X86_REG_EBP}, {"edi", X86_REG_EDI},
+ {"esi", X86_REG_ESI}, {"esp", X86_REG_ESP}, {"eip", X86_REG_EIP},
+};
+
+std::unordered_map<std::string, uint32_t> OfflineUnwindUtils::x86_64_regs_ = {
+ {"rax", X86_64_REG_RAX}, {"rbx", X86_64_REG_RBX}, {"rcx", X86_64_REG_RCX},
+ {"rdx", X86_64_REG_RDX}, {"r8", X86_64_REG_R8}, {"r9", X86_64_REG_R9},
+ {"r10", X86_64_REG_R10}, {"r11", X86_64_REG_R11}, {"r12", X86_64_REG_R12},
+ {"r13", X86_64_REG_R13}, {"r14", X86_64_REG_R14}, {"r15", X86_64_REG_R15},
+ {"rdi", X86_64_REG_RDI}, {"rsi", X86_64_REG_RSI}, {"rbp", X86_64_REG_RBP},
+ {"rsp", X86_64_REG_RSP}, {"rip", X86_64_REG_RIP},
+};
+
+} // namespace unwindstack
diff --git a/libunwindstack/utils/OfflineUnwindUtils.h b/libunwindstack/utils/OfflineUnwindUtils.h
new file mode 100644
index 0000000..df89c68
--- /dev/null
+++ b/libunwindstack/utils/OfflineUnwindUtils.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <unwindstack/Arch.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include "MemoryOffline.h"
+
+// These utils facilitate performing offline unwinds. Offline unwinds are similar to local
+// unwinds, however, instead of pausing the process to gather the current execution state
+// (stack, registers, Elf / maps), a snapshot of the process is taken. This snapshot data
+// is used at a later time (when the process is no longer running) to unwind the process
+// at the point the snapshot was taken.
+//
+// Offline unwinds simulate one of the most common use cases of the Unwinder. These types of
+// unwinds are performed by two of the largest clients of libunwindstack: Perfetto and Simpleperf.
+//
+// Offline unwind snapshots were obtained using the following approach:
+// 1. (Optional) Flash a virtual or physical device with the internal Android build rather than
+// an AOSP build to have additional and more complex apps to unwind.
+// 2. Determine the pid of the app/process you want to unwind. View all of the running
+// processes with `adb shell ps -A` or `adb shell ps -A | grep name.of.process` if you know
+// the (package) name of the process.
+// 3. (Optional) If you want to ensure that an application is compiled or that the compiled code is
+// erased (e.g. want interpreter / JIT frames in the unwind), run `adb shell cmd package compile`
+// based on the options provided here
+// (https://source.android.com/devices/tech/dalvik/jit-compiler).
+// 4. Ensure the process is running and in a "desired state" when you execute
+// `adb shell /bin/unwind_for_offline [options] pid`. For example:
+// a. If you are unwinding the bluetooth process and want the unwind to contain the bluetooth
+// ELF (libbluetooth.so), try to pair with a device over bluetooth. Make sure you use the
+// `-t` and `-e` flags.
+// b. You will likely see more variation in the thread snapshots (especially if you are trying
+// to capture JIT/interpreter frames) if you ensure the app is not-idle when you run
+// `unwind_for_offline`. E.g. immediately run unwind_for_offline after searching for a
+// landmark in Google Maps.
+// 5. Grab the desired snapshot directories with `adb pull ...`
+// 6. (Optional) Reduce the size of copied ELFs:
+// a. Use tools/share_common_elfs.sh to eliminate copies of the same ELF files that are already
+// used by other 'libunwindstack/offline_files/' subdirectories.
+// b. Strip ELFs of all sections that are not needed for unwinding and/or symbolization.
+// c. Compress/Zip the entire snapshot directory.
+// 7. Use the path to the snapshot directory(ies) for the `offline_files_dirs` parameter to
+// `OfflineUnwindUtils::Init`.
+//
+// See b/192012600 for additional information regarding Offline Unwind Benchmarks.
+namespace unwindstack {
+
+void DecompressFiles(const std::string& directory);
+
+std::string GetOfflineFilesDirectory();
+
+std::string DumpFrames(const Unwinder& unwinder);
+
+bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string* error_msg);
+
+// Enum that indicates how `UnwindSample::process_memory` of `OfflineUnwindUtils::samples_`
+// should be initialized.
+enum class ProcessMemoryFlag {
+ kNone = 0,
+ kIncludeJitMemory,
+ kNoMemory,
+};
+
+// A `UnwindSampleInfo` object contains the information necessary for OfflineUnwindUtils
+// to initialize a single offline unwind sample.
+struct UnwindSampleInfo {
+ std::string offline_files_dir;
+ ArchEnum arch;
+ std::string frame_info_filename = "output.txt";
+ ProcessMemoryFlag memory_flag = ProcessMemoryFlag::kNone;
+ bool create_maps = true;
+};
+
+// The `OfflineUnwindUtils` class helps perform offline unwinds by handling the creation of the
+// `Regs`, `Maps`, and `Memory` objects needed for unwinding.
+//
+// `OfflineUnwindUtils` assists in two unwind use cases:
+// 1. Single unwinds: unwind from a single sample/snapshot (one set of offline unwind files).
+// 2. Consecutive/Multiple unwinds: unwind from a multiple samples/snapshots.
+//
+// `Init` contains two overloads for these two unwind cases. Other than `Init` and
+// `ReturnToCurrentWorkingDirectory`, the remainder of the public API includes a `sample_name`
+// parameter to indicate which sample/snapshot we are referencing. Specifying this value is
+// REQUIRED for the multiple unwind use case. However, in the single use case, the caller has
+// the choice of either providing the sample name or using the default value.
+class OfflineUnwindUtils {
+ public:
+ // If the sample name passed to Get* is an invalid sample, nullptr is returned.
+ Regs* GetRegs(const std::string& sample_name = kSingleSample) const;
+
+ Maps* GetMaps(const std::string& sample_name = kSingleSample) const;
+
+ std::shared_ptr<Memory> GetProcessMemory(const std::string& sample_name = kSingleSample) const;
+
+ JitDebug* GetJitDebug(const std::string& sample_name = kSingleSample) const;
+
+ const std::string* GetOfflineFilesPath(const std::string& sample_name = kSingleSample) const;
+
+ const std::string* GetFrameInfoFilepath(const std::string& sample_name = kSingleSample) const;
+
+ // Note: If the caller sets elements of `set_maps` to false or `memory_types` to
+ // kNoMemory, they are responsible for calling `CreateMaps` or `CreateProcessMemory` before
+ // expecting `GetMaps` or `GetProcessMemory` to return anything but nullptr.
+ bool Init(const std::vector<UnwindSampleInfo>& sample_infos, std::string* error_msg);
+
+ bool Init(const UnwindSampleInfo& sample_info, std::string* error_msg);
+
+ // This must be called explicitly for the multiple unwind use case sometime before
+ // Unwinder::Unwind is called. This is required because the Unwinder must init each
+ // ELF object with a MemoryFileAtOffset memory object. Because the maps.txt provides a relative
+ // path to the ELF files, we must be in the directory of the maps.txt when unwinding.
+ //
+ // Note: Init performs the check that this sample directory exists. If Init fails,
+ // `initted_` is not set to true and this function will return false.
+ bool ChangeToSampleDirectory(std::string* error_msg,
+ const std::string& initial_sample_name = kSingleSample) const;
+
+ void ReturnToCurrentWorkingDirectory() {
+ if (!cwd_.empty()) std::filesystem::current_path(cwd_);
+ }
+
+ bool GetExpectedNumFrames(size_t* expected_num_frames, std::string* error_msg,
+ const std::string& sample_name = kSingleSample) const;
+
+ bool CreateMaps(std::string* error_msg, const std::string& sample_name = kSingleSample);
+
+ bool CreateProcessMemory(std::string* error_msg, const std::string& sample_name = kSingleSample);
+
+ static constexpr char kSingleSample[] = "";
+
+ private:
+ // An `UnwindSample` encapsulates the information necessary to perform an offline unwind for a
+ // single offline sample/snapshot.
+ struct UnwindSample {
+ std::string offline_files_path;
+ std::string frame_info_filepath;
+ std::string map_buffer;
+ std::unique_ptr<Regs> regs;
+ std::unique_ptr<Maps> maps;
+ std::shared_ptr<Memory> process_memory;
+ std::unique_ptr<JitDebug> jit_debug;
+ };
+
+ bool CreateRegs(ArchEnum arch, std::string* error_msg,
+ const std::string& sample_name = kSingleSample);
+
+ // Needed to support using the default value `kSingleSample` for the single unwind use case.
+ const std::string& GetAdjustedSampleName(const std::string& sample_name) const;
+
+ bool IsValidUnwindSample(const std::string& sample_name, std::string* error_msg) const;
+
+ static std::unordered_map<std::string, uint32_t> arm_regs_;
+ static std::unordered_map<std::string, uint32_t> arm64_regs_;
+ static std::unordered_map<std::string, uint32_t> x86_regs_;
+ static std::unordered_map<std::string, uint32_t> x86_64_regs_;
+
+ std::string cwd_;
+ std::unordered_map<std::string, UnwindSample> samples_;
+ bool initted_ = false;
+};
+
+} // namespace unwindstack
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..5f67204
--- /dev/null
+++ b/libunwindstack/utils/PidUtils.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#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
diff --git a/libunwindstack/utils/ProcessTracer.cpp b/libunwindstack/utils/ProcessTracer.cpp
new file mode 100644
index 0000000..7f60ca0
--- /dev/null
+++ b/libunwindstack/utils/ProcessTracer.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2021 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 <fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <csignal>
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+#include <regex>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <procinfo/process.h>
+
+#include "ProcessTracer.h"
+
+namespace unwindstack {
+
+ProcessTracer::ProcessTracer(pid_t pid, bool is_tracing_threads)
+ : pid_(pid), is_tracing_threads_(is_tracing_threads) {
+ if (is_tracing_threads_) is_tracing_threads_ = InitProcessTids();
+}
+
+bool ProcessTracer::InitProcessTids() {
+ std::string error_msg;
+ if (!android::procinfo::GetProcessTids(pid_, &tids_, &error_msg)) {
+ fprintf(stderr,
+ "Failed to get process tids: %s. Reverting to tracing the "
+ "main thread only.\n",
+ error_msg.c_str());
+ return false;
+ }
+ if (tids_.erase(pid_) != 1) {
+ fprintf(stderr,
+ "Failed to erase the main thread from the thread id set. "
+ "Reverting to tracing the main thread only.\n");
+ return false;
+ }
+ return true;
+}
+
+ProcessTracer::~ProcessTracer() {
+ if (cur_attached_tid_ != kNoThreadAttached) Detach(cur_attached_tid_);
+ if (!is_running_) Resume();
+}
+
+bool ProcessTracer::Stop() {
+ if (kill(pid_, SIGSTOP) == kKillFailed) {
+ fprintf(stderr, "Failed to send stop signal to pid %d: %s\n", pid_, strerror(errno));
+ return false;
+ }
+ usleep(1000); // 1 ms. Without this sleep, any attempt to resume right away may fail.
+
+ is_running_ = false;
+ return true;
+}
+
+bool ProcessTracer::Resume() {
+ if (kill(pid_, SIGCONT) == kKillFailed) {
+ fprintf(stderr, "Failed to send continue signal to pid %d: %s\n", pid_, strerror(errno));
+ return false;
+ }
+ usleep(1000); // 1 ms. Without this sleep, any attempt to stop right away may fail.
+
+ is_running_ = true;
+ return true;
+}
+
+bool ProcessTracer::Detach(pid_t tid) {
+ if (tid != pid_ && tids_.find(tid) == tids_.end()) {
+ fprintf(stderr, "Tid %d does not belong to proc %d.\n", tid, pid_);
+ return false;
+ }
+
+ if (cur_attached_tid_ == kNoThreadAttached) {
+ fprintf(stderr, "Cannot detach because no thread is currently attached.\n");
+ return false;
+ }
+ if (is_running_ && !Stop()) return false;
+
+ if (ptrace(PTRACE_DETACH, tid, nullptr, nullptr) == kPtraceFailed) {
+ fprintf(stderr, "Failed to detach from tid %d: %s\n", tid, strerror(errno));
+ return false;
+ }
+
+ cur_attached_tid_ = kNoThreadAttached;
+ return true;
+}
+
+bool ProcessTracer::Attach(pid_t tid) {
+ if (tid != pid_ && tids_.find(tid) == tids_.end()) {
+ fprintf(stderr, "Tid %d does not belong to proc %d.\n", tid, pid_);
+ return false;
+ }
+
+ if (is_running_) Stop();
+ if (cur_attached_tid_ != kNoThreadAttached) {
+ fprintf(stderr, "Cannot attatch to tid %d. Already attached to tid %d.\n", tid,
+ cur_attached_tid_);
+ return false;
+ }
+
+ if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) == kPtraceFailed) {
+ fprintf(stderr, "Failed to attached to tid %d: %s\n", tid, strerror(errno));
+ return false;
+ }
+ int status;
+ if (waitpid(tid, &status, 0) == kWaitpidFailed) {
+ fprintf(stderr, "Failed to stop tid %d: %s\n", tid, strerror(errno));
+ return false;
+ }
+
+ cur_attached_tid_ = tid;
+ return true;
+}
+
+bool ProcessTracer::StopInDesiredElf(const std::string& elf_name) {
+ signal(SIGINT, [](int) { keepWaitingForPcInElf = false; });
+ bool pc_in_desired_elf = true;
+ do {
+ if (!Attach(pid_)) return false;
+ pc_in_desired_elf = ProcIsInDesiredElf(pid_, elf_name);
+ if (!Detach(pid_)) return false;
+
+ if (!pc_in_desired_elf) {
+ for (pid_t tid : tids_) {
+ if (!Attach(tid)) return false;
+ pc_in_desired_elf = ProcIsInDesiredElf(tid, elf_name);
+ if (!Detach(tid)) return false;
+ if (pc_in_desired_elf) break;
+ }
+ }
+
+ // If the process is not in the desired ELF, resume it for a short time, then check again.
+ if (!pc_in_desired_elf) {
+ Resume();
+ usleep(1000); // 1 ms
+ Stop();
+ }
+ } while (!pc_in_desired_elf && keepWaitingForPcInElf);
+
+ if (!pc_in_desired_elf) {
+ fprintf(stderr, "\nExited while waiting for pid %d to enter %s.\n", pid_, elf_name.c_str());
+ return false;
+ }
+ return true;
+}
+
+bool ProcessTracer::UsesSharedLibrary(pid_t pid, const std::string& desired_elf_name) {
+ std::unique_ptr<Maps> maps = std::make_unique<RemoteMaps>(pid);
+ if (!maps->Parse()) {
+ fprintf(stderr, "Could not parse maps for pid %d.\n", pid);
+ return false;
+ }
+ for (const auto& map : *maps) {
+ if (android::base::Basename(map->name()).c_str() == desired_elf_name) return true;
+ }
+ return false;
+}
+
+bool ProcessTracer::ProcIsInDesiredElf(pid_t pid, const std::string& desired_elf_name) {
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
+ if (regs == nullptr) {
+ fprintf(stderr, "Unable to get remote reg data.\n");
+ return false;
+ }
+ UnwinderFromPid unwinder(1024, pid);
+ unwinder.SetRegs(regs.get());
+ if (!unwinder.Init()) {
+ fprintf(stderr, "Unable to intitialize unwinder.\n");
+ return false;
+ }
+ Maps* maps = unwinder.GetMaps();
+ auto map_info = maps->Find(regs->pc());
+ if (map_info == nullptr) {
+ regs->fallback_pc();
+ map_info = maps->Find(regs->pc());
+ if (map_info == nullptr) {
+ return false;
+ }
+ }
+
+ const std::string& current_elf_name = android::base::Basename(map_info->name()).c_str();
+ bool in_desired_elf = current_elf_name == desired_elf_name;
+ if (in_desired_elf) printf("pid %d is in %s! Unwinding...\n\n", pid, desired_elf_name.c_str());
+ return in_desired_elf;
+}
+} // namespace unwindstack
diff --git a/libunwindstack/utils/ProcessTracer.h b/libunwindstack/utils/ProcessTracer.h
new file mode 100644
index 0000000..1809cb2
--- /dev/null
+++ b/libunwindstack/utils/ProcessTracer.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <set>
+#include <string>
+#include <unordered_set>
+
+namespace unwindstack {
+
+// ProcessTracer objects abstract operations for tracing a process and its threads with ptrace(2).
+class ProcessTracer final {
+ public:
+ ProcessTracer(pid_t pid, bool is_tracing_threads);
+
+ ~ProcessTracer();
+
+ // ProcessTracer instances are moveable but not copyable because they manage the
+ // state of a process.
+ ProcessTracer(const ProcessTracer&) = delete;
+ ProcessTracer& operator=(const ProcessTracer&) = delete;
+ ProcessTracer(ProcessTracer&&) = default;
+ ProcessTracer& operator=(ProcessTracer&&) = default;
+
+ pid_t pid() const { return pid_; }
+
+ const std::set<pid_t>& tids() const { return tids_; }
+
+ bool IsTracingThreads() const { return is_tracing_threads_; }
+
+ bool Stop();
+
+ bool Resume();
+
+ // Like ptrace, it is required to call ProcessTracer::Detach before calling ProcessTracer::Attach
+ // on a different thread of the same process.
+ bool Detach(pid_t tid);
+
+ bool Attach(pid_t tid);
+
+ // This method for determining whether a thread is currently executing instructions from a
+ // desired ELF is not the most time efficient solution. In the interest of simplicity and
+ // limiting memory usage, the UnwinderFromPid, Regs, and Maps instances constructed for
+ // in each check (loop iteration) are thrown away.
+ //
+ // A SIGINT signal handler is set up to allow the user to gracefully exit with CTRL-C if they
+ // decide that they no longer want to wait for the process to enter the desired ELF.
+ bool StopInDesiredElf(const std::string& elf_name);
+
+ // `desired_elf_name` should match the filename of the path (the component following the final
+ // '/') corresponding to the shared library as indicated in /proc/pid/maps.
+ static bool UsesSharedLibrary(pid_t pid, const std::string& desired_elf_name);
+
+ private:
+ static bool ProcIsInDesiredElf(pid_t tid, const std::string& desired_elf_name);
+
+ // Initialize tids_ such that the main thread is the first element and
+ // the remaining tids are in order from least to greatest.
+ bool InitProcessTids();
+
+ static constexpr pid_t kNoThreadAttached = -2;
+ static constexpr pid_t kKillFailed = -1;
+ static constexpr pid_t kPtraceFailed = -1;
+ static constexpr pid_t kWaitpidFailed = -1;
+ static inline std::atomic_bool keepWaitingForPcInElf = true;
+ const pid_t pid_;
+ bool is_tracing_threads_ = false;
+ std::set<pid_t> tids_;
+ bool is_running_ = true;
+ pid_t cur_attached_tid_ = kNoThreadAttached;
+};
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/utils/RegsFake.h
similarity index 96%
rename from libunwindstack/tests/RegsFake.h
rename to libunwindstack/utils/RegsFake.h
index f67d7dc..4bb4181 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/utils/RegsFake.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
-#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
+#pragma once
#include <stdint.h>
@@ -113,5 +112,3 @@
};
} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
diff --git a/libunwindstack/utils/tests/ProcessTracerTest.cpp b/libunwindstack/utils/tests/ProcessTracerTest.cpp
new file mode 100644
index 0000000..eae55bb
--- /dev/null
+++ b/libunwindstack/utils/tests/ProcessTracerTest.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 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 <gtest/gtest.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <atomic>
+#include <cerrno>
+#include <csignal>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <thread>
+
+#include <android-base/file.h>
+#include <procinfo/process.h>
+
+#include "OfflineUnwindUtils.h"
+#include "ProcessTracer.h"
+#include "tests/TestUtils.h"
+
+namespace unwindstack {
+namespace {
+
+class ProcessTracerTest : public ::testing::TestWithParam<bool> {
+ protected:
+ enum class BoolOrTimeout {
+ kSuccess = 0,
+ kFail,
+ kTimeout,
+ };
+
+ // Setup a child process that has a few threads that simply busy wait.
+ void SetUp() override {
+ // Setup signal handlers for child to let parent know that it is ready and for parent
+ // to kill the child.
+ child_is_ready_ = false;
+ ASSERT_NE(SIG_ERR, signal(kChildIsReadySignal, [](int) { child_is_ready_ = true; }))
+ << "Failed to set up signal handler for kChildIsReadySignal: " << strerror(errno);
+ child_keep_running_ = true;
+ ASSERT_NE(SIG_ERR, signal(kStopChildSignal, [](int) { child_keep_running_ = false; }))
+ << "Failed to set up signal handler for kStopChildSignal: " << strerror(errno);
+
+ pid_t parent_pid = getpid();
+ child_pid_ = fork();
+ if (child_pid_ == static_cast<pid_t>(-1)) FAIL() << "SetUp: fork() failed: " << strerror(errno);
+ if (child_pid_ == 0) {
+ ASSERT_NO_FATAL_FAILURE(ChildProcSpin(parent_pid));
+ }
+
+ // Make sure the child process has set up its threads before running the test.
+ sigset_t signal_mask, old_mask;
+ sigemptyset(&signal_mask);
+ sigaddset(&signal_mask, kChildIsReadySignal);
+ sigprocmask(SIG_BLOCK, &signal_mask, &old_mask);
+ while (!child_is_ready_) sigsuspend(&old_mask);
+ sigprocmask(SIG_UNBLOCK, &signal_mask, NULL);
+ }
+
+ void TearDown() override {
+ // Send signal to join threads and exit.
+ if (-1 == kill(child_pid_, kStopChildSignal)) {
+ std::cerr << "TearDown: kill sending kStopChildSignal failed: " << strerror(errno) << ".\n";
+ kill(child_pid_, SIGKILL);
+ }
+ }
+
+ void ChildProcSpin(pid_t parent_pid) {
+ // Busy wait in a dlopened local library so we can reliably test (across different
+ // architecture) if a process is within a desired ELF.
+ std::unique_ptr<void, decltype(&dlclose)> test_lib_handle(GetTestLibHandle(), &dlclose);
+ ASSERT_TRUE(test_lib_handle);
+ int (*busy_wait_func)() = reinterpret_cast<int (*)()>(dlsym(test_lib_handle.get(), "BusyWait"));
+ ASSERT_NE(nullptr, busy_wait_func);
+
+ std::array<std::thread, kNumThreads> threads;
+ std::array<std::atomic_bool, kNumThreads> threads_are_ready{false, false, false, false, false};
+ for (size_t i = 0; i < kNumThreads; ++i) {
+ threads.at(i) = std::thread([&threads_are_ready, i, &busy_wait_func]() {
+ while (child_keep_running_) {
+ DoNotOptimize(busy_wait_func());
+ threads_are_ready.at(i) = true;
+ }
+ });
+ }
+ // Wait until all threads have entered the loop before informing parent child is
+ // ready to avoid a race.
+ while (!std::all_of(threads_are_ready.begin(), threads_are_ready.end(),
+ [&](const std::atomic_bool& el) { return el == true; })) {
+ usleep(100);
+ }
+ ASSERT_NE(-1, kill(parent_pid, kChildIsReadySignal) == -1)
+ << "TearDown: kill sending kChildIsReady failed: " << strerror(errno) << ".\n";
+ for (size_t i = 0; i < kNumThreads; ++i) {
+ threads.at(i).join();
+ }
+ exit(EXIT_SUCCESS);
+ }
+
+ BoolOrTimeout StopInDesiredElfTimeout(ProcessTracer& proc, const std::string& elf_name,
+ size_t timeout_sec = 2) {
+ static BoolOrTimeout result = BoolOrTimeout::kSuccess;
+ if (SIG_ERR == signal(SIGALRM, [](int) {
+ result = BoolOrTimeout::kTimeout;
+ // StopInDesiredElf contains signal handler for SIGINT mainly so that we could stop the
+ // search easily when running unwind_for_offline and we can use it here too.
+ kill(getpid(), SIGINT);
+ })) {
+ std::cerr << "Failed to set up signal handler for SIGALRM: " << strerror(errno) << ".\n";
+ exit(EXIT_FAILURE);
+ }
+ alarm(timeout_sec);
+ if (proc.StopInDesiredElf(elf_name)) {
+ result = BoolOrTimeout::kSuccess;
+ } else if (result != BoolOrTimeout::kTimeout) {
+ result = BoolOrTimeout::kFail;
+ }
+ alarm(0);
+ return result;
+ }
+
+ static constexpr size_t kNumThreads = 5;
+ static constexpr int kChildIsReadySignal = SIGUSR1;
+ static constexpr int kStopChildSignal = SIGUSR2;
+ static inline std::atomic_bool child_is_ready_ = false;
+ static inline std::atomic_bool child_keep_running_ = true;
+ pid_t child_pid_;
+};
+
+static void VerifyState(pid_t tid, bool running) {
+ while (true) {
+ android::procinfo::ProcessInfo proc_info;
+ ASSERT_TRUE(GetProcessInfo(tid, &proc_info));
+ if (running) {
+ if (proc_info.state == android::procinfo::kProcessStateRunning ||
+ proc_info.state == android::procinfo::kProcessStateSleeping) {
+ break;
+ }
+ } else if (proc_info.state == android::procinfo::kProcessStateStopped) {
+ break;
+ }
+ usleep(1000);
+ }
+}
+
+static void VerifyState(ProcessTracer& proc, bool running) {
+ // Verify that the main thread and all threads are in the expected state.
+ VerifyState(proc.pid(), running);
+ if (::testing::Test::HasFatalFailure()) return;
+ for (const pid_t& tid : proc.tids()) {
+ VerifyState(tid, running);
+ if (::testing::Test::HasFatalFailure()) return;
+ }
+}
+
+TEST_P(ProcessTracerTest, stop_and_resume) {
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+ ASSERT_TRUE(proc.Stop());
+ VerifyState(proc, /*running*/ false);
+ if (::testing::Test::HasFatalFailure()) return;
+
+ ASSERT_TRUE(proc.Resume());
+ VerifyState(proc, /*running*/ true);
+ if (::testing::Test::HasFatalFailure()) return;
+}
+
+TEST_P(ProcessTracerTest, attach_and_detach) {
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+ ASSERT_TRUE(proc.Attach(child_pid_));
+ // Attaching to the same pid should result in failure and errno indicating that we cannot trace
+ // the priocess because it is already being traced after the call to Attach().
+ ASSERT_EQ(-1, ptrace(PTRACE_ATTACH, child_pid_, nullptr, nullptr));
+ ASSERT_EQ(EPERM, errno);
+ ASSERT_TRUE(proc.Detach(child_pid_));
+ for (const pid_t& tid : proc.tids()) {
+ ASSERT_TRUE(proc.Attach(tid));
+ ASSERT_EQ(-1, ptrace(PTRACE_ATTACH, tid, nullptr, nullptr));
+ ASSERT_EQ(EPERM, errno);
+ ASSERT_TRUE(proc.Detach(tid));
+ }
+}
+
+TEST_P(ProcessTracerTest, consecutive_attach_fail) {
+ if (!GetParam()) GTEST_SKIP();
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+ bool is_first_thread = true;
+ for (const pid_t& tid : proc.tids()) {
+ if (is_first_thread) {
+ ASSERT_TRUE(proc.Attach(tid));
+ is_first_thread = false;
+ } else {
+ ASSERT_FALSE(proc.Attach(tid));
+ }
+ }
+}
+
+TEST_P(ProcessTracerTest, trace_invalid_tid) {
+ if (GetParam()) GTEST_SKIP();
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+ ASSERT_FALSE(proc.Attach(getpid()));
+ ASSERT_FALSE(proc.Detach(getpid()));
+}
+
+TEST_P(ProcessTracerTest, detach_with_no_attached) {
+ if (GetParam()) GTEST_SKIP();
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+ ASSERT_FALSE(proc.Detach(child_pid_));
+}
+
+TEST_P(ProcessTracerTest, uses_shared_library) {
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+ std::string elf_name = "libunwindstack_local.so";
+ ASSERT_TRUE(proc.UsesSharedLibrary(child_pid_, elf_name));
+ for (const pid_t& tid : proc.tids()) {
+ ASSERT_TRUE(proc.UsesSharedLibrary(tid, elf_name));
+ }
+}
+
+TEST_P(ProcessTracerTest, does_not_use_shared_library) {
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+
+ std::string elf_name = "libfake.so";
+ ASSERT_FALSE(proc.UsesSharedLibrary(child_pid_, elf_name));
+ for (const pid_t& tid : proc.tids()) {
+ ASSERT_FALSE(proc.UsesSharedLibrary(tid, elf_name));
+ }
+}
+
+TEST_P(ProcessTracerTest, stop_in_elf_we_use) {
+ // Skip the run with is_tracing_threads=false because main thread only uses
+ // the threading library.
+ if (!GetParam()) GTEST_SKIP();
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+ std::string elf_name = "libunwindstack_local.so";
+
+ EXPECT_EQ(BoolOrTimeout::kSuccess, StopInDesiredElfTimeout(proc, elf_name));
+}
+
+TEST_P(ProcessTracerTest, timeout_when_try_to_stop_in_elf_we_do_not_use) {
+ ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
+ std::string elf_name = "libfake.so";
+
+ EXPECT_EQ(BoolOrTimeout::kTimeout, StopInDesiredElfTimeout(proc, elf_name));
+}
+
+INSTANTIATE_TEST_CASE_P(IsTracingThreads, ProcessTracerTest, testing::Values(false, true));
+
+} // namespace
+} // namespace unwindstack