[Lldb-commits] [lldb] eee687a - [lldb] Add minidump save-core functionality to ELF object files

Andy Yankovsky via lldb-commits lldb-commits at lists.llvm.org
Wed Sep 1 06:19:04 PDT 2021


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 &reg_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 &reg_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 &reg_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 &reg_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 &reg_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 &reg_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


        


More information about the lldb-commits mailing list