summaryrefslogtreecommitdiff
path: root/libunwindstack/utils/OfflineUnwindUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libunwindstack/utils/OfflineUnwindUtils.cpp')
-rw-r--r--libunwindstack/utils/OfflineUnwindUtils.cpp335
1 files changed, 239 insertions, 96 deletions
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},