Improve LLDB reliability by fixing the following "uninitialized variables" static code inspection warnings from
scan.coverity.com:
1094796 1095721 1095728 1095737 1095741
1095756 1095779 1095789 1095805 1214552
1229457 1232475 1274006 1274010 1293427
1364800 1364802 1364804 1364812 1364816
1374902 1374909 1384975 1399312 1420451
1431704 1454230 1454554 1454615 1454579
1454594 1454832 1457759 1458696 1461909
1467658 1487814 1487830 1487845
Differential Revision: https://reviews.llvm.org/D130098
775 lines
30 KiB
C++
775 lines
30 KiB
C++
//===-- MinidumpFileBuilder.cpp -------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MinidumpFileBuilder.h"
|
|
|
|
#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"
|
|
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/ModuleList.h"
|
|
#include "lldb/Core/Section.h"
|
|
#include "lldb/Target/MemoryRegionInfo.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/RegisterContext.h"
|
|
#include "lldb/Target/StopInfo.h"
|
|
#include "lldb/Target/ThreadList.h"
|
|
#include "lldb/Utility/DataExtractor.h"
|
|
#include "lldb/Utility/RegisterValue.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/BinaryFormat/Minidump.h"
|
|
#include "llvm/Support/ConvertUTF.h"
|
|
#include "llvm/Support/Error.h"
|
|
|
|
#include "Plugins/Process/minidump/MinidumpTypes.h"
|
|
|
|
#include <cinttypes>
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace llvm::minidump;
|
|
|
|
void MinidumpFileBuilder::AddDirectory(StreamType type, size_t stream_size) {
|
|
LocationDescriptor loc;
|
|
loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size);
|
|
// Stream will begin at the current end of data section
|
|
loc.RVA = static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
|
|
|
|
Directory dir;
|
|
dir.Type = static_cast<llvm::support::little_t<StreamType>>(type);
|
|
dir.Location = loc;
|
|
|
|
m_directories.push_back(dir);
|
|
}
|
|
|
|
Status MinidumpFileBuilder::AddSystemInfo(const llvm::Triple &target_triple) {
|
|
Status error;
|
|
AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo));
|
|
|
|
llvm::minidump::ProcessorArchitecture arch;
|
|
switch (target_triple.getArch()) {
|
|
case llvm::Triple::ArchType::x86_64:
|
|
arch = ProcessorArchitecture::AMD64;
|
|
break;
|
|
case llvm::Triple::ArchType::x86:
|
|
arch = ProcessorArchitecture::X86;
|
|
break;
|
|
case llvm::Triple::ArchType::arm:
|
|
arch = ProcessorArchitecture::ARM;
|
|
break;
|
|
case llvm::Triple::ArchType::aarch64:
|
|
arch = ProcessorArchitecture::ARM64;
|
|
break;
|
|
case llvm::Triple::ArchType::mips64:
|
|
case llvm::Triple::ArchType::mips64el:
|
|
case llvm::Triple::ArchType::mips:
|
|
case llvm::Triple::ArchType::mipsel:
|
|
arch = ProcessorArchitecture::MIPS;
|
|
break;
|
|
case llvm::Triple::ArchType::ppc64:
|
|
case llvm::Triple::ArchType::ppc:
|
|
case llvm::Triple::ArchType::ppc64le:
|
|
arch = ProcessorArchitecture::PPC;
|
|
break;
|
|
default:
|
|
error.SetErrorStringWithFormat("Architecture %s not supported.",
|
|
target_triple.getArchName().str().c_str());
|
|
return error;
|
|
};
|
|
|
|
llvm::support::little_t<OSPlatform> platform_id;
|
|
switch (target_triple.getOS()) {
|
|
case llvm::Triple::OSType::Linux:
|
|
if (target_triple.getEnvironment() ==
|
|
llvm::Triple::EnvironmentType::Android)
|
|
platform_id = OSPlatform::Android;
|
|
else
|
|
platform_id = OSPlatform::Linux;
|
|
break;
|
|
case llvm::Triple::OSType::Win32:
|
|
platform_id = OSPlatform::Win32NT;
|
|
break;
|
|
case llvm::Triple::OSType::MacOSX:
|
|
platform_id = OSPlatform::MacOSX;
|
|
break;
|
|
case llvm::Triple::OSType::IOS:
|
|
platform_id = OSPlatform::IOS;
|
|
break;
|
|
default:
|
|
error.SetErrorStringWithFormat("OS %s not supported.",
|
|
target_triple.getOSName().str().c_str());
|
|
return error;
|
|
};
|
|
|
|
llvm::minidump::SystemInfo sys_info;
|
|
sys_info.ProcessorArch =
|
|
static_cast<llvm::support::little_t<ProcessorArchitecture>>(arch);
|
|
// Global offset to beginning of a csd_string in a data section
|
|
sys_info.CSDVersionRVA = static_cast<llvm::support::ulittle32_t>(
|
|
GetCurrentDataEndOffset() + sizeof(llvm::minidump::SystemInfo));
|
|
sys_info.PlatformId = platform_id;
|
|
m_data.AppendData(&sys_info, sizeof(llvm::minidump::SystemInfo));
|
|
|
|
std::string csd_string;
|
|
|
|
error = WriteString(csd_string, &m_data);
|
|
if (error.Fail()) {
|
|
error.SetErrorString("Unable to convert the csd string to UTF16.");
|
|
return error;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
Status WriteString(const std::string &to_write,
|
|
lldb_private::DataBufferHeap *buffer) {
|
|
Status error;
|
|
// let the StringRef eat also null termination char
|
|
llvm::StringRef to_write_ref(to_write.c_str(), to_write.size() + 1);
|
|
llvm::SmallVector<llvm::UTF16, 128> to_write_utf16;
|
|
|
|
bool converted = convertUTF8ToUTF16String(to_write_ref, to_write_utf16);
|
|
if (!converted) {
|
|
error.SetErrorStringWithFormat(
|
|
"Unable to convert the string to UTF16. Failed to convert %s",
|
|
to_write.c_str());
|
|
return error;
|
|
}
|
|
|
|
// size of the UTF16 string should be written without the null termination
|
|
// character that is stored in 2 bytes
|
|
llvm::support::ulittle32_t to_write_size(to_write_utf16.size_in_bytes() - 2);
|
|
|
|
buffer->AppendData(&to_write_size, sizeof(llvm::support::ulittle32_t));
|
|
buffer->AppendData(to_write_utf16.data(), to_write_utf16.size_in_bytes());
|
|
|
|
return error;
|
|
}
|
|
|
|
llvm::Expected<uint64_t> getModuleFileSize(Target &target,
|
|
const ModuleSP &mod) {
|
|
SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection();
|
|
uint64_t SizeOfImage = 0;
|
|
|
|
if (!sect_sp) {
|
|
return llvm::createStringError(std::errc::operation_not_supported,
|
|
"Couldn't obtain the section information.");
|
|
}
|
|
lldb::addr_t sect_addr = sect_sp->GetLoadBaseAddress(&target);
|
|
// Use memory size since zero fill sections, like ".bss", will be smaller on
|
|
// disk.
|
|
lldb::addr_t sect_size = sect_sp->GetByteSize();
|
|
// This will usually be zero, but make sure to calculate the BaseOfImage
|
|
// offset.
|
|
const lldb::addr_t base_sect_offset =
|
|
mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target) -
|
|
sect_addr;
|
|
SizeOfImage = sect_size - base_sect_offset;
|
|
lldb::addr_t next_sect_addr = sect_addr + sect_size;
|
|
Address sect_so_addr;
|
|
target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
|
|
lldb::SectionSP next_sect_sp = sect_so_addr.GetSection();
|
|
while (next_sect_sp &&
|
|
next_sect_sp->GetLoadBaseAddress(&target) == next_sect_addr) {
|
|
sect_size = sect_sp->GetByteSize();
|
|
SizeOfImage += sect_size;
|
|
next_sect_addr += sect_size;
|
|
target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
|
|
next_sect_sp = sect_so_addr.GetSection();
|
|
}
|
|
|
|
return SizeOfImage;
|
|
}
|
|
|
|
// ModuleList stream consists of a number of modules, followed by an array
|
|
// of llvm::minidump::Module's structures. Every structure informs about a
|
|
// single module. Additional data of variable length, such as module's names,
|
|
// are stored just after the ModuleList stream. The llvm::minidump::Module
|
|
// structures point to this helper data by global offset.
|
|
Status MinidumpFileBuilder::AddModuleList(Target &target) {
|
|
constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module);
|
|
Status error;
|
|
|
|
const ModuleList &modules = target.GetImages();
|
|
llvm::support::ulittle32_t modules_count =
|
|
static_cast<llvm::support::ulittle32_t>(modules.GetSize());
|
|
|
|
// This helps us with getting the correct global offset in minidump
|
|
// file later, when we will be setting up offsets from the
|
|
// the llvm::minidump::Module's structures into helper data
|
|
size_t size_before = GetCurrentDataEndOffset();
|
|
|
|
// This is the size of the main part of the ModuleList stream.
|
|
// It consists of a module number and corresponding number of
|
|
// structs describing individual modules
|
|
size_t module_stream_size =
|
|
sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size;
|
|
|
|
// Adding directory describing this stream.
|
|
AddDirectory(StreamType::ModuleList, module_stream_size);
|
|
|
|
m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t));
|
|
|
|
// Temporary storage for the helper data (of variable length)
|
|
// as these cannot be dumped to m_data before dumping entire
|
|
// array of module structures.
|
|
DataBufferHeap helper_data;
|
|
|
|
for (size_t i = 0; i < modules_count; ++i) {
|
|
ModuleSP mod = modules.GetModuleAtIndex(i);
|
|
std::string module_name = mod->GetSpecificationDescription();
|
|
auto maybe_mod_size = getModuleFileSize(target, mod);
|
|
if (!maybe_mod_size) {
|
|
error.SetErrorStringWithFormat("Unable to get the size of module %s.",
|
|
module_name.c_str());
|
|
return error;
|
|
}
|
|
|
|
uint64_t mod_size = std::move(*maybe_mod_size);
|
|
|
|
llvm::support::ulittle32_t signature =
|
|
static_cast<llvm::support::ulittle32_t>(
|
|
static_cast<uint32_t>(minidump::CvSignature::ElfBuildId));
|
|
auto uuid = mod->GetUUID().GetBytes();
|
|
|
|
VSFixedFileInfo info;
|
|
info.Signature = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.StructVersion = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.ProductVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.ProductVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileFlagsMask = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileFlags = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileOS = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileType = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileSubtype = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileDateHigh = static_cast<llvm::support::ulittle32_t>(0u);
|
|
info.FileDateLow = static_cast<llvm::support::ulittle32_t>(0u);
|
|
|
|
LocationDescriptor ld;
|
|
ld.DataSize = static_cast<llvm::support::ulittle32_t>(0u);
|
|
ld.RVA = static_cast<llvm::support::ulittle32_t>(0u);
|
|
|
|
// Setting up LocationDescriptor for uuid string. The global offset into
|
|
// minidump file is calculated.
|
|
LocationDescriptor ld_cv;
|
|
ld_cv.DataSize = static_cast<llvm::support::ulittle32_t>(
|
|
sizeof(llvm::support::ulittle32_t) + uuid.size());
|
|
ld_cv.RVA = static_cast<llvm::support::ulittle32_t>(
|
|
size_before + module_stream_size + helper_data.GetByteSize());
|
|
|
|
helper_data.AppendData(&signature, sizeof(llvm::support::ulittle32_t));
|
|
helper_data.AppendData(uuid.begin(), uuid.size());
|
|
|
|
llvm::minidump::Module m;
|
|
m.BaseOfImage = static_cast<llvm::support::ulittle64_t>(
|
|
mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target));
|
|
m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size);
|
|
m.Checksum = static_cast<llvm::support::ulittle32_t>(0);
|
|
m.TimeDateStamp =
|
|
static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
|
|
m.ModuleNameRVA = static_cast<llvm::support::ulittle32_t>(
|
|
size_before + module_stream_size + helper_data.GetByteSize());
|
|
m.VersionInfo = info;
|
|
m.CvRecord = ld_cv;
|
|
m.MiscRecord = ld;
|
|
|
|
error = WriteString(module_name, &helper_data);
|
|
|
|
if (error.Fail())
|
|
return error;
|
|
|
|
m_data.AppendData(&m, sizeof(llvm::minidump::Module));
|
|
}
|
|
|
|
m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
|
|
return error;
|
|
}
|
|
|
|
uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
|
|
const std::string ®_name) {
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
|
|
if (!reg_info)
|
|
return 0;
|
|
lldb_private::RegisterValue reg_value;
|
|
bool success = reg_ctx->ReadRegister(reg_info, reg_value);
|
|
if (!success)
|
|
return 0;
|
|
return reg_value.GetAsUInt16();
|
|
}
|
|
|
|
uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
|
|
const std::string ®_name) {
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
|
|
if (!reg_info)
|
|
return 0;
|
|
lldb_private::RegisterValue reg_value;
|
|
bool success = reg_ctx->ReadRegister(reg_info, reg_value);
|
|
if (!success)
|
|
return 0;
|
|
return reg_value.GetAsUInt32();
|
|
}
|
|
|
|
uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
|
|
const std::string ®_name) {
|
|
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
|
|
if (!reg_info)
|
|
return 0;
|
|
lldb_private::RegisterValue reg_value;
|
|
bool success = reg_ctx->ReadRegister(reg_info, reg_value);
|
|
if (!success)
|
|
return 0;
|
|
return reg_value.GetAsUInt64();
|
|
}
|
|
|
|
llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,
|
|
const std::string ®_name) {
|
|
return static_cast<llvm::support::ulittle16_t>(
|
|
read_register_u16_raw(reg_ctx, reg_name));
|
|
}
|
|
|
|
llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,
|
|
const std::string ®_name) {
|
|
return static_cast<llvm::support::ulittle32_t>(
|
|
read_register_u32_raw(reg_ctx, reg_name));
|
|
}
|
|
|
|
llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,
|
|
const std::string ®_name) {
|
|
return static_cast<llvm::support::ulittle64_t>(
|
|
read_register_u64_raw(reg_ctx, reg_name));
|
|
}
|
|
|
|
lldb_private::minidump::MinidumpContext_x86_64
|
|
GetThreadContext_64(RegisterContext *reg_ctx) {
|
|
lldb_private::minidump::MinidumpContext_x86_64 thread_context;
|
|
thread_context.p1_home = {};
|
|
thread_context.context_flags = static_cast<uint32_t>(
|
|
lldb_private::minidump::MinidumpContext_x86_64_Flags::x86_64_Flag |
|
|
lldb_private::minidump::MinidumpContext_x86_64_Flags::Control |
|
|
lldb_private::minidump::MinidumpContext_x86_64_Flags::Segments |
|
|
lldb_private::minidump::MinidumpContext_x86_64_Flags::Integer);
|
|
thread_context.rax = read_register_u64(reg_ctx, "rax");
|
|
thread_context.rbx = read_register_u64(reg_ctx, "rbx");
|
|
thread_context.rcx = read_register_u64(reg_ctx, "rcx");
|
|
thread_context.rdx = read_register_u64(reg_ctx, "rdx");
|
|
thread_context.rdi = read_register_u64(reg_ctx, "rdi");
|
|
thread_context.rsi = read_register_u64(reg_ctx, "rsi");
|
|
thread_context.rbp = read_register_u64(reg_ctx, "rbp");
|
|
thread_context.rsp = read_register_u64(reg_ctx, "rsp");
|
|
thread_context.r8 = read_register_u64(reg_ctx, "r8");
|
|
thread_context.r9 = read_register_u64(reg_ctx, "r9");
|
|
thread_context.r10 = read_register_u64(reg_ctx, "r10");
|
|
thread_context.r11 = read_register_u64(reg_ctx, "r11");
|
|
thread_context.r12 = read_register_u64(reg_ctx, "r12");
|
|
thread_context.r13 = read_register_u64(reg_ctx, "r13");
|
|
thread_context.r14 = read_register_u64(reg_ctx, "r14");
|
|
thread_context.r15 = read_register_u64(reg_ctx, "r15");
|
|
thread_context.rip = read_register_u64(reg_ctx, "rip");
|
|
thread_context.eflags = read_register_u32(reg_ctx, "rflags");
|
|
thread_context.cs = read_register_u16(reg_ctx, "cs");
|
|
thread_context.fs = read_register_u16(reg_ctx, "fs");
|
|
thread_context.gs = read_register_u16(reg_ctx, "gs");
|
|
thread_context.ss = read_register_u16(reg_ctx, "ss");
|
|
thread_context.ds = read_register_u16(reg_ctx, "ds");
|
|
return thread_context;
|
|
}
|
|
|
|
// Function returns start and size of the memory region that contains
|
|
// memory location pointed to by the current stack pointer.
|
|
llvm::Expected<std::pair<addr_t, addr_t>>
|
|
findStackHelper(const lldb::ProcessSP &process_sp, uint64_t rsp) {
|
|
MemoryRegionInfo range_info;
|
|
Status error = process_sp->GetMemoryRegionInfo(rsp, range_info);
|
|
// Skip failed memory region requests or any regions with no permissions.
|
|
if (error.Fail() || range_info.GetLLDBPermissions() == 0)
|
|
return llvm::createStringError(
|
|
std::errc::not_supported,
|
|
"unable to load stack segment of the process");
|
|
|
|
const addr_t addr = range_info.GetRange().GetRangeBase();
|
|
const addr_t size = range_info.GetRange().GetByteSize();
|
|
|
|
if (size == 0)
|
|
return llvm::createStringError(std::errc::not_supported,
|
|
"stack segment of the process is empty");
|
|
|
|
return std::make_pair(addr, size);
|
|
}
|
|
|
|
Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) {
|
|
constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread);
|
|
lldb_private::ThreadList thread_list = process_sp->GetThreadList();
|
|
|
|
// size of the entire thread stream consists of:
|
|
// number of threads and threads array
|
|
size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) +
|
|
thread_list.GetSize() * minidump_thread_size;
|
|
// save for the ability to set up RVA
|
|
size_t size_before = GetCurrentDataEndOffset();
|
|
|
|
AddDirectory(StreamType::ThreadList, thread_stream_size);
|
|
|
|
llvm::support::ulittle32_t thread_count =
|
|
static_cast<llvm::support::ulittle32_t>(thread_list.GetSize());
|
|
m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t));
|
|
|
|
DataBufferHeap helper_data;
|
|
|
|
const uint32_t num_threads = thread_list.GetSize();
|
|
|
|
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
|
|
ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
|
|
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
|
|
Status error;
|
|
|
|
if (!reg_ctx_sp) {
|
|
error.SetErrorString("Unable to get the register context.");
|
|
return error;
|
|
}
|
|
RegisterContext *reg_ctx = reg_ctx_sp.get();
|
|
auto thread_context = GetThreadContext_64(reg_ctx);
|
|
uint64_t rsp = read_register_u64_raw(reg_ctx, "rsp");
|
|
auto expected_address_range = findStackHelper(process_sp, rsp);
|
|
|
|
if (!expected_address_range) {
|
|
error.SetErrorString("Unable to get the stack address.");
|
|
return error;
|
|
}
|
|
|
|
std::pair<uint64_t, uint64_t> range = std::move(*expected_address_range);
|
|
uint64_t addr = range.first;
|
|
uint64_t size = range.second;
|
|
|
|
auto data_up = std::make_unique<DataBufferHeap>(size, 0);
|
|
const size_t stack_bytes_read =
|
|
process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
|
|
|
|
if (error.Fail())
|
|
return error;
|
|
|
|
LocationDescriptor stack_memory;
|
|
stack_memory.DataSize =
|
|
static_cast<llvm::support::ulittle32_t>(stack_bytes_read);
|
|
stack_memory.RVA = static_cast<llvm::support::ulittle32_t>(
|
|
size_before + thread_stream_size + helper_data.GetByteSize());
|
|
|
|
MemoryDescriptor stack;
|
|
stack.StartOfMemoryRange = static_cast<llvm::support::ulittle64_t>(addr);
|
|
stack.Memory = stack_memory;
|
|
|
|
helper_data.AppendData(data_up->GetBytes(), stack_bytes_read);
|
|
|
|
LocationDescriptor thread_context_memory_locator;
|
|
thread_context_memory_locator.DataSize =
|
|
static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
|
|
thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
|
|
size_before + thread_stream_size + helper_data.GetByteSize());
|
|
|
|
helper_data.AppendData(
|
|
&thread_context,
|
|
sizeof(lldb_private::minidump::MinidumpContext_x86_64));
|
|
|
|
llvm::minidump::Thread t;
|
|
t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
|
|
t.SuspendCount = static_cast<llvm::support::ulittle32_t>(
|
|
(thread_sp->GetState() == StateType::eStateSuspended) ? 1 : 0);
|
|
t.PriorityClass = static_cast<llvm::support::ulittle32_t>(0);
|
|
t.Priority = static_cast<llvm::support::ulittle32_t>(0);
|
|
t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0);
|
|
t.Stack = stack, t.Context = thread_context_memory_locator;
|
|
|
|
m_data.AppendData(&t, sizeof(llvm::minidump::Thread));
|
|
}
|
|
|
|
m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
|
|
return Status();
|
|
}
|
|
|
|
Status MinidumpFileBuilder::AddException(const lldb::ProcessSP &process_sp) {
|
|
Status error;
|
|
lldb_private::ThreadList thread_list = process_sp->GetThreadList();
|
|
|
|
const uint32_t num_threads = thread_list.GetSize();
|
|
uint32_t stop_reason_thread_idx = 0;
|
|
for (stop_reason_thread_idx = 0; stop_reason_thread_idx < num_threads;
|
|
++stop_reason_thread_idx) {
|
|
ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
|
|
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
|
|
|
|
if (stop_info_sp && stop_info_sp->IsValid())
|
|
break;
|
|
}
|
|
|
|
if (stop_reason_thread_idx == num_threads) {
|
|
error.SetErrorString("No stop reason thread found.");
|
|
return error;
|
|
}
|
|
|
|
constexpr size_t minidump_exception_size =
|
|
sizeof(llvm::minidump::ExceptionStream);
|
|
AddDirectory(StreamType::Exception, minidump_exception_size);
|
|
size_t size_before = GetCurrentDataEndOffset();
|
|
|
|
ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx));
|
|
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
|
|
RegisterContext *reg_ctx = reg_ctx_sp.get();
|
|
auto thread_context = GetThreadContext_64(reg_ctx);
|
|
StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
|
|
|
|
DataBufferHeap helper_data;
|
|
|
|
LocationDescriptor thread_context_memory_locator;
|
|
thread_context_memory_locator.DataSize =
|
|
static_cast<llvm::support::ulittle32_t>(sizeof(thread_context));
|
|
thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
|
|
size_before + minidump_exception_size + helper_data.GetByteSize());
|
|
|
|
helper_data.AppendData(
|
|
&thread_context, sizeof(lldb_private::minidump::MinidumpContext_x86_64));
|
|
|
|
Exception exp_record;
|
|
exp_record.ExceptionCode =
|
|
static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
|
|
exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
|
|
exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
|
|
exp_record.ExceptionAddress = read_register_u64(reg_ctx, "rip");
|
|
exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
|
|
exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
|
|
// exp_record.ExceptionInformation;
|
|
|
|
ExceptionStream exp_stream;
|
|
exp_stream.ThreadId =
|
|
static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
|
|
exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
|
|
exp_stream.ExceptionRecord = exp_record;
|
|
exp_stream.ThreadContext = thread_context_memory_locator;
|
|
|
|
m_data.AppendData(&exp_stream, minidump_exception_size);
|
|
m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
|
|
return error;
|
|
}
|
|
|
|
lldb_private::Status
|
|
MinidumpFileBuilder::AddMemoryList(const lldb::ProcessSP &process_sp) {
|
|
Status error;
|
|
|
|
if (error.Fail()) {
|
|
error.SetErrorString("Process doesn't support getting memory region info.");
|
|
return error;
|
|
}
|
|
|
|
// Get interesting addresses
|
|
std::vector<size_t> interesting_addresses;
|
|
auto thread_list = process_sp->GetThreadList();
|
|
for (size_t i = 0; i < thread_list.GetSize(); ++i) {
|
|
ThreadSP thread_sp(thread_list.GetThreadAtIndex(i));
|
|
RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
|
|
RegisterContext *reg_ctx = reg_ctx_sp.get();
|
|
|
|
interesting_addresses.push_back(read_register_u64(reg_ctx, "rsp"));
|
|
interesting_addresses.push_back(read_register_u64(reg_ctx, "rip"));
|
|
}
|
|
|
|
DataBufferHeap helper_data;
|
|
std::vector<MemoryDescriptor> mem_descriptors;
|
|
|
|
std::set<addr_t> visited_region_base_addresses;
|
|
for (size_t interesting_address : interesting_addresses) {
|
|
MemoryRegionInfo range_info;
|
|
error = process_sp->GetMemoryRegionInfo(interesting_address, range_info);
|
|
// Skip failed memory region requests or any regions with no permissions.
|
|
if (error.Fail() || range_info.GetLLDBPermissions() == 0)
|
|
continue;
|
|
const addr_t addr = range_info.GetRange().GetRangeBase();
|
|
// Skip any regions we have already saved out.
|
|
if (visited_region_base_addresses.insert(addr).second == false)
|
|
continue;
|
|
const addr_t size = range_info.GetRange().GetByteSize();
|
|
if (size == 0)
|
|
continue;
|
|
auto data_up = std::make_unique<DataBufferHeap>(size, 0);
|
|
const size_t bytes_read =
|
|
process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
|
|
if (bytes_read == 0)
|
|
continue;
|
|
// We have a good memory region with valid bytes to store.
|
|
LocationDescriptor memory_dump;
|
|
memory_dump.DataSize = static_cast<llvm::support::ulittle32_t>(bytes_read);
|
|
memory_dump.RVA =
|
|
static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
|
|
MemoryDescriptor memory_desc;
|
|
memory_desc.StartOfMemoryRange =
|
|
static_cast<llvm::support::ulittle64_t>(addr);
|
|
memory_desc.Memory = memory_dump;
|
|
mem_descriptors.push_back(memory_desc);
|
|
m_data.AppendData(data_up->GetBytes(), bytes_read);
|
|
}
|
|
|
|
AddDirectory(StreamType::MemoryList,
|
|
sizeof(llvm::support::ulittle32_t) +
|
|
mem_descriptors.size() *
|
|
sizeof(llvm::minidump::MemoryDescriptor));
|
|
llvm::support::ulittle32_t memory_ranges_num(mem_descriptors.size());
|
|
|
|
m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t));
|
|
for (auto memory_descriptor : mem_descriptors) {
|
|
m_data.AppendData(&memory_descriptor,
|
|
sizeof(llvm::minidump::MemoryDescriptor));
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void MinidumpFileBuilder::AddMiscInfo(const lldb::ProcessSP &process_sp) {
|
|
AddDirectory(StreamType::MiscInfo,
|
|
sizeof(lldb_private::minidump::MinidumpMiscInfo));
|
|
|
|
lldb_private::minidump::MinidumpMiscInfo misc_info;
|
|
misc_info.size = static_cast<llvm::support::ulittle32_t>(
|
|
sizeof(lldb_private::minidump::MinidumpMiscInfo));
|
|
// Default set flags1 to 0, in case that we will not be able to
|
|
// get any information
|
|
misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0);
|
|
|
|
lldb_private::ProcessInstanceInfo process_info;
|
|
process_sp->GetProcessInfo(process_info);
|
|
if (process_info.ProcessIDIsValid()) {
|
|
// Set flags1 to reflect that PID is filled in
|
|
misc_info.flags1 =
|
|
static_cast<llvm::support::ulittle32_t>(static_cast<uint32_t>(
|
|
lldb_private::minidump::MinidumpMiscInfoFlags::ProcessID));
|
|
misc_info.process_id =
|
|
static_cast<llvm::support::ulittle32_t>(process_info.GetProcessID());
|
|
}
|
|
|
|
m_data.AppendData(&misc_info,
|
|
sizeof(lldb_private::minidump::MinidumpMiscInfo));
|
|
}
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer>
|
|
getFileStreamHelper(const std::string &path) {
|
|
auto maybe_stream = llvm::MemoryBuffer::getFileAsStream(path);
|
|
if (!maybe_stream)
|
|
return nullptr;
|
|
return std::move(maybe_stream.get());
|
|
}
|
|
|
|
void MinidumpFileBuilder::AddLinuxFileStreams(
|
|
const lldb::ProcessSP &process_sp) {
|
|
std::vector<std::pair<StreamType, std::string>> files_with_stream_types = {
|
|
{StreamType::LinuxCPUInfo, "/proc/cpuinfo"},
|
|
{StreamType::LinuxLSBRelease, "/etc/lsb-release"},
|
|
};
|
|
|
|
lldb_private::ProcessInstanceInfo process_info;
|
|
process_sp->GetProcessInfo(process_info);
|
|
if (process_info.ProcessIDIsValid()) {
|
|
lldb::pid_t pid = process_info.GetProcessID();
|
|
std::string pid_str = std::to_string(pid);
|
|
files_with_stream_types.push_back(
|
|
{StreamType::LinuxProcStatus, "/proc/" + pid_str + "/status"});
|
|
files_with_stream_types.push_back(
|
|
{StreamType::LinuxCMDLine, "/proc/" + pid_str + "/cmdline"});
|
|
files_with_stream_types.push_back(
|
|
{StreamType::LinuxEnviron, "/proc/" + pid_str + "/environ"});
|
|
files_with_stream_types.push_back(
|
|
{StreamType::LinuxAuxv, "/proc/" + pid_str + "/auxv"});
|
|
files_with_stream_types.push_back(
|
|
{StreamType::LinuxMaps, "/proc/" + pid_str + "/maps"});
|
|
files_with_stream_types.push_back(
|
|
{StreamType::LinuxProcStat, "/proc/" + pid_str + "/stat"});
|
|
files_with_stream_types.push_back(
|
|
{StreamType::LinuxProcFD, "/proc/" + pid_str + "/fd"});
|
|
}
|
|
|
|
for (const auto &entry : files_with_stream_types) {
|
|
StreamType stream = entry.first;
|
|
std::string path = entry.second;
|
|
auto memory_buffer = getFileStreamHelper(path);
|
|
|
|
if (memory_buffer) {
|
|
size_t size = memory_buffer->getBufferSize();
|
|
if (size == 0)
|
|
continue;
|
|
AddDirectory(stream, size);
|
|
m_data.AppendData(memory_buffer->getBufferStart(), size);
|
|
}
|
|
}
|
|
}
|
|
|
|
Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const {
|
|
constexpr size_t header_size = sizeof(llvm::minidump::Header);
|
|
constexpr size_t directory_size = sizeof(llvm::minidump::Directory);
|
|
|
|
// write header
|
|
llvm::minidump::Header header;
|
|
header.Signature = static_cast<llvm::support::ulittle32_t>(
|
|
llvm::minidump::Header::MagicSignature);
|
|
header.Version = static_cast<llvm::support::ulittle32_t>(
|
|
llvm::minidump::Header::MagicVersion);
|
|
header.NumberOfStreams =
|
|
static_cast<llvm::support::ulittle32_t>(GetDirectoriesNum());
|
|
header.StreamDirectoryRVA =
|
|
static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
|
|
header.Checksum = static_cast<llvm::support::ulittle32_t>(
|
|
0u), // not used in most of the writers
|
|
header.TimeDateStamp =
|
|
static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
|
|
header.Flags =
|
|
static_cast<llvm::support::ulittle64_t>(0u); // minidump normal flag
|
|
|
|
Status error;
|
|
size_t bytes_written;
|
|
|
|
bytes_written = header_size;
|
|
error = core_file->Write(&header, bytes_written);
|
|
if (error.Fail() || bytes_written != header_size) {
|
|
if (bytes_written != header_size)
|
|
error.SetErrorStringWithFormat(
|
|
"unable to write the header (written %zd/%zd)", bytes_written,
|
|
header_size);
|
|
return error;
|
|
}
|
|
|
|
// write data
|
|
bytes_written = m_data.GetByteSize();
|
|
error = core_file->Write(m_data.GetBytes(), bytes_written);
|
|
if (error.Fail() || bytes_written != m_data.GetByteSize()) {
|
|
if (bytes_written != m_data.GetByteSize())
|
|
error.SetErrorStringWithFormat(
|
|
"unable to write the data (written %zd/%" PRIu64 ")", bytes_written,
|
|
m_data.GetByteSize());
|
|
return error;
|
|
}
|
|
|
|
// write directories
|
|
for (const Directory &dir : m_directories) {
|
|
bytes_written = directory_size;
|
|
error = core_file->Write(&dir, bytes_written);
|
|
if (error.Fail() || bytes_written != directory_size) {
|
|
if (bytes_written != directory_size)
|
|
error.SetErrorStringWithFormat(
|
|
"unable to write the directory (written %zd/%zd)", bytes_written,
|
|
directory_size);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
size_t MinidumpFileBuilder::GetDirectoriesNum() const {
|
|
return m_directories.size();
|
|
}
|
|
|
|
size_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {
|
|
return sizeof(llvm::minidump::Header) + m_data.GetByteSize();
|
|
}
|