[Lldb-commits] [lldb] eee687a - [lldb] Add minidump save-core functionality to ELF object files
Richard Smith via lldb-commits
lldb-commits at lists.llvm.org
Wed Sep 1 16:53:43 PDT 2021
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 at 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 at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/lldb-commits/attachments/20210901/a2f62bb9/attachment-0001.html>
More information about the lldb-commits
mailing list