The new test fails under MSan: Uninitialized bytes in __interceptor_write at offset 2 inside [0x7fb1f42ed000, 18438530) ==3871==WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x55f5706515d9 in RetryAfterSignal<int, long (int, const void *, unsigned long), int, const void *, unsigned long> llvm-project/llvm/include/llvm/Support/Errno.h:38:11 #1 0x55f5706515d9 in lldb_private::NativeFile::Write(void const*, unsigned long&) llvm-project/lldb/source/Host/common/File.cpp:585:9 #2 0x55f570badbf5 in MinidumpFileBuilder::Dump(std::__msan::unique_ptr<lldb_private::File, std::__msan::default_delete<lldb_private::File> >&) const llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp:739:22 #3 0x55f570bb075c in ObjectFileMinidump::SaveCore(std::__msan::shared_ptr<lldb_private::Process> const&, lldb_private::FileSpec const&, lldb::SaveCoreStyle&, lldb_private::Status&) llvm-project/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp:114:19 #4 0x55f57048960b in lldb_private::PluginManager::SaveCore(std::__msan::shared_ptr<lldb_private::Process> const&, lldb_private::FileSpec const&, lldb::SaveCoreStyle&, lldb_private::ConstString) llvm-project/lldb/source/Core/PluginManager.cpp:696:9
Please can you take a look? On Wed, 1 Sept 2021 at 06:19, Andy Yankovsky via lldb-commits < lldb-commits@lists.llvm.org> wrote: > > Author: Andrej Korman > Date: 2021-09-01T15:14:29+02:00 > New Revision: eee687a66d76bf0b6e3746f7b8d09b0d871bff27 > > URL: > https://github.com/llvm/llvm-project/commit/eee687a66d76bf0b6e3746f7b8d09b0d871bff27 > DIFF: > https://github.com/llvm/llvm-project/commit/eee687a66d76bf0b6e3746f7b8d09b0d871bff27.diff > > LOG: [lldb] Add minidump save-core functionality to ELF object files > > This change adds save-core functionality into the ObjectFileELF that > enables > saving minidump of a stopped process. This change is mainly targeting Linux > running on x86_64 machines. Minidump should contain basic information > needed > to examine state of threads, local variables and stack traces. Full support > for other platforms is not so far implemented. API tests are using LLDB's > MinidumpParser. > > This relands commit aafa05e, reverted in 1f986f6. > Failed tests were fixed. > > Reviewed By: clayborg > > Differential Revision: https://reviews.llvm.org/D108233 > > Added: > lldb/source/Plugins/ObjectFile/Minidump/CMakeLists.txt > lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp > lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h > lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp > lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h > lldb/test/API/functionalities/process_save_core_minidump/Makefile > > lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py > lldb/test/API/functionalities/process_save_core_minidump/main.cpp > > Modified: > lldb/include/lldb/Core/PluginManager.h > lldb/source/API/SBProcess.cpp > lldb/source/Commands/CommandObjectProcess.cpp > lldb/source/Commands/Options.td > lldb/source/Core/PluginManager.cpp > lldb/source/Plugins/ObjectFile/CMakeLists.txt > > Removed: > > > > > ################################################################################ > diff --git a/lldb/include/lldb/Core/PluginManager.h > b/lldb/include/lldb/Core/PluginManager.h > index be91929c62e13..2bee2edea6360 100644 > --- a/lldb/include/lldb/Core/PluginManager.h > +++ b/lldb/include/lldb/Core/PluginManager.h > @@ -192,7 +192,8 @@ class PluginManager { > > static Status SaveCore(const lldb::ProcessSP &process_sp, > const FileSpec &outfile, > - lldb::SaveCoreStyle &core_style); > + lldb::SaveCoreStyle &core_style, > + const ConstString plugin_name); > > // ObjectContainer > static bool > > diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp > index 47c35a23b0781..a965814be1b98 100644 > --- a/lldb/source/API/SBProcess.cpp > +++ b/lldb/source/API/SBProcess.cpp > @@ -1228,7 +1228,8 @@ 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); > + error.ref() = > + PluginManager::SaveCore(process_sp, core_file, core_style, > ConstString()); > return LLDB_RECORD_RESULT(error); > } > > > diff --git a/lldb/source/Commands/CommandObjectProcess.cpp > b/lldb/source/Commands/CommandObjectProcess.cpp > index bb6220a53d4e8..b3e2f6a1a02b7 100644 > --- a/lldb/source/Commands/CommandObjectProcess.cpp > +++ b/lldb/source/Commands/CommandObjectProcess.cpp > @@ -1180,12 +1180,13 @@ 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] 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 -p plugin-name] FILE", > + eCommandRequiresProcess | eCommandTryTargetAPILock | > + eCommandProcessMustBeLaunched) {} > > ~CommandObjectProcessSaveCore() override = default; > > @@ -1208,6 +1209,9 @@ class CommandObjectProcessSaveCore : public > CommandObjectParsed { > 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( > @@ -1223,10 +1227,12 @@ class CommandObjectProcessSaveCore : public > CommandObjectParsed { > > 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: > @@ -1237,7 +1243,8 @@ class CommandObjectProcessSaveCore : public > CommandObjectParsed { > 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); > + PluginManager::SaveCore(process_sp, output_file, > corefile_style, > + m_options.m_requested_plugin_name); > if (error.Success()) { > if (corefile_style == SaveCoreStyle::eSaveCoreDirtyOnly || > corefile_style == SaveCoreStyle::eSaveCoreStackOnly) { > > diff --git a/lldb/source/Commands/Options.td > b/lldb/source/Commands/Options.td > index 5cef8f93b6989..67cfc60f9d1b5 100644 > --- a/lldb/source/Commands/Options.td > +++ b/lldb/source/Commands/Options.td > @@ -749,6 +749,9 @@ 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 > diff erent formats.">; > } > > let Command = "process trace save" in { > > diff --git a/lldb/source/Core/PluginManager.cpp > b/lldb/source/Core/PluginManager.cpp > index fcaa868b083ed..f65ec9fae277f 100644 > --- a/lldb/source/Core/PluginManager.cpp > +++ b/lldb/source/Core/PluginManager.cpp > @@ -685,10 +685,13 @@ > PluginManager::GetObjectFileCreateMemoryCallbackForPluginName( > > Status PluginManager::SaveCore(const lldb::ProcessSP &process_sp, > const FileSpec &outfile, > - lldb::SaveCoreStyle &core_style) { > + lldb::SaveCoreStyle &core_style, > + const ConstString plugin_name) { > 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; > > diff --git a/lldb/source/Plugins/ObjectFile/CMakeLists.txt > b/lldb/source/Plugins/ObjectFile/CMakeLists.txt > index 3b2cc6177d313..34ad087173f01 100644 > --- a/lldb/source/Plugins/ObjectFile/CMakeLists.txt > +++ b/lldb/source/Plugins/ObjectFile/CMakeLists.txt > @@ -1,6 +1,7 @@ > add_subdirectory(Breakpad) > add_subdirectory(ELF) > add_subdirectory(Mach-O) > +add_subdirectory(Minidump) > add_subdirectory(PDB) > add_subdirectory(PECOFF) > add_subdirectory(JIT) > > diff --git a/lldb/source/Plugins/ObjectFile/Minidump/CMakeLists.txt > b/lldb/source/Plugins/ObjectFile/Minidump/CMakeLists.txt > new file mode 100644 > index 0000000000000..ac5fba200f351 > --- /dev/null > +++ b/lldb/source/Plugins/ObjectFile/Minidump/CMakeLists.txt > @@ -0,0 +1,14 @@ > +add_lldb_library(lldbPluginObjectFileMinidump PLUGIN > + ObjectFileMinidump.cpp > + MinidumpFileBuilder.cpp > + > + LINK_LIBS > + lldbCore > + lldbHost > + lldbSymbol > + lldbTarget > + lldbUtility > + lldbPluginProcessUtility > + LINK_COMPONENTS > + Support > + ) > > diff --git > a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp > b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp > new file mode 100644 > index 0000000000000..1f9c96013ebff > --- /dev/null > +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp > @@ -0,0 +1,770 @@ > +//===-- 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::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(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(), 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(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(); > +} > \ No newline at end of file > > diff --git > a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h > b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h > new file mode 100644 > index 0000000000000..1d67505d736ec > --- /dev/null > +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h > @@ -0,0 +1,92 @@ > +//===-- 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 > \ No newline at end of file > > diff --git > a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp > b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp > new file mode 100644 > index 0000000000000..22b5ae0fa2576 > --- /dev/null > +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp > @@ -0,0 +1,119 @@ > +//===-- 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; > +} > > diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h > b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h > new file mode 100644 > index 0000000000000..d48600e0c6586 > --- /dev/null > +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h > @@ -0,0 +1,70 @@ > +//===-- 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 > \ No newline at end of file > > diff --git > a/lldb/test/API/functionalities/process_save_core_minidump/Makefile > b/lldb/test/API/functionalities/process_save_core_minidump/Makefile > new file mode 100644 > index 0000000000000..2d177981fdde1 > --- /dev/null > +++ b/lldb/test/API/functionalities/process_save_core_minidump/Makefile > @@ -0,0 +1,6 @@ > +CXX_SOURCES := main.cpp > + > +CFLAGS_EXTRAS := -lpthread > + > +include Makefile.rules > + > > diff --git > a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py > b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py > new file mode 100644 > index 0000000000000..8d9c12c7ffe61 > --- /dev/null > +++ > b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py > @@ -0,0 +1,79 @@ > +""" > +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__) > + > + @skipUnlessArch("x86_64") > + @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) > > diff --git > a/lldb/test/API/functionalities/process_save_core_minidump/main.cpp > b/lldb/test/API/functionalities/process_save_core_minidump/main.cpp > new file mode 100644 > index 0000000000000..49b471a4cc517 > --- /dev/null > +++ b/lldb/test/API/functionalities/process_save_core_minidump/main.cpp > @@ -0,0 +1,30 @@ > +#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; > +} > \ No newline at end of file > > > > _______________________________________________ > lldb-commits mailing list > lldb-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits >
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits