summaryrefslogtreecommitdiff
path: root/libunwindstack
diff options
context:
space:
mode:
authorZach Ghera <zghera@google.com>2021-07-19 21:33:01 +0000
committerDavid Srbecky <dsrbecky@google.com>2021-08-10 14:45:13 +0100
commit4bd169875969a796270a900c73d4b4ec4c9b3661 (patch)
tree04d00f20befb963030aea0b52aee9e1ef078c03f /libunwindstack
parent6a6d38a89820a05e00cbf89a93bb00427c7880a0 (diff)
downloadunwinding-4bd169875969a796270a900c73d4b4ec4c9b3661.tar.gz
OfflineUnwindUtils: Support consecutive unwinds.
This CL refactors OfflineUnwindUtils to allow clients to perform multiple/consecutive unwinds. In the previous version of OfflineUnwindUtils, a client would need to create multiple OfflineUnwindUtils instances in order to test or benchmark offline unwinds. This would require modifying the existing test/benchmark fixtures and/or each of the individual test/benchmarks to support this change. This change simply adds a new Init overload for consecutive offline unwinds and leaves the remainder of the public API the same. Consecutive unwinds are necessary for profiler-like offline unwind benchmarks. See b/192012600 for more for more information regarding these benchmarks. Bug: 192012600 Test: Refactoring CL. Benchmarks still run and unit tests still pass. Change-Id: I5ebb8606eaf4bf144690de41283619218108e3b8
Diffstat (limited to 'libunwindstack')
-rw-r--r--libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp46
-rw-r--r--libunwindstack/tests/UnwindOfflineTest.cpp98
-rw-r--r--libunwindstack/utils/OfflineUnwindUtils.cpp335
-rw-r--r--libunwindstack/utils/OfflineUnwindUtils.h103
4 files changed, 390 insertions, 192 deletions
diff --git a/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp b/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp
index b06c5ee..52525d8 100644
--- a/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp
+++ b/libunwindstack/benchmarks/OfflineUnwindBenchmarks.cpp
@@ -42,47 +42,45 @@ class OfflineUnwindBenchmark : public benchmark::Fixture {
}
void RunBenchmark(benchmark::State& state, const std::string& offline_files_dir,
- size_t expected_num_frames, ArchEnum arch, bool cache_maps, bool is_jit_debug) {
+ size_t expected_num_frames, ArchEnum arch, ProcessMemoryFlag memory_flag,
+ bool cache_maps = false) {
std::string error_msg;
- if (!offline_utils_.Init(offline_files_dir, arch, error_msg, /*add_stack=*/true, cache_maps)) {
- state.SkipWithError(error_msg.c_str());
- return;
- }
- if (is_jit_debug && !offline_utils_.SetJitProcessMemory(error_msg)) {
+ if (!offline_utils_.Init(offline_files_dir, arch, &error_msg, memory_flag, cache_maps)) {
state.SkipWithError(error_msg.c_str());
return;
}
- std::unique_ptr<JitDebug> jit_debug;
- std::stringstream err_stream;
- Unwinder unwinder(0, nullptr, nullptr);
for (auto _ : state) {
state.PauseTiming();
// Need to init unwinder with new copy of regs each iteration because unwinding changes
// the attributes of the regs object.
std::unique_ptr<Regs> regs_copy(offline_utils_.GetRegs()->Clone());
- // If we don't want to use cached maps, make sure to reset them.
- if (!cache_maps && !offline_utils_.ResetMaps(error_msg)) {
- state.SkipWithError(error_msg.c_str());
- return;
+ // 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 (!cache_maps) {
+ if (!offline_utils_.CreateMaps(&error_msg)) {
+ state.SkipWithError(error_msg.c_str());
+ return;
+ }
}
mem_tracker_.StartTrackingAllocations();
state.ResumeTiming();
- std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
- unwinder = Unwinder(128, offline_utils_.GetMaps(), regs_copy.get(), process_memory);
- if (is_jit_debug) {
- jit_debug = CreateJitDebug(regs_copy->Arch(), process_memory);
- unwinder.SetJitDebug(jit_debug.get());
+ Unwinder unwinder = Unwinder(128, offline_utils_.GetMaps(), regs_copy.get(),
+ offline_utils_.GetProcessMemory());
+ if (memory_flag == ProcessMemoryFlag::kIncludeJitMemory) {
+ unwinder.SetJitDebug(offline_utils_.GetJitDebug());
}
unwinder.Unwind();
state.PauseTiming();
mem_tracker_.StopTrackingAllocations();
if (unwinder.NumFrames() != expected_num_frames) {
+ std::stringstream err_stream;
err_stream << "Failed to unwind properly.Expected " << expected_num_frames
<< " frames, but unwinder contained " << unwinder.NumFrames() << " frames.\n";
- break;
+ state.SkipWithError(err_stream.str().c_str());
+ return;
}
state.ResumeTiming();
}
@@ -95,18 +93,18 @@ class OfflineUnwindBenchmark : public benchmark::Fixture {
BENCHMARK_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64)(benchmark::State& state) {
RunBenchmark(state, "straddle_arm64/", /*expected_num_frames=*/6, ARCH_ARM64,
- /*cached_maps=*/false, /*is_jit_debug=*/false);
+ ProcessMemoryFlag::kNone);
}
BENCHMARK_F(OfflineUnwindBenchmark, BM_offline_straddle_arm64_cached_maps)
(benchmark::State& state) {
RunBenchmark(state, "straddle_arm64/", /*expected_num_frames=*/6, ARCH_ARM64,
- /*cached_maps=*/true, /*is_jit_debug=*/false);
+ ProcessMemoryFlag::kNone, /*cached_maps=*/true);
}
-BENCHMARK_F(OfflineUnwindBenchmark, BM_offline_jit_debug_x86)(benchmark::State& state) {
- RunBenchmark(state, "jit_debug_x86/", /*expected_num_frames=*/69, ARCH_X86,
- /*cached_maps=*/false, /*is_jit_debug=*/true);
+BENCHMARK_F(OfflineUnwindBenchmark, BM_offline_jit_debug_arm)(benchmark::State& state) {
+ RunBenchmark(state, "jit_debug_arm/", /*expected_num_frames=*/76, ARCH_ARM,
+ ProcessMemoryFlag::kIncludeJitMemory);
}
} // namespace
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 2261449..9d09aa3 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -44,7 +44,7 @@ class UnwindOfflineTest : public ::testing::Test {
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
std::string error_msg;
- if (!offline_utils_.Init("straddle_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("straddle_arm/", ARCH_ARM, &error_msg)) FAIL() << error_msg;
Regs* regs = offline_utils_.GetRegs();
std::unique_ptr<Regs> regs_copy(regs->Clone());
@@ -87,7 +87,7 @@ TEST_F(UnwindOfflineTest, pc_straddle_arm) {
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
std::string error_msg;
- if (!offline_utils_.Init("gnu_debugdata_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("gnu_debugdata_arm/", ARCH_ARM, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -110,7 +110,7 @@ TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("straddle_arm64/", ARCH_ARM64, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("straddle_arm64/", ARCH_ARM64, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -144,15 +144,13 @@ TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
TEST_F(UnwindOfflineTest, jit_debug_x86) {
std::string error_msg;
- if (!offline_utils_.Init("jit_debug_x86/", ARCH_X86, error_msg)) FAIL() << error_msg;
-
- if (!offline_utils_.SetJitProcessMemory(error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("jit_debug_x86/", ARCH_X86, &error_msg,
+ ProcessMemoryFlag::kIncludeJitMemory))
+ FAIL() << error_msg;
- Regs* regs = offline_utils_.GetRegs();
- std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
- std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs->Arch(), process_memory);
- Unwinder unwinder(128, offline_utils_.GetMaps(), regs, 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();
std::string frame_info(DumpFrames(unwinder));
@@ -441,15 +439,13 @@ TEST_F(UnwindOfflineTest, jit_debug_x86) {
TEST_F(UnwindOfflineTest, jit_debug_arm) {
std::string error_msg;
- if (!offline_utils_.Init("jit_debug_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
-
- if (!offline_utils_.SetJitProcessMemory(error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("jit_debug_arm/", ARCH_ARM, &error_msg,
+ ProcessMemoryFlag::kIncludeJitMemory))
+ FAIL() << error_msg;
- Regs* regs = offline_utils_.GetRegs();
- std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
- std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs->Arch(), process_memory);
- Unwinder unwinder(128, offline_utils_.GetMaps(), regs, 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();
std::string frame_info(DumpFrames(unwinder));
@@ -780,9 +776,9 @@ static void OfflineUnwind(void* data) {
TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
std::string error_msg;
- if (!offline_utils_.Init("jit_debug_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
-
- if (!offline_utils_.SetJitProcessMemory(error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("jit_debug_arm/", ARCH_ARM, &error_msg,
+ ProcessMemoryFlag::kIncludeJitMemory))
+ FAIL() << error_msg;
std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
LeakType data(offline_utils_.GetMaps(), offline_utils_.GetRegs(), process_memory);
@@ -794,7 +790,7 @@ TEST_F(UnwindOfflineTest, unwind_offline_check_for_leaks) {
// No .gnu_debugdata section in the elf file, so no symbols.
TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("bad_eh_frame_hdr_arm64/", ARCH_ARM64, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -825,7 +821,7 @@ TEST_F(UnwindOfflineTest, bad_eh_frame_hdr_arm64) {
// is used first, the unwind will not match the expected output.
TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
std::string error_msg;
- if (!offline_utils_.Init("debug_frame_first_x86/", ARCH_X86, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("debug_frame_first_x86/", ARCH_X86, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -855,7 +851,7 @@ TEST_F(UnwindOfflineTest, debug_frame_first_x86) {
// Make sure that a pc that is at the beginning of an fde unwinds correctly.
TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
std::string error_msg;
- if (!offline_utils_.Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64, error_msg))
+ if (!offline_utils_.Init("eh_frame_hdr_begin_x86_64/", ARCH_X86_64, &error_msg))
FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
@@ -885,15 +881,13 @@ TEST_F(UnwindOfflineTest, eh_frame_hdr_begin_x86_64) {
TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
std::string error_msg;
- if (!offline_utils_.Init("art_quick_osr_stub_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
-
- if (!offline_utils_.SetJitProcessMemory(error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("art_quick_osr_stub_arm/", ARCH_ARM, &error_msg,
+ ProcessMemoryFlag::kIncludeJitMemory))
+ FAIL() << error_msg;
- Regs* regs = offline_utils_.GetRegs();
- std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
- std::unique_ptr<JitDebug> jit_debug = CreateJitDebug(regs->Arch(), process_memory);
- Unwinder unwinder(128, offline_utils_.GetMaps(), regs, 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();
std::string frame_info(DumpFrames(unwinder));
@@ -999,7 +993,7 @@ TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
TEST_F(UnwindOfflineTest, jit_map_arm) {
std::string error_msg;
- if (!offline_utils_.Init("jit_map_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("jit_map_arm/", ARCH_ARM, &error_msg)) FAIL() << error_msg;
Maps* maps = offline_utils_.GetMaps();
maps->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
@@ -1041,7 +1035,7 @@ TEST_F(UnwindOfflineTest, jit_map_arm) {
TEST_F(UnwindOfflineTest, offset_arm) {
std::string error_msg;
- if (!offline_utils_.Init("offset_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("offset_arm/", ARCH_ARM, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1119,7 +1113,7 @@ TEST_F(UnwindOfflineTest, offset_arm) {
// encoded as 0xb, which is not set as pc relative.
TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
std::string error_msg;
- if (!offline_utils_.Init("debug_frame_load_bias_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("debug_frame_load_bias_arm/", ARCH_ARM, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1158,7 +1152,7 @@ TEST_F(UnwindOfflineTest, debug_frame_load_bias_arm) {
TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("shared_lib_in_apk_arm64/", ARCH_ARM64, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("shared_lib_in_apk_arm64/", ARCH_ARM64, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1197,14 +1191,16 @@ TEST_F(UnwindOfflineTest, shared_lib_in_apk_arm64) {
TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64, error_msg))
+ if (!offline_utils_.Init("shared_lib_in_apk_memory_only_arm64/", ARCH_ARM64, &error_msg))
FAIL() << error_msg;
// Add the memory that represents the shared library.
std::shared_ptr<Memory> process_memory = offline_utils_.GetProcessMemory();
MemoryOfflineParts* memory = reinterpret_cast<MemoryOfflineParts*>(process_memory.get());
- if (!AddMemory(offline_utils_.GetOfflineDirectory() + "lib_mem.data", memory, error_msg))
- FAIL() << error_msg;
+ 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();
@@ -1241,7 +1237,7 @@ TEST_F(UnwindOfflineTest, shared_lib_in_apk_memory_only_arm64) {
TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64, error_msg))
+ if (!offline_utils_.Init("shared_lib_in_apk_single_map_arm64/", ARCH_ARM64, &error_msg))
FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
@@ -1296,8 +1292,8 @@ TEST_F(UnwindOfflineTest, shared_lib_in_apk_single_map_arm64) {
TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
std::string error_msg;
- if (!offline_utils_.Init("invalid_elf_offset_arm/", ARCH_ARM, error_msg,
- /*add_stack=*/false))
+ if (!offline_utils_.Init("invalid_elf_offset_arm/", ARCH_ARM, &error_msg,
+ ProcessMemoryFlag::kNoMemory))
FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
@@ -1313,7 +1309,7 @@ TEST_F(UnwindOfflineTest, invalid_elf_offset_arm) {
TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
std::string error_msg;
- if (!offline_utils_.Init("load_bias_ro_rx_x86_64/", ARCH_X86_64, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("load_bias_ro_rx_x86_64/", ARCH_X86_64, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1392,7 +1388,7 @@ TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
TEST_F(UnwindOfflineTest, load_bias_different_section_bias_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("load_bias_different_section_bias_arm64/", ARCH_ARM64, error_msg))
+ if (!offline_utils_.Init("load_bias_different_section_bias_arm64/", ARCH_ARM64, &error_msg))
FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
@@ -1444,7 +1440,7 @@ TEST_F(UnwindOfflineTest, load_bias_different_section_bias_arm64) {
TEST_F(UnwindOfflineTest, eh_frame_bias_x86) {
std::string error_msg;
- if (!offline_utils_.Init("eh_frame_bias_x86/", ARCH_X86, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("eh_frame_bias_x86/", ARCH_X86, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1492,7 +1488,7 @@ TEST_F(UnwindOfflineTest, eh_frame_bias_x86) {
TEST_F(UnwindOfflineTest, signal_load_bias_arm) {
std::string error_msg;
- if (!offline_utils_.Init("signal_load_bias_arm/", ARCH_ARM, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("signal_load_bias_arm/", ARCH_ARM, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1562,7 +1558,7 @@ TEST_F(UnwindOfflineTest, signal_load_bias_arm) {
TEST_F(UnwindOfflineTest, empty_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("empty_arm64/", ARCH_ARM64, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("empty_arm64/", ARCH_ARM64, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1603,7 +1599,7 @@ TEST_F(UnwindOfflineTest, empty_arm64) {
// fde to do the unwind.
TEST_F(UnwindOfflineTest, signal_fde_x86) {
std::string error_msg;
- if (!offline_utils_.Init("signal_fde_x86/", ARCH_X86, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("signal_fde_x86/", ARCH_X86, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1685,7 +1681,7 @@ TEST_F(UnwindOfflineTest, signal_fde_x86) {
// fde to do the unwind.
TEST_F(UnwindOfflineTest, signal_fde_x86_64) {
std::string error_msg;
- if (!offline_utils_.Init("signal_fde_x86_64/", ARCH_X86_64, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("signal_fde_x86_64/", ARCH_X86_64, &error_msg)) FAIL() << error_msg;
Unwinder unwinder(128, offline_utils_.GetMaps(), offline_utils_.GetRegs(),
offline_utils_.GetProcessMemory());
@@ -1758,7 +1754,7 @@ TEST_F(UnwindOfflineTest, signal_fde_x86_64) {
TEST_F(UnwindOfflineTest, pauth_pc_arm64) {
std::string error_msg;
- if (!offline_utils_.Init("pauth_pc_arm64/", ARCH_ARM64, error_msg)) FAIL() << error_msg;
+ if (!offline_utils_.Init("pauth_pc_arm64/", ARCH_ARM64, &error_msg)) FAIL() << error_msg;
static_cast<RegsArm64*>(offline_utils_.GetRegs())->SetPACMask(0x007fff8000000000ULL);
diff --git a/libunwindstack/utils/OfflineUnwindUtils.cpp b/libunwindstack/utils/OfflineUnwindUtils.cpp
index 8bc4baf..d9d147e 100644
--- a/libunwindstack/utils/OfflineUnwindUtils.cpp
+++ b/libunwindstack/utils/OfflineUnwindUtils.cpp
@@ -19,6 +19,7 @@
#include <inttypes.h>
#include <string.h>
+#include <cstddef>
#include <filesystem>
#include <fstream>
#include <iostream>
@@ -27,16 +28,20 @@
#include <sstream>
#include <string>
#include <tuple>
+#include <utility>
+#include <vector>
#include <android-base/file.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>
@@ -89,12 +94,12 @@ std::string DumpFrames(const Unwinder& unwinder) {
return str;
}
-bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string& error_msg) {
+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();
+ *error_msg = err_stream.str();
return false;
}
parts->Add(memory);
@@ -102,132 +107,203 @@ bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string& er
return true;
}
-bool OfflineUnwindUtils::Init(const std::string& offline_files_dir, ArchEnum arch,
- std::string& error_msg, bool add_stack, bool set_maps) {
- // Change to offline files directory so we can read the ELF files
- cwd_ = std::filesystem::current_path();
- offline_dir_ = GetOfflineFilesDirectory() + offline_files_dir;
- std::filesystem::current_path(std::filesystem::path(offline_dir_));
-
- if (!android::base::ReadFileToString((offline_dir_ + "maps.txt"), &map_buffer_)) {
- std::stringstream err_stream;
- err_stream << "Failed to read from '" << offline_dir_ << "maps.txt' into memory.";
- error_msg = err_stream.str();
- return false;
+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;
}
- if (set_maps) {
- if (!ResetMaps(error_msg)) return false;
+ 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();
+}
- if (!SetRegs(arch, error_msg)) return false;
+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;
+}
- if (add_stack) {
- if (!SetProcessMemory(error_msg)) return false;
+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;
}
- if (process_memory_ == nullptr) {
- process_memory_.reset(new MemoryFake);
+ 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 true;
+ return &samples_.at(sample_name).offline_files_path;
}
-bool OfflineUnwindUtils::ResetMaps(std::string& error_msg) {
- maps_.reset(new BufferMaps(map_buffer_.c_str()));
- if (!maps_->Parse()) {
- error_msg = "Failed to parse offline maps.";
+bool OfflineUnwindUtils::Init(std::vector<std::string> offline_files_dirs,
+ const std::vector<ArchEnum>& archs, std::string* error_msg,
+ const std::vector<ProcessMemoryFlag>& memory_flags,
+ const std::vector<bool>& set_maps) {
+ if (!(offline_files_dirs.size() == archs.size() && archs.size() == memory_flags.size() &&
+ memory_flags.size() == set_maps.size())) {
+ *error_msg = "Sizes of vector inputs are not the same.";
return false;
}
- return true;
-}
-bool OfflineUnwindUtils::SetProcessMemory(std::string& error_msg) {
- std::string stack_name(offline_dir_ + "stack.data");
- struct stat st;
- if (stat(stack_name.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
- auto stack_memory = std::make_unique<MemoryOffline>();
- if (!stack_memory->Init((offline_dir_ + "stack.data").c_str(), 0)) {
- std::stringstream err_stream;
- err_stream << "Failed to initialize stack memory from " << offline_dir_ << "stack.data.";
- error_msg = err_stream.str();
+ // 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 (size_t i = 0; i < offline_files_dirs.size(); ++i) {
+ std::string offline_files_full_path = GetOfflineFilesDirectory() + offline_files_dirs[i];
+ 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;
}
- process_memory_.reset(stack_memory.release());
- } else {
- std::unique_ptr<MemoryOfflineParts> stack_memory(new MemoryOfflineParts);
- for (size_t i = 0;; i++) {
- stack_name = offline_dir_ + "stack" + std::to_string(i) + ".data";
- if (stat(stack_name.c_str(), &st) == -1 || !S_ISREG(st.st_mode)) {
- if (i == 0) {
- error_msg = "No stack data files found.";
- return false;
- }
+ 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 = offline_files_dirs[i];
+ samples_.emplace(sample_name, (UnwindSample){
+ std::move(offline_files_full_path), std::move(map_buffer),
+ nullptr, // regs
+ nullptr, // maps
+ std::make_shared<MemoryFake>(), // process_memory
+ nullptr, // jit_debug
+ });
+ UnwindSample& sample = samples_.at(sample_name);
+
+ if (set_maps[i]) {
+ if (!CreateMaps(error_msg, sample_name)) return false;
+ }
+ if (!CreateRegs(archs[i], error_msg, sample_name)) return false;
+
+ switch (memory_flags[i]) {
+ case ProcessMemoryFlag::kNone: {
+ if (!CreateProcessMemory(error_msg, sample_name)) return false;
break;
}
- if (!AddMemory(stack_name, stack_memory.get(), error_msg)) return false;
+ 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;
+ }
}
- process_memory_.reset(stack_memory.release());
}
+ initted_ = true;
return true;
}
-bool OfflineUnwindUtils::SetJitProcessMemory(std::string& error_msg) {
- MemoryOfflineParts* memory = new MemoryOfflineParts;
+bool OfflineUnwindUtils::Init(std::string offline_files_dir, ArchEnum arch, std::string* error_msg,
+ ProcessMemoryFlag memory_flag, bool set_maps) {
+ if (Init(std::vector<std::string>{std::move(offline_files_dir)}, std::vector<ArchEnum>{arch},
+ error_msg, std::vector<ProcessMemoryFlag>{memory_flag}, std::vector<bool>{set_maps})) {
+ if (!ChangeToSampleDirectory(error_msg)) return false;
+ return true;
+ }
+ return false;
+}
- // Construct process memory from all descriptor, stack, entry, and jit files
- for (const auto& file : std::filesystem::directory_iterator(offline_dir_)) {
- std::string filename = file.path().string();
- if (std::regex_match(filename,
- std::regex("^(.+)\\/(descriptor|stack|entry|jit)(\\d*)\\.data$"))) {
- if (!AddMemory(filename, memory, error_msg)) 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;
- process_memory_.reset(memory);
+ std::filesystem::current_path(std::filesystem::path(samples_.at(sample_name).offline_files_path));
return true;
}
-bool OfflineUnwindUtils::SetRegs(ArchEnum arch, std::string& error_msg) {
- switch (arch) {
- case ARCH_ARM: {
- RegsArm* regs = new RegsArm;
- regs_.reset(regs);
- if (!ReadRegs<uint32_t>(regs, arm_regs_, error_msg)) return false;
- break;
- }
- case ARCH_ARM64: {
- RegsArm64* regs = new RegsArm64;
- regs_.reset(regs);
- if (!ReadRegs<uint64_t>(regs, arm64_regs_, error_msg)) return false;
- break;
- }
- case ARCH_X86: {
- RegsX86* regs = new RegsX86;
- regs_.reset(regs);
- if (!ReadRegs<uint32_t>(regs, x86_regs_, error_msg)) return false;
- break;
- }
- case ARCH_X86_64: {
- RegsX86_64* regs = new RegsX86_64;
- regs_.reset(regs);
- if (!ReadRegs<uint64_t>(regs, x86_64_regs_, error_msg)) return false;
- break;
+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;
}
- default:
- error_msg = "Unknown architechture " + std::to_string(arch);
- 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 OfflineUnwindUtils::ReadRegs(RegsImpl<AddressType>* regs,
- const std::unordered_map<std::string, uint32_t>& name_to_reg,
- std::string& error_msg) {
+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_dir_ + "regs.txt").c_str(), "r");
+ FILE* fp = fopen((offline_files_path + "regs.txt").c_str(), "r");
if (fp == nullptr) {
- err_stream << "Error opening file '" << offline_dir_ << "regs.txt': " << strerror(errno);
- error_msg = err_stream.str();
+ err_stream << "Error opening file '" << offline_files_path << "regs.txt': " << strerror(errno);
+ *error_msg = err_stream.str();
return false;
}
@@ -235,8 +311,9 @@ bool OfflineUnwindUtils::ReadRegs(RegsImpl<AddressType>* regs,
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_dir_ << "regs.txt'.";
- error_msg = err_stream.str();
+ 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);
@@ -247,7 +324,7 @@ bool OfflineUnwindUtils::ReadRegs(RegsImpl<AddressType>* regs,
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();
+ *error_msg = err_stream.str();
return false;
}
(*regs)[entry->second] = value;
@@ -255,6 +332,72 @@ bool OfflineUnwindUtils::ReadRegs(RegsImpl<AddressType>* regs,
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},
diff --git a/libunwindstack/utils/OfflineUnwindUtils.h b/libunwindstack/utils/OfflineUnwindUtils.h
index 928ac5c..75bfd38 100644
--- a/libunwindstack/utils/OfflineUnwindUtils.h
+++ b/libunwindstack/utils/OfflineUnwindUtils.h
@@ -17,10 +17,17 @@
#ifndef _LIBUNWINDSTACK_UTILS_OFFLINE_UNWIND_UTILS_H
#define _LIBUNWINDSTACK_UTILS_OFFLINE_UNWIND_UTILS_H
+#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"
@@ -64,40 +71,97 @@
//
// See b/192012600 for additional information regarding Offline Unwind Benchmarks.
namespace unwindstack {
+
std::string GetOfflineFilesDirectory();
std::string DumpFrames(const Unwinder& unwinder);
-bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string& error_msg);
+bool AddMemory(std::string file_name, MemoryOfflineParts* parts, std::string* error_msg);
+
+// 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 map_buffer;
+ std::unique_ptr<Regs> regs;
+ std::unique_ptr<Maps> maps;
+ std::shared_ptr<Memory> process_memory;
+ std::unique_ptr<JitDebug> jit_debug;
+};
+// Enum that indicates how `UnwindSample::process_memory` of `OfflineUnwindUtils::samples_`
+// should be initialized.
+enum class ProcessMemoryFlag {
+ kNone = 0,
+ kIncludeJitMemory,
+ kNoMemory,
+};
+
+// 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:
- Regs* GetRegs() { return regs_.get(); }
+ // 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;
- Maps* GetMaps() { return maps_.get(); }
+ JitDebug* GetJitDebug(const std::string& sample_name = kSingleSample) const;
- std::shared_ptr<Memory> GetProcessMemory() { return process_memory_; }
+ const std::string* GetOfflineFilesPath(const std::string& sample_name = kSingleSample) const;
- std::string GetOfflineDirectory() { return offline_dir_; }
+ // Notes:
+ // * 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.
+ // * Pass offline_files_dirs by value because we move each string to create the samples_ elements.
+ bool Init(std::vector<std::string> offline_files_dirs, const std::vector<ArchEnum>& archs,
+ std::string* error_msg, const std::vector<ProcessMemoryFlag>& memory_flags,
+ const std::vector<bool>& set_maps);
- bool Init(const std::string& offline_files_dir, ArchEnum arch, std::string& error_msg,
- bool add_stack = true, bool set_maps = true);
+ bool Init(std::string offline_files_dir, ArchEnum arch, std::string* error_msg,
+ ProcessMemoryFlag memory_flag = ProcessMemoryFlag::kNone, bool set_maps = true);
- bool ResetMaps(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;
- bool SetProcessMemory(std::string& error_msg);
+ void ReturnToCurrentWorkingDirectory() {
+ if (!cwd_.empty()) std::filesystem::current_path(cwd_);
+ }
- bool SetJitProcessMemory(std::string& error_msg);
+ bool CreateMaps(std::string* error_msg, const std::string& sample_name = kSingleSample);
- void ReturnToCurrentWorkingDirectory() { std::filesystem::current_path(cwd_); }
+ bool CreateProcessMemory(std::string* error_msg, const std::string& sample_name = kSingleSample);
private:
- bool SetRegs(ArchEnum arch, std::string& error_msg);
+ static constexpr char kSingleSample[] = "";
+
+ 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;
- template <typename AddressType>
- bool ReadRegs(RegsImpl<AddressType>* regs,
- const std::unordered_map<std::string, uint32_t>& name_to_reg,
- std::string& error_msg);
+ 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_;
@@ -105,11 +169,8 @@ class OfflineUnwindUtils {
static std::unordered_map<std::string, uint32_t> x86_64_regs_;
std::string cwd_;
- std::string offline_dir_;
- std::string map_buffer_;
- std::unique_ptr<Regs> regs_;
- std::unique_ptr<Maps> maps_;
- std::shared_ptr<Memory> process_memory_;
+ std::unordered_map<std::string, UnwindSample> samples_;
+ bool initted_ = false;
};
} // namespace unwindstack