diff options
Diffstat (limited to 'libunwindstack/utils/OfflineUnwindUtils.cpp')
-rw-r--r-- | libunwindstack/utils/OfflineUnwindUtils.cpp | 335 |
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}, |