Revert "[lldb] Add minidump save-core functionality to ELF object files"
This reverts commit aafa05e03d.
Broke builder on aarch64 --
https://lab.llvm.org/buildbot/#/builders/96/builds/10926
This commit is contained in:
@@ -192,8 +192,7 @@ public:
|
||||
|
||||
static Status SaveCore(const lldb::ProcessSP &process_sp,
|
||||
const FileSpec &outfile,
|
||||
lldb::SaveCoreStyle &core_style,
|
||||
const ConstString plugin_name);
|
||||
lldb::SaveCoreStyle &core_style);
|
||||
|
||||
// ObjectContainer
|
||||
static bool
|
||||
|
||||
@@ -1228,8 +1228,7 @@ lldb::SBError SBProcess::SaveCore(const char *file_name) {
|
||||
|
||||
FileSpec core_file(file_name);
|
||||
SaveCoreStyle core_style = SaveCoreStyle::eSaveCoreFull;
|
||||
error.ref() =
|
||||
PluginManager::SaveCore(process_sp, core_file, core_style, ConstString());
|
||||
error.ref() = PluginManager::SaveCore(process_sp, core_file, core_style);
|
||||
return LLDB_RECORD_RESULT(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -1180,13 +1180,12 @@ static constexpr OptionEnumValues SaveCoreStyles() {
|
||||
class CommandObjectProcessSaveCore : public CommandObjectParsed {
|
||||
public:
|
||||
CommandObjectProcessSaveCore(CommandInterpreter &interpreter)
|
||||
: CommandObjectParsed(
|
||||
interpreter, "process save-core",
|
||||
"Save the current process as a core file using an "
|
||||
"appropriate file type.",
|
||||
"process save-core [-s corefile-style -p plugin-name] FILE",
|
||||
eCommandRequiresProcess | eCommandTryTargetAPILock |
|
||||
eCommandProcessMustBeLaunched) {}
|
||||
: CommandObjectParsed(interpreter, "process save-core",
|
||||
"Save the current process as a core file using an "
|
||||
"appropriate file type.",
|
||||
"process save-core [-s corefile-style] FILE",
|
||||
eCommandRequiresProcess | eCommandTryTargetAPILock |
|
||||
eCommandProcessMustBeLaunched) {}
|
||||
|
||||
~CommandObjectProcessSaveCore() override = default;
|
||||
|
||||
@@ -1209,9 +1208,6 @@ public:
|
||||
Status error;
|
||||
|
||||
switch (short_option) {
|
||||
case 'p':
|
||||
m_requested_plugin_name.SetString(option_arg);
|
||||
break;
|
||||
case 's':
|
||||
m_requested_save_core_style =
|
||||
(lldb::SaveCoreStyle)OptionArgParser::ToOptionEnum(
|
||||
@@ -1227,12 +1223,10 @@ public:
|
||||
|
||||
void OptionParsingStarting(ExecutionContext *execution_context) override {
|
||||
m_requested_save_core_style = eSaveCoreUnspecified;
|
||||
m_requested_plugin_name.Clear();
|
||||
}
|
||||
|
||||
// Instance variables to hold the values for command options.
|
||||
SaveCoreStyle m_requested_save_core_style;
|
||||
ConstString m_requested_plugin_name;
|
||||
};
|
||||
|
||||
protected:
|
||||
@@ -1243,8 +1237,7 @@ protected:
|
||||
FileSpec output_file(command.GetArgumentAtIndex(0));
|
||||
SaveCoreStyle corefile_style = m_options.m_requested_save_core_style;
|
||||
Status error =
|
||||
PluginManager::SaveCore(process_sp, output_file, corefile_style,
|
||||
m_options.m_requested_plugin_name);
|
||||
PluginManager::SaveCore(process_sp, output_file, corefile_style);
|
||||
if (error.Success()) {
|
||||
if (corefile_style == SaveCoreStyle::eSaveCoreDirtyOnly ||
|
||||
corefile_style == SaveCoreStyle::eSaveCoreStackOnly) {
|
||||
|
||||
@@ -749,9 +749,6 @@ let Command = "process save_core" in {
|
||||
def process_save_core_style : Option<"style", "s">, Group<1>,
|
||||
EnumArg<"SaveCoreStyle", "SaveCoreStyles()">, Desc<"Request a specific style "
|
||||
"of corefile to be saved.">;
|
||||
def process_save_core_plugin_name : Option<"plugin-name", "p">,
|
||||
OptionalArg<"Plugin">, Desc<"Specify a plugin name to create the core file."
|
||||
"This allows core files to be saved in different formats.">;
|
||||
}
|
||||
|
||||
let Command = "process trace save" in {
|
||||
|
||||
@@ -685,13 +685,10 @@ PluginManager::GetObjectFileCreateMemoryCallbackForPluginName(
|
||||
|
||||
Status PluginManager::SaveCore(const lldb::ProcessSP &process_sp,
|
||||
const FileSpec &outfile,
|
||||
lldb::SaveCoreStyle &core_style,
|
||||
const ConstString plugin_name) {
|
||||
lldb::SaveCoreStyle &core_style) {
|
||||
Status error;
|
||||
auto &instances = GetObjectFileInstances().GetInstances();
|
||||
for (auto &instance : instances) {
|
||||
if (plugin_name && instance.name != plugin_name)
|
||||
continue;
|
||||
if (instance.save_core &&
|
||||
instance.save_core(process_sp, outfile, core_style, error))
|
||||
return error;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
add_subdirectory(Breakpad)
|
||||
add_subdirectory(ELF)
|
||||
add_subdirectory(Mach-O)
|
||||
add_subdirectory(Minidump)
|
||||
add_subdirectory(PDB)
|
||||
add_subdirectory(PECOFF)
|
||||
add_subdirectory(JIT)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
add_lldb_library(lldbPluginObjectFileMinidump PLUGIN
|
||||
ObjectFileMinidump.cpp
|
||||
MinidumpFileBuilder.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbCore
|
||||
lldbHost
|
||||
lldbSymbol
|
||||
lldbTarget
|
||||
lldbUtility
|
||||
lldbPluginProcessUtility
|
||||
LINK_COMPONENTS
|
||||
Support
|
||||
)
|
||||
@@ -1,767 +0,0 @@
|
||||
//===-- 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"
|
||||
|
||||
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::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(0));
|
||||
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.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(), size);
|
||||
|
||||
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>(size);
|
||||
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(0));
|
||||
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 %ld/%ld).", 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 %ld/%ld).", 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 %ld/%ld).", 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();
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
//===-- MinidumpFileBuilder.h ---------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file
|
||||
/// Structure holding data neccessary for minidump file creation.
|
||||
///
|
||||
/// The class MinidumpFileWriter is used to hold the data that will eventually
|
||||
/// be dumped to the file.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
|
||||
#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Utility/DataBufferHeap.h"
|
||||
#include "lldb/Utility/Status.h"
|
||||
|
||||
#include "llvm/Object/Minidump.h"
|
||||
|
||||
// Write std::string to minidump in the UTF16 format(with null termination char)
|
||||
// with the size(without null termination char) preceding the UTF16 string.
|
||||
// Empty strings are also printed with zero length and just null termination
|
||||
// char.
|
||||
lldb_private::Status WriteString(const std::string &to_write,
|
||||
lldb_private::DataBufferHeap *buffer);
|
||||
|
||||
/// \class MinidumpFileBuilder
|
||||
/// Minidump writer for Linux
|
||||
///
|
||||
/// This class provides a Minidump writer that is able to
|
||||
/// snapshot the current process state. For the whole time, it stores all
|
||||
/// the data on heap.
|
||||
class MinidumpFileBuilder {
|
||||
public:
|
||||
MinidumpFileBuilder() = default;
|
||||
|
||||
MinidumpFileBuilder(const MinidumpFileBuilder &) = delete;
|
||||
MinidumpFileBuilder &operator=(const MinidumpFileBuilder &) = delete;
|
||||
|
||||
MinidumpFileBuilder(MinidumpFileBuilder &&other) = default;
|
||||
MinidumpFileBuilder &operator=(MinidumpFileBuilder &&other) = default;
|
||||
|
||||
~MinidumpFileBuilder() = default;
|
||||
|
||||
// Add SystemInfo stream, used for storing the most basic information
|
||||
// about the system, platform etc...
|
||||
lldb_private::Status AddSystemInfo(const llvm::Triple &target_triple);
|
||||
// Add ModuleList stream, containing information about all loaded modules
|
||||
// at the time of saving minidump.
|
||||
lldb_private::Status AddModuleList(lldb_private::Target &target);
|
||||
// Add ThreadList stream, containing information about all threads running
|
||||
// at the moment of core saving. Contains information about thread
|
||||
// contexts.
|
||||
lldb_private::Status AddThreadList(const lldb::ProcessSP &process_sp);
|
||||
// Add Exception stream, this contains information about the exception
|
||||
// that stopped the process. In case no thread made exception it return
|
||||
// failed status.
|
||||
lldb_private::Status AddException(const lldb::ProcessSP &process_sp);
|
||||
// Add MemoryList stream, containing dumps of important memory segments
|
||||
lldb_private::Status AddMemoryList(const lldb::ProcessSP &process_sp);
|
||||
// Add MiscInfo stream, mainly providing ProcessId
|
||||
void AddMiscInfo(const lldb::ProcessSP &process_sp);
|
||||
// Add informative files about a Linux process
|
||||
void AddLinuxFileStreams(const lldb::ProcessSP &process_sp);
|
||||
// Dump the prepared data into file. In case of the failure data are
|
||||
// intact.
|
||||
lldb_private::Status Dump(lldb::FileUP &core_file) const;
|
||||
// Returns the current number of directories(streams) that have been so far
|
||||
// created. This number of directories will be dumped when calling Dump()
|
||||
size_t GetDirectoriesNum() const;
|
||||
|
||||
private:
|
||||
// Add directory of StreamType pointing to the current end of the prepared
|
||||
// file with the specified size.
|
||||
void AddDirectory(llvm::minidump::StreamType type, size_t stream_size);
|
||||
size_t GetCurrentDataEndOffset() const;
|
||||
|
||||
// Stores directories to later put them at the end of minidump file
|
||||
std::vector<llvm::minidump::Directory> m_directories;
|
||||
// Main data buffer consisting of data without the minidump header and
|
||||
// directories
|
||||
lldb_private::DataBufferHeap m_data;
|
||||
};
|
||||
|
||||
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H
|
||||
@@ -1,119 +0,0 @@
|
||||
//===-- ObjectFileMinidump.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 "ObjectFileMinidump.h"
|
||||
|
||||
#include "MinidumpFileBuilder.h"
|
||||
|
||||
#include "lldb/Core/ModuleSpec.h"
|
||||
#include "lldb/Core/PluginManager.h"
|
||||
#include "lldb/Core/Section.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
LLDB_PLUGIN_DEFINE(ObjectFileMinidump)
|
||||
|
||||
void ObjectFileMinidump::Initialize() {
|
||||
PluginManager::RegisterPlugin(
|
||||
GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
|
||||
CreateMemoryInstance, GetModuleSpecifications, SaveCore);
|
||||
}
|
||||
|
||||
void ObjectFileMinidump::Terminate() {
|
||||
PluginManager::UnregisterPlugin(CreateInstance);
|
||||
}
|
||||
|
||||
ConstString ObjectFileMinidump::GetPluginNameStatic() {
|
||||
static ConstString g_name("minidump");
|
||||
return g_name;
|
||||
}
|
||||
|
||||
ObjectFile *ObjectFileMinidump::CreateInstance(
|
||||
const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
|
||||
lldb::offset_t data_offset, const lldb_private::FileSpec *file,
|
||||
lldb::offset_t offset, lldb::offset_t length) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectFile *ObjectFileMinidump::CreateMemoryInstance(
|
||||
const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
|
||||
const ProcessSP &process_sp, lldb::addr_t header_addr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t ObjectFileMinidump::GetModuleSpecifications(
|
||||
const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
|
||||
lldb::offset_t data_offset, lldb::offset_t file_offset,
|
||||
lldb::offset_t length, lldb_private::ModuleSpecList &specs) {
|
||||
specs.Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ObjectFileMinidump::SaveCore(const lldb::ProcessSP &process_sp,
|
||||
const lldb_private::FileSpec &outfile,
|
||||
lldb::SaveCoreStyle &core_style,
|
||||
lldb_private::Status &error) {
|
||||
if (core_style != SaveCoreStyle::eSaveCoreStackOnly) {
|
||||
error.SetErrorString("Only stack minidumps supported yet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!process_sp)
|
||||
return false;
|
||||
|
||||
MinidumpFileBuilder builder;
|
||||
|
||||
Target &target = process_sp->GetTarget();
|
||||
|
||||
error = builder.AddSystemInfo(target.GetArchitecture().GetTriple());
|
||||
if (error.Fail())
|
||||
return false;
|
||||
|
||||
error = builder.AddModuleList(target);
|
||||
if (error.Fail())
|
||||
return false;
|
||||
|
||||
builder.AddMiscInfo(process_sp);
|
||||
|
||||
if (target.GetArchitecture().GetMachine() == llvm::Triple::ArchType::x86_64) {
|
||||
error = builder.AddThreadList(process_sp);
|
||||
if (error.Fail())
|
||||
return false;
|
||||
|
||||
error = builder.AddException(process_sp);
|
||||
if (error.Fail())
|
||||
return false;
|
||||
|
||||
error = builder.AddMemoryList(process_sp);
|
||||
if (error.Fail())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.GetArchitecture().GetTriple().getOS() ==
|
||||
llvm::Triple::OSType::Linux) {
|
||||
builder.AddLinuxFileStreams(process_sp);
|
||||
}
|
||||
|
||||
llvm::Expected<lldb::FileUP> maybe_core_file = FileSystem::Instance().Open(
|
||||
outfile, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate);
|
||||
if (!maybe_core_file) {
|
||||
error = maybe_core_file.takeError();
|
||||
return false;
|
||||
}
|
||||
lldb::FileUP core_file = std::move(maybe_core_file.get());
|
||||
|
||||
error = builder.Dump(core_file);
|
||||
if (error.Fail())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
//===-- ObjectFileMinidump.h ---------------------------------- -*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file
|
||||
/// Placeholder plugin for the save core functionality.
|
||||
///
|
||||
/// ObjectFileMinidump is created only to be able to save minidump core files
|
||||
/// from existing processes with the ObjectFileMinidump::SaveCore function.
|
||||
/// Minidump files are not ObjectFile objects, but they are core files and
|
||||
/// currently LLDB's ObjectFile plug-ins handle emitting core files. If the
|
||||
/// core file saving ever moves into a new plug-in type within LLDB, this code
|
||||
/// should move as well, but for now this is the best place architecturally.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_OBJECTFILEMINIDUMP_H
|
||||
#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_OBJECTFILEMINIDUMP_H
|
||||
|
||||
#include "lldb/Symbol/ObjectFile.h"
|
||||
#include "lldb/Utility/ArchSpec.h"
|
||||
|
||||
class ObjectFileMinidump : public lldb_private::PluginInterface {
|
||||
public:
|
||||
// Static Functions
|
||||
static void Initialize();
|
||||
static void Terminate();
|
||||
|
||||
static lldb_private::ConstString GetPluginNameStatic();
|
||||
static const char *GetPluginDescriptionStatic() {
|
||||
return "Minidump object file.";
|
||||
}
|
||||
|
||||
// PluginInterface protocol
|
||||
lldb_private::ConstString GetPluginName() override {
|
||||
return GetPluginNameStatic();
|
||||
}
|
||||
|
||||
static lldb_private::ObjectFile *
|
||||
CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
|
||||
lldb::offset_t data_offset, const lldb_private::FileSpec *file,
|
||||
lldb::offset_t offset, lldb::offset_t length);
|
||||
|
||||
static lldb_private::ObjectFile *CreateMemoryInstance(
|
||||
const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
|
||||
const lldb::ProcessSP &process_sp, lldb::addr_t header_addr);
|
||||
|
||||
static size_t GetModuleSpecifications(const lldb_private::FileSpec &file,
|
||||
lldb::DataBufferSP &data_sp,
|
||||
lldb::offset_t data_offset,
|
||||
lldb::offset_t file_offset,
|
||||
lldb::offset_t length,
|
||||
lldb_private::ModuleSpecList &specs);
|
||||
|
||||
uint32_t GetPluginVersion() override { return 1; }
|
||||
|
||||
// Saves dump in Minidump file format
|
||||
static bool SaveCore(const lldb::ProcessSP &process_sp,
|
||||
const lldb_private::FileSpec &outfile,
|
||||
lldb::SaveCoreStyle &core_style,
|
||||
lldb_private::Status &error);
|
||||
|
||||
private:
|
||||
ObjectFileMinidump() = default;
|
||||
};
|
||||
|
||||
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_OBJECTFILEMINIDUMP_H
|
||||
@@ -1,6 +0,0 @@
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
CFLAGS_EXTRAS := -lpthread
|
||||
|
||||
include Makefile.rules
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
"""
|
||||
Test saving a mini dump.
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class ProcessSaveCoreMinidumpTestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
@skipUnlessPlatform(['linux'])
|
||||
def test_save_linux_mini_dump(self):
|
||||
"""Test that we can save a Linux mini dump."""
|
||||
self.build()
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
core = self.getBuildArtifact("core.dmp")
|
||||
try:
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
process = target.LaunchSimple(
|
||||
None, None, self.get_process_working_directory())
|
||||
self.assertEqual(process.GetState(), lldb.eStateStopped)
|
||||
|
||||
# get neccessary data for the verification phase
|
||||
process_info = process.GetProcessInfo()
|
||||
expected_pid = process_info.GetProcessID() if process_info.IsValid() else -1
|
||||
expected_number_of_modules = target.GetNumModules()
|
||||
expected_modules = target.modules
|
||||
expected_number_of_threads = process.GetNumThreads()
|
||||
expected_threads = []
|
||||
|
||||
for thread_idx in range(process.GetNumThreads()):
|
||||
thread = process.GetThreadAtIndex(thread_idx)
|
||||
thread_id = thread.GetThreadID()
|
||||
expected_threads.append(thread_id)
|
||||
|
||||
# save core and, kill process and verify corefile existence
|
||||
self.runCmd("process save-core --plugin-name=minidump --style=stack " + core)
|
||||
self.assertTrue(os.path.isfile(core))
|
||||
self.assertTrue(process.Kill().Success())
|
||||
|
||||
# To verify, we'll launch with the mini dump
|
||||
target = self.dbg.CreateTarget(None)
|
||||
process = target.LoadCore(core)
|
||||
|
||||
# check if the core is in desired state
|
||||
self.assertTrue(process, PROCESS_IS_VALID)
|
||||
self.assertTrue(process.GetProcessInfo().IsValid())
|
||||
self.assertEqual(process.GetProcessInfo().GetProcessID(), expected_pid)
|
||||
self.assertTrue(target.GetTriple().find("linux") != -1)
|
||||
self.assertTrue(target.GetNumModules(), expected_number_of_modules)
|
||||
self.assertEqual(process.GetNumThreads(), expected_number_of_threads)
|
||||
|
||||
for module, expected in zip(target.modules, expected_modules):
|
||||
self.assertTrue(module.IsValid())
|
||||
module_file_name = module.GetFileSpec().GetFilename()
|
||||
expected_file_name = expected.GetFileSpec().GetFilename()
|
||||
# skip kernel virtual dynamic shared objects
|
||||
if "vdso" in expected_file_name:
|
||||
continue
|
||||
self.assertEqual(module_file_name, expected_file_name)
|
||||
self.assertEqual(module.GetUUIDString(), expected.GetUUIDString())
|
||||
|
||||
for thread_idx in range(process.GetNumThreads()):
|
||||
thread = process.GetThreadAtIndex(thread_idx)
|
||||
self.assertTrue(thread.IsValid())
|
||||
thread_id = thread.GetThreadID()
|
||||
self.assertTrue(thread_id in expected_threads)
|
||||
finally:
|
||||
# Clean up the mini dump file.
|
||||
self.assertTrue(self.dbg.DeleteTarget(target))
|
||||
if (os.path.isfile(core)):
|
||||
os.unlink(core)
|
||||
@@ -1,30 +0,0 @@
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void g() { assert(false); }
|
||||
|
||||
void f() { g(); }
|
||||
|
||||
size_t h() {
|
||||
size_t sum = 0;
|
||||
for (size_t i = 0; i < 1000000; ++i)
|
||||
for (size_t j = 0; j < 1000000; ++j)
|
||||
if ((i * j) % 2 == 0) {
|
||||
sum += 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
int main() {
|
||||
thread t1(f);
|
||||
|
||||
size_t x = h();
|
||||
|
||||
t1.join();
|
||||
|
||||
cout << "X is " << x << "\n";
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user