diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/crashreporter/google-breakpad/src/processor/microdump.cc | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/processor/microdump.cc')
-rw-r--r-- | toolkit/crashreporter/google-breakpad/src/processor/microdump.cc | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/processor/microdump.cc b/toolkit/crashreporter/google-breakpad/src/processor/microdump.cc new file mode 100644 index 0000000000..d8141a2a8f --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/processor/microdump.cc @@ -0,0 +1,405 @@ +// Copyright (c) 2014 Google Inc. +// 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. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +// microdump.cc: A microdump reader. +// +// See microdump.h for documentation. + +#include "google_breakpad/processor/microdump.h" + +#include <stdio.h> +#include <string.h> + +#include <memory> +#include <sstream> +#include <string> +#include <vector> + +#include "google_breakpad/common/minidump_cpu_arm.h" +#include "google_breakpad/processor/code_module.h" +#include "processor/basic_code_module.h" +#include "processor/convert_old_arm64_context.h" +#include "processor/linked_ptr.h" +#include "processor/logging.h" +#include "processor/range_map-inl.h" + +namespace { +static const char kGoogleBreakpadKey[] = "google-breakpad"; +static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----"; +static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----"; +static const char kOsKey[] = ": O "; +static const char kCpuKey[] = ": C "; +static const char kCrashReasonKey[] = ": R "; +static const char kGpuKey[] = ": G "; +static const char kMmapKey[] = ": M "; +static const char kStackKey[] = ": S "; +static const char kStackFirstLineKey[] = ": S 0 "; +static const char kArmArchitecture[] = "arm"; +static const char kArm64Architecture[] = "arm64"; +static const char kX86Architecture[] = "x86"; +static const char kMipsArchitecture[] = "mips"; +static const char kMips64Architecture[] = "mips64"; +static const char kGpuUnknown[] = "UNKNOWN"; + +template<typename T> +T HexStrToL(const string& str) { + uint64_t res = 0; + std::istringstream ss(str); + ss >> std::hex >> res; + return static_cast<T>(res); +} + +std::vector<uint8_t> ParseHexBuf(const string& str) { + std::vector<uint8_t> buf; + for (size_t i = 0; i < str.length(); i += 2) { + buf.push_back(HexStrToL<uint8_t>(str.substr(i, 2))); + } + return buf; +} + +bool GetLine(std::istringstream* istream, string* str) { + if (std::getline(*istream, *str)) { + // Trim any trailing newline from the end of the line. Allows us + // to seamlessly handle both Windows/DOS and Unix formatted input. The + // adb tool generally writes logcat dumps in Windows/DOS format. + if (!str->empty() && str->at(str->size() - 1) == '\r') { + str->erase(str->size() - 1); + } + return true; + } + return false; +} + +} // namespace + +namespace google_breakpad { + +// +// MicrodumpModules +// + +void MicrodumpModules::Add(const CodeModule* module) { + linked_ptr<const CodeModule> module_ptr(module); + if (!map_.StoreRange(module->base_address(), module->size(), module_ptr)) { + BPLOG(ERROR) << "Module " << module->code_file() << + " could not be stored"; + } +} + +void MicrodumpModules::SetEnableModuleShrink(bool is_enabled) { + map_.SetMergeStrategy(is_enabled ? MergeRangeStrategy::kTruncateUpper + : MergeRangeStrategy::kExclusiveRanges); +} + +// +// MicrodumpContext +// + +void MicrodumpContext::SetContextARM(MDRawContextARM* arm) { + DumpContext::SetContextFlags(MD_CONTEXT_ARM); + DumpContext::SetContextARM(arm); + valid_ = true; +} + +void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) { + DumpContext::SetContextFlags(MD_CONTEXT_ARM64); + DumpContext::SetContextARM64(arm64); + valid_ = true; +} + +void MicrodumpContext::SetContextX86(MDRawContextX86* x86) { + DumpContext::SetContextFlags(MD_CONTEXT_X86); + DumpContext::SetContextX86(x86); + valid_ = true; +} + +void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* mips32) { + DumpContext::SetContextFlags(MD_CONTEXT_MIPS); + DumpContext::SetContextMIPS(mips32); + valid_ = true; +} + +void MicrodumpContext::SetContextMIPS64(MDRawContextMIPS* mips64) { + DumpContext::SetContextFlags(MD_CONTEXT_MIPS64); + DumpContext::SetContextMIPS(mips64); + valid_ = true; +} + + +// +// MicrodumpMemoryRegion +// + +MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { } + +void MicrodumpMemoryRegion::Init(uint64_t base_address, + const std::vector<uint8_t>& contents) { + base_address_ = base_address; + contents_ = contents; +} + +uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; } + +uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); } + +bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, + uint8_t* value) const { + return GetMemoryLittleEndian(address, value); +} + +bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, + uint16_t* value) const { + return GetMemoryLittleEndian(address, value); +} + +bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, + uint32_t* value) const { + return GetMemoryLittleEndian(address, value); +} + +bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, + uint64_t* value) const { + return GetMemoryLittleEndian(address, value); +} + +template<typename ValueType> +bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address, + ValueType* value) const { + if (address < base_address_ || + address - base_address_ + sizeof(ValueType) > contents_.size()) + return false; + ValueType v = 0; + uint64_t start = address - base_address_; + // The loop condition is odd, but it's correct for size_t. + for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) + v = (v << 8) | static_cast<uint8_t>(contents_[start + i]); + *value = v; + return true; +} + +void MicrodumpMemoryRegion::Print() const { + // Not reached, just needed to honor the base class contract. + assert(false); +} + +// +// Microdump +// +Microdump::Microdump(const string& contents) + : context_(new MicrodumpContext()), + stack_region_(new MicrodumpMemoryRegion()), + modules_(new MicrodumpModules()), + system_info_(new SystemInfo()), + crash_reason_(), + crash_address_(0u) { + assert(!contents.empty()); + + bool in_microdump = false; + string line; + uint64_t stack_start = 0; + std::vector<uint8_t> stack_content; + string arch; + + std::istringstream stream(contents); + while (GetLine(&stream, &line)) { + if (line.find(kGoogleBreakpadKey) == string::npos) { + continue; + } + if (line.find(kMicrodumpBegin) != string::npos) { + in_microdump = true; + continue; + } + if (!in_microdump) { + continue; + } + if (line.find(kMicrodumpEnd) != string::npos) { + break; + } + + size_t pos; + if ((pos = line.find(kOsKey)) != string::npos) { + string os_str(line, pos + strlen(kOsKey)); + std::istringstream os_tokens(os_str); + string os_id; + string num_cpus; + string os_version; + // This reflect the actual HW arch and might not match the arch emulated + // for the execution (e.g., running a 32-bit binary on a 64-bit cpu). + string hw_arch; + + os_tokens >> os_id; + os_tokens >> arch; + os_tokens >> num_cpus; + os_tokens >> hw_arch; + GetLine(&os_tokens, &os_version); + os_version.erase(0, 1); // remove leading space. + + system_info_->cpu = arch; + system_info_->cpu_count = HexStrToL<uint8_t>(num_cpus); + system_info_->os_version = os_version; + + if (os_id == "L") { + system_info_->os = "Linux"; + system_info_->os_short = "linux"; + } else if (os_id == "A") { + system_info_->os = "Android"; + system_info_->os_short = "android"; + modules_->SetEnableModuleShrink(true); + } + + // OS line also contains release and version for future use. + } else if ((pos = line.find(kStackKey)) != string::npos) { + if (line.find(kStackFirstLineKey) != string::npos) { + // The first line of the stack (S 0 stack header) provides the value of + // the stack pointer, the start address of the stack being dumped and + // the length of the stack. We could use it in future to double check + // that we received all the stack as expected. + continue; + } + string stack_str(line, pos + strlen(kStackKey)); + std::istringstream stack_tokens(stack_str); + string start_addr_str; + string raw_content; + stack_tokens >> start_addr_str; + stack_tokens >> raw_content; + uint64_t start_addr = HexStrToL<uint64_t>(start_addr_str); + + if (stack_start != 0) { + // Verify that the stack chunks in the microdump are contiguous. + assert(start_addr == stack_start + stack_content.size()); + } else { + stack_start = start_addr; + } + std::vector<uint8_t> chunk = ParseHexBuf(raw_content); + stack_content.insert(stack_content.end(), chunk.begin(), chunk.end()); + + } else if ((pos = line.find(kCpuKey)) != string::npos) { + string cpu_state_str(line, pos + strlen(kCpuKey)); + std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str); + if (strcmp(arch.c_str(), kArmArchitecture) == 0) { + if (cpu_state_raw.size() != sizeof(MDRawContextARM)) { + std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() + << " bytes instead of " << sizeof(MDRawContextARM) + << std::endl; + continue; + } + MDRawContextARM* arm = new MDRawContextARM(); + memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); + context_->SetContextARM(arm); + } else if (strcmp(arch.c_str(), kArm64Architecture) == 0) { + if (cpu_state_raw.size() == sizeof(MDRawContextARM64)) { + MDRawContextARM64* arm = new MDRawContextARM64(); + memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); + context_->SetContextARM64(arm); + } else if (cpu_state_raw.size() == sizeof(MDRawContextARM64_Old)) { + MDRawContextARM64_Old old_arm; + memcpy(&old_arm, &cpu_state_raw[0], cpu_state_raw.size()); + MDRawContextARM64* new_arm = new MDRawContextARM64(); + ConvertOldARM64Context(old_arm, new_arm); + context_->SetContextARM64(new_arm); + } else { + std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() + << " bytes instead of " << sizeof(MDRawContextARM64) + << std::endl; + continue; + } + } else if (strcmp(arch.c_str(), kX86Architecture) == 0) { + if (cpu_state_raw.size() != sizeof(MDRawContextX86)) { + std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() + << " bytes instead of " << sizeof(MDRawContextX86) + << std::endl; + continue; + } + MDRawContextX86* x86 = new MDRawContextX86(); + memcpy(x86, &cpu_state_raw[0], cpu_state_raw.size()); + context_->SetContextX86(x86); + } else if (strcmp(arch.c_str(), kMipsArchitecture) == 0) { + if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) { + std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() + << " bytes instead of " << sizeof(MDRawContextMIPS) + << std::endl; + continue; + } + MDRawContextMIPS* mips32 = new MDRawContextMIPS(); + memcpy(mips32, &cpu_state_raw[0], cpu_state_raw.size()); + context_->SetContextMIPS(mips32); + } else if (strcmp(arch.c_str(), kMips64Architecture) == 0) { + if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) { + std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() + << " bytes instead of " << sizeof(MDRawContextMIPS) + << std::endl; + continue; + } + MDRawContextMIPS* mips64 = new MDRawContextMIPS(); + memcpy(mips64, &cpu_state_raw[0], cpu_state_raw.size()); + context_->SetContextMIPS64(mips64); + } else { + std::cerr << "Unsupported architecture: " << arch << std::endl; + } + } else if ((pos = line.find(kCrashReasonKey)) != string::npos) { + string crash_reason_str(line, pos + strlen(kCrashReasonKey)); + std::istringstream crash_reason_tokens(crash_reason_str); + string signal; + string address; + crash_reason_tokens >> signal; + crash_reason_tokens >> crash_reason_; + crash_reason_tokens >> address; + crash_address_ = HexStrToL<uint64_t>(address); + } else if ((pos = line.find(kGpuKey)) != string::npos) { + string gpu_str(line, pos + strlen(kGpuKey)); + if (strcmp(gpu_str.c_str(), kGpuUnknown) != 0) { + std::istringstream gpu_tokens(gpu_str); + std::getline(gpu_tokens, system_info_->gl_version, '|'); + std::getline(gpu_tokens, system_info_->gl_vendor, '|'); + std::getline(gpu_tokens, system_info_->gl_renderer, '|'); + } + } else if ((pos = line.find(kMmapKey)) != string::npos) { + string mmap_line(line, pos + strlen(kMmapKey)); + std::istringstream mmap_tokens(mmap_line); + string addr, offset, size, identifier, filename; + mmap_tokens >> addr; + mmap_tokens >> offset; + mmap_tokens >> size; + mmap_tokens >> identifier; + mmap_tokens >> filename; + + modules_->Add(new BasicCodeModule( + HexStrToL<uint64_t>(addr), // base_address + HexStrToL<uint64_t>(size), // size + filename, // code_file + identifier, // code_identifier + filename, // debug_file + identifier, // debug_identifier + "")); // version + } + } + stack_region_->Init(stack_start, stack_content); +} + +} // namespace google_breakpad |