https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/146777
>From d17473cc32acb31935759012ca87342d750d68f7 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 09:18:59 -0700 Subject: [PATCH 1/8] Fix logs, prevent accidentally printing a partial read when that's not true, and fix where we write the base RVA 8 bytes earlier than it starts --- .../Minidump/MinidumpFileBuilder.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 806f256d9da48..34a71f41f3b84 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -977,6 +977,7 @@ Status MinidumpFileBuilder::ReadWriteMemoryInChunks( const lldb::addr_t addr = range.range.start(); const lldb::addr_t size = range.range.size(); Log *log = GetLog(LLDBLog::Object); + uint64_t total_bytes_read = 0; Status addDataError; Process::ReadMemoryChunkCallback callback = [&](Status &error, lldb::addr_t current_addr, const void *buf, @@ -984,7 +985,7 @@ Status MinidumpFileBuilder::ReadWriteMemoryInChunks( if (error.Fail() || bytes_read == 0) { LLDB_LOGF(log, "Failed to read memory region at: 0x%" PRIx64 - ". Bytes read: %" PRIx64 ", error: %s", + ". Bytes read: 0x%" PRIx64 ", error: %s", current_addr, bytes_read, error.AsCString()); // If we failed in a memory read, we would normally want to skip @@ -997,6 +998,10 @@ Status MinidumpFileBuilder::ReadWriteMemoryInChunks( return lldb_private::IterationAction::Stop; } + if (current_addr != addr + total_bytes_read) { + LLDB_LOGF(log, "Current addr is at expected address, 0x%" PRIx64 ", expected at 0x%" PRIx64, current_addr, addr + total_bytes_read); + } + // Write to the minidump file with the chunk potentially flushing to // disk. // This error will be captured by the outer scope and is considered fatal. @@ -1006,13 +1011,14 @@ Status MinidumpFileBuilder::ReadWriteMemoryInChunks( if (addDataError.Fail()) return lldb_private::IterationAction::Stop; + total_bytes_read += bytes_read; // If we have a partial read, report it, but only if the partial read // didn't finish reading the entire region. if (bytes_read != data_buffer.GetByteSize() && - current_addr + bytes_read != size) { + total_bytes_read != size) { LLDB_LOGF(log, - "Memory region at: %" PRIx64 " partiall read 0x%" PRIx64 - " bytes out of %" PRIx64 " bytes.", + "Memory region at: 0x%" PRIx64 " partial read 0x%" PRIx64 + " bytes out of 0x%" PRIx64 " bytes.", current_addr, bytes_read, data_buffer.GetByteSize() - bytes_read); @@ -1059,7 +1065,7 @@ MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges, LLDB_LOGF(log, "AddMemoryList %zu/%zu reading memory for region " - "(%" PRIx64 " bytes) [%" PRIx64 ", %" PRIx64 ")", + "(0x%" PRIx64 " bytes) [0x%" PRIx64 ", 0x%" PRIx64 ")", region_index, ranges.size(), size, addr, addr + size); ++region_index; @@ -1130,7 +1136,7 @@ MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges, // Capture the starting offset for all the descriptors so we can clean them up // if needed. offset_t starting_offset = - GetCurrentDataEndOffset() + sizeof(llvm::support::ulittle64_t); + GetCurrentDataEndOffset() + sizeof(llvm::minidump::Memory64ListHeader); // The base_rva needs to start after the directories, which is right after // this 8 byte variable. offset_t base_rva = >From cbb5ee7914c201323730b73dee9d0394f012673c Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 11:01:03 -0700 Subject: [PATCH 2/8] Add new iterator to SBSaveCoreOptions to enable better test asserts --- .../include/lldb/API/SBMemoryRegionInfoList.h | 1 + lldb/include/lldb/API/SBSaveCoreOptions.h | 8 ++++ lldb/include/lldb/Symbol/SaveCoreOptions.h | 2 + lldb/source/API/SBSaveCoreOptions.cpp | 19 ++++++++ .../Minidump/MinidumpFileBuilder.cpp | 2 +- lldb/source/Symbol/SaveCoreOptions.cpp | 23 +++++++++- .../TestSBSaveCoreOptions.py | 44 +++++++++++++++++++ 7 files changed, 97 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/API/SBMemoryRegionInfoList.h b/lldb/include/lldb/API/SBMemoryRegionInfoList.h index 1d939dff55faa..8ac9c1aceb6f6 100644 --- a/lldb/include/lldb/API/SBMemoryRegionInfoList.h +++ b/lldb/include/lldb/API/SBMemoryRegionInfoList.h @@ -45,6 +45,7 @@ class LLDB_API SBMemoryRegionInfoList { private: friend class SBProcess; + friend class SBSaveCoreOptions; lldb_private::MemoryRegionInfos &ref(); diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index 37552c13d0f36..a965f4448cbf0 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -15,6 +15,7 @@ #include "lldb/API/SBProcess.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" +#include "lldb/API/SBMemoryRegionInfoList.h" namespace lldb { @@ -119,6 +120,13 @@ class LLDB_API SBSaveCoreOptions { /// an empty collection will be returned. SBThreadCollection GetThreadsToSave() const; + /// Get an unsorted copy of all memory regions to save + /// + /// \returns + /// An unsorted copy of all memory regions to save. If no process or style + /// is specified an empty collection will be returned. + SBMemoryRegionInfoList GetMemoryRegionsToSave(); + /// Get the current total number of bytes the core is expected to have /// excluding the overhead of the core file format. Requires a Process and /// Style to be specified. diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index da66b184745db..2a171ba2d8ee2 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -10,6 +10,7 @@ #define LLDB_SOURCE_PLUGINS_OBJECTFILE_SaveCoreOPTIONS_H #include "lldb/Target/ThreadCollection.h" +#include "lldb/Target/CoreFileMemoryRanges.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/RangeMap.h" @@ -47,6 +48,7 @@ class SaveCoreOptions { void AddMemoryRegionToSave(const lldb_private::MemoryRegionInfo ®ion); + llvm::Expected<lldb_private::CoreFileMemoryRanges> GetMemoryRegionsToSave(); lldb_private::ThreadCollection::collection GetThreadsToSave() const; llvm::Expected<uint64_t> GetCurrentSizeInBytes(); diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 15584abaac013..2b71cb695e584 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -128,6 +128,25 @@ uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { return *expected_bytes; } +lldb::SBMemoryRegionInfoList SBSaveCoreOptions::GetMemoryRegionsToSave() { + LLDB_INSTRUMENT_VA(this); + llvm::Expected<lldb_private::CoreFileMemoryRanges> memory_ranges = m_opaque_up->GetMemoryRegionsToSave(); + if (!memory_ranges) { + llvm::consumeError(memory_ranges.takeError()); + return SBMemoryRegionInfoList(); + } + + + SBMemoryRegionInfoList m_memory_region_infos; + for (const auto &range : *memory_ranges) { + SBMemoryRegionInfo region_info(nullptr, + range.GetRangeBase(), range.GetRangeEnd(), range.data.lldb_permissions, /*mapped=*/true); + m_memory_region_infos.Append(region_info); + } + + return m_memory_region_infos; +} + lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { return *m_opaque_up; } diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 34a71f41f3b84..3adcb2633a29e 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -1138,7 +1138,7 @@ MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges, offset_t starting_offset = GetCurrentDataEndOffset() + sizeof(llvm::minidump::Memory64ListHeader); // The base_rva needs to start after the directories, which is right after - // this 8 byte variable. + // the descriptors + the size of the header. offset_t base_rva = starting_offset + (ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64)); diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index f93b58f59cf96..dfabe3a62ed1d 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -155,6 +155,23 @@ SaveCoreOptions::GetThreadsToSave() const { return thread_collection; } +llvm::Expected<lldb_private::CoreFileMemoryRanges> SaveCoreOptions::GetMemoryRegionsToSave() { + Status error; + if (!m_process_sp) + return Status::FromErrorString("Requires a process to be set.").takeError(); + + error = EnsureValidConfiguration(m_process_sp); + if (error.Fail()) + return error.takeError(); + + CoreFileMemoryRanges ranges; + error = m_process_sp->CalculateCoreFileSaveRanges(*this, ranges); + if (error.Fail()) + return error.takeError(); + + return ranges; +} + llvm::Expected<uint64_t> SaveCoreOptions::GetCurrentSizeInBytes() { Status error; if (!m_process_sp) @@ -169,8 +186,12 @@ llvm::Expected<uint64_t> SaveCoreOptions::GetCurrentSizeInBytes() { if (error.Fail()) return error.takeError(); + llvm::Expected<lldb_private::CoreFileMemoryRanges> core_file_ranges_maybe = GetMemoryRegionsToSave(); + if (!core_file_ranges_maybe) + return core_file_ranges_maybe.takeError(); + const lldb_private::CoreFileMemoryRanges &core_file_ranges = *core_file_ranges_maybe; uint64_t total_in_bytes = 0; - for (auto &core_range : ranges) + for (auto &core_range : core_file_ranges) total_in_bytes += core_range.data.range.size(); return total_in_bytes; diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index 31e35e0285f17..ec889db983ea0 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -164,3 +164,47 @@ def test_get_total_in_bytes_missing_requirements(self): options.SetStyle(lldb.eSaveCoreCustomOnly) total = options.GetCurrentSizeInBytes(error) self.assertTrue(error.Fail(), error.GetCString()) + + def test_get_memory_regions_to_save(self): + """ + Tests the matrix of responses for GetMemoryRegionsToSave + """ + + options = lldb.SBSaveCoreOptions() + + # Not specifying plugin or process should return an empty list. + memory_list = options.GetMemoryRegionsToSave() + self.assertEqual(0, memory_list.GetSize()) + + # No style returns an empty list + process = self.get_basic_process() + options.SetProcess(process) + memory_list = options.GetMemoryRegionsToSave() + self.assertEqual(0, memory_list.GetSize()) + options.Clear() + + # No Process returns an empty list + options.SetStyle(lldb.eSaveCoreCustomOnly) + memory_list = options.GetMemoryRegionsToSave() + self.assertEqual(0, memory_list.GetSize()) + options.Clear() + + + # Validate we get back the single region we populate + options.SetStyle(lldb.eSaveCoreCustomOnly) + process = self.get_basic_process() + options.SetProcess(process) + memory_range = lldb.SBMemoryRegionInfo() + + # Add the memory range of 0x1000-0x1100 + process.GetMemoryRegionInfo(0x1000, memory_range) + options.AddMemoryRegionToSave(memory_range) + memory_list = options.GetMemoryRegionsToSave() + self.assertEqual(1, memory_list.GetSize()) + read_region = lldb.SBMemoryRegionInfo() + memory_list.GetMemoryRegionAtIndex(0, read_region) + + # Permissions from Process getLLDBRegion aren't matching up with + # the live process permissions, so we're just checking the range for now. + self.assertEqual(memory_range.GetRegionBase(), read_region.GetRegionBase()) + self.assertEqual(memory_range.GetRegionEnd(), read_region.GetRegionEnd()) >From d5d443c49f47d2da93f06e7c672279e3978bd4bd Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 11:16:17 -0700 Subject: [PATCH 3/8] Create flag api, and define it in MinidumpFileBuilder --- lldb/include/lldb/API/SBSaveCoreOptions.h | 8 ++++++++ lldb/include/lldb/Symbol/SaveCoreOptions.h | 7 ++++++- lldb/source/API/SBSaveCoreOptions.cpp | 6 ++++++ .../ObjectFile/Minidump/MinidumpFileBuilder.cpp | 5 +++++ .../ObjectFile/Minidump/MinidumpFileBuilder.h | 2 ++ lldb/source/Symbol/SaveCoreOptions.cpp | 14 ++++++++++++++ 6 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index a965f4448cbf0..92c5c12a8ae33 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -140,6 +140,14 @@ class LLDB_API SBSaveCoreOptions { /// The expected size of the data contained in the core in bytes. uint64_t GetCurrentSizeInBytes(SBError &error); + /// Add a flag specific to a plugin provider, null or empty flags + /// will be ignored. + /// + /// \note + /// This API is currently only used for testing, with forcing Minidumps to + /// to 64b memory list the reason this api was added + void AddFlag(const char* flag); + /// Reset all options. void Clear(); diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index 2a171ba2d8ee2..9ff4b494c2bf7 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -24,7 +24,7 @@ namespace lldb_private { class SaveCoreOptions { public: - SaveCoreOptions(){}; + SaveCoreOptions() = default; ~SaveCoreOptions() = default; lldb_private::Status SetPluginName(const char *name); @@ -53,6 +53,10 @@ class SaveCoreOptions { llvm::Expected<uint64_t> GetCurrentSizeInBytes(); + void AddFlag(const char *flag); + + bool ContainsFlag(const char *flag) const; + void Clear(); private: @@ -61,6 +65,7 @@ class SaveCoreOptions { std::optional<std::string> m_plugin_name; std::optional<lldb_private::FileSpec> m_file; std::optional<lldb::SaveCoreStyle> m_style; + std::optional<std::unordered_set<std::string>> m_flags; lldb::ProcessSP m_process_sp; std::unordered_set<lldb::tid_t> m_threads_to_save; MemoryRanges m_regions_to_save; diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 2b71cb695e584..7696912a56bbd 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -147,6 +147,12 @@ lldb::SBMemoryRegionInfoList SBSaveCoreOptions::GetMemoryRegionsToSave() { return m_memory_region_infos; } +void SBSaveCoreOptions::AddFlag(const char *flag) { + LLDB_INSTRUMENT_VA(this, flag); + + m_opaque_up->AddFlag(flag); +} + lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const { return *m_opaque_up; } diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 3adcb2633a29e..349797c80376b 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -831,6 +831,11 @@ Status MinidumpFileBuilder::AddLinuxFileStreams() { Status MinidumpFileBuilder::AddMemoryList() { Status error; + // Note this is here for testing. In the past there has been many occasions that the 64b + // code has regressed because it's wasteful and expensive to write a 4.2gb+ on every CI run + // to get around this and to exercise this codepath we define a flag in the options object. + bool force_64b_for_non_threads = m_save_core_options.ContainsFlag(&FORCE_64B_FLAG); + // We first save the thread stacks to ensure they fit in the first UINT32_MAX // bytes of the core file. Thread structures in minidump files can only use // 32 bit memory descriptiors, so we emit them first to ensure the memory is diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h index 46b20f90138fe..978fc7b44a384 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h @@ -176,6 +176,8 @@ class MinidumpFileBuilder { static constexpr size_t HEADER_SIZE = sizeof(llvm::minidump::Header); static constexpr size_t DIRECTORY_SIZE = sizeof(llvm::minidump::Directory); + static const char[10] FORCE_64B_FLAG = "force_64b"; + // More that one place can mention the register thread context locations, // so when we emit the thread contents, remember where it is so we don't have // to duplicate it in the exception data. diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index dfabe3a62ed1d..58d8b59f7b843 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -211,3 +211,17 @@ void SaveCoreOptions::Clear() { m_process_sp.reset(); m_regions_to_save.Clear(); } + +void SaveCoreOptions::AddFlag(const char *flag) { + if (!flag || !flag[0]) + return; + + if (!m_flags) + m_flags = std::unordered_set<std::string>(); + + m_flags->emplace(std::string(flag)); +} + +bool SaveCoreOptions::ContainsFlag(const char *flag) const { + return m_flags && m_flags->find(flag) != m_flags->end(); +} >From 3c4adde006fd6add000178b79cc4c571adfb220a Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 13:13:20 -0700 Subject: [PATCH 4/8] Add new test class, and do some code fix-up --- lldb/include/lldb/API/SBSaveCoreOptions.h | 2 +- .../Minidump/MinidumpFileBuilder.cpp | 4 +- .../ObjectFile/Minidump/MinidumpFileBuilder.h | 2 +- .../TestProcessSaveCoreMinidump64b.py | 84 +++++++++++++++++++ 4 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index 92c5c12a8ae33..e42b6e2823775 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -140,7 +140,7 @@ class LLDB_API SBSaveCoreOptions { /// The expected size of the data contained in the core in bytes. uint64_t GetCurrentSizeInBytes(SBError &error); - /// Add a flag specific to a plugin provider, null or empty flags + /// Add a flag to be consumed by the specified plugin, null or empty flags /// will be ignored. /// /// \note diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 349797c80376b..cb1fb2cb5ca25 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -834,7 +834,7 @@ Status MinidumpFileBuilder::AddMemoryList() { // Note this is here for testing. In the past there has been many occasions that the 64b // code has regressed because it's wasteful and expensive to write a 4.2gb+ on every CI run // to get around this and to exercise this codepath we define a flag in the options object. - bool force_64b_for_non_threads = m_save_core_options.ContainsFlag(&FORCE_64B_FLAG); + bool force_64b_for_non_threads = m_save_core_options.ContainsFlag(FORCE_64B_FLAG); // We first save the thread stacks to ensure they fit in the first UINT32_MAX // bytes of the core file. Thread structures in minidump files can only use @@ -895,7 +895,7 @@ Status MinidumpFileBuilder::AddMemoryList() { const addr_t range_size = core_range.range.size(); // We don't need to check for stacks here because we already removed them // from all_core_memory_ranges. - if (total_size + range_size < UINT32_MAX) { + if (!force_64b_for_non_threads && total_size + range_size < UINT32_MAX) { ranges_32.push_back(core_range); total_size += range_size; } else { diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h index 978fc7b44a384..cc2520d7f0b16 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h @@ -176,7 +176,7 @@ class MinidumpFileBuilder { static constexpr size_t HEADER_SIZE = sizeof(llvm::minidump::Header); static constexpr size_t DIRECTORY_SIZE = sizeof(llvm::minidump::Directory); - static const char[10] FORCE_64B_FLAG = "force_64b"; + static constexpr const char FORCE_64B_FLAG[] = "force_64b"; // More that one place can mention the register thread context locations, // so when we emit the thread contents, remember where it is so we don't have diff --git a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py new file mode 100644 index 0000000000000..5a2300188f140 --- /dev/null +++ b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py @@ -0,0 +1,84 @@ +""" +Test saving a minidumps with the force 64b flag, and evaluate that every +saved memory region is byte-wise 1:1 with the live process. +""" + +import os +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +# Constant from MinidumpFileBuilder.h, this forces 64b for non threads +FORCE_64B = "force_64b" + +class ProcessSaveCoreMinidump64bTestCase(TestBase): + + def verify_minidump( + self, + core_proc, + live_proc, + options, + ): + """Verify that the minidump is the same byte for byte as the live process.""" + # Get the memory regions we saved off in this core, we can't compare to the core + # because we pull from /proc/pid/maps, so even ranges that don't get mapped in will show up + # as ranges in the minidump. + # + # Instead, we have an API that returns to us the number of regions we planned to save from the live process + # and we compare those + memory_regions_to_compare = options.GetMemoryRegionsToSave() + + for region in memory_regions_to_compare: + start_addr = region.GetRegionBase() + end_addr = region.GetRegionEnd() + actual_process_read_error = lldb.SBError() + actual = live_proc.ReadMemory(start_addr, end_addr - start_addr, actual_process_read_error) + expected_process_read_error = lldb.SBError() + expected = core_proc.ReadMemory(start_addr, end_addr - start_addr, expected_process_read_error) + + # Both processes could fail to read a given memory region, so if they both pass + # compare, then we'll fail them if the core differs from the live process. + if (actual_process_read_error.Success() and expected_process_read_error.Success()): + self.assertEqual(actual, expected, "Bytes differ between live process and core") + + # Now we check if the error is the same, error isn't abnormal but they should fail for the same reason + self.assertTrue( + (actual_process_read_error.Success() and expected_process_read_error.Success()) or + (actual_process_read_error.Fail() and expected_process_read_error.Fail()) + ) + + @skipUnlessArch("x86_64") + @skipUnlessPlatform(["linux"]) + def test_minidump_save_style_full(self): + """Test that a full minidump is the same byte for byte.""" + + self.build() + exe = self.getBuildArtifact("a.out") + minidump_path = self.getBuildArtifact("minidump_full_force64b.dmp") + + try: + target = self.dbg.CreateTarget(exe) + live_process = target.LaunchSimple( + None, None, self.get_process_working_directory() + ) + self.assertState(live_process.GetState(), lldb.eStateStopped) + options = lldb.SBSaveCoreOptions() + + options.SetOutputFile(lldb.SBFileSpec(minidump_path)) + options.SetStyle(lldb.eSaveCoreFull) + options.SetPluginName("minidump") + options.SetProcess(live_process) + options.AddFlag(FORCE_64B) + + error = live_process.SaveCore(options) + self.assertTrue(error.Success(), error.GetCString()) + + target = self.dbg.CreateTarget(None) + core_proc = target.LoadCore(minidump_path) + + self.verify_minidump(core_proc, live_process, options) + finally: + self.assertTrue(self.dbg.DeleteTarget(target)) + if os.path.isfile(minidump_path): + os.unlink(minidump_path) >From 6c762afea5a7e1a337cffec37838935743fe46e0 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 13:14:23 -0700 Subject: [PATCH 5/8] GCF --- lldb/include/lldb/API/SBSaveCoreOptions.h | 6 +++--- lldb/include/lldb/Symbol/SaveCoreOptions.h | 2 +- lldb/source/API/SBSaveCoreOptions.cpp | 9 +++++---- .../Minidump/MinidumpFileBuilder.cpp | 18 +++++++++++------- lldb/source/Symbol/SaveCoreOptions.cpp | 9 ++++++--- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index e42b6e2823775..e72dd53780ab9 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -12,10 +12,10 @@ #include "lldb/API/SBDefines.h" #include "lldb/API/SBError.h" #include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" -#include "lldb/API/SBMemoryRegionInfoList.h" namespace lldb { @@ -123,7 +123,7 @@ class LLDB_API SBSaveCoreOptions { /// Get an unsorted copy of all memory regions to save /// /// \returns - /// An unsorted copy of all memory regions to save. If no process or style + /// An unsorted copy of all memory regions to save. If no process or style /// is specified an empty collection will be returned. SBMemoryRegionInfoList GetMemoryRegionsToSave(); @@ -146,7 +146,7 @@ class LLDB_API SBSaveCoreOptions { /// \note /// This API is currently only used for testing, with forcing Minidumps to /// to 64b memory list the reason this api was added - void AddFlag(const char* flag); + void AddFlag(const char *flag); /// Reset all options. void Clear(); diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index 9ff4b494c2bf7..92e474c1131a1 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -9,8 +9,8 @@ #ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_SaveCoreOPTIONS_H #define LLDB_SOURCE_PLUGINS_OBJECTFILE_SaveCoreOPTIONS_H -#include "lldb/Target/ThreadCollection.h" #include "lldb/Target/CoreFileMemoryRanges.h" +#include "lldb/Target/ThreadCollection.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/RangeMap.h" diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 7696912a56bbd..0d618bc1916b9 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -130,17 +130,18 @@ uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) { lldb::SBMemoryRegionInfoList SBSaveCoreOptions::GetMemoryRegionsToSave() { LLDB_INSTRUMENT_VA(this); - llvm::Expected<lldb_private::CoreFileMemoryRanges> memory_ranges = m_opaque_up->GetMemoryRegionsToSave(); + llvm::Expected<lldb_private::CoreFileMemoryRanges> memory_ranges = + m_opaque_up->GetMemoryRegionsToSave(); if (!memory_ranges) { llvm::consumeError(memory_ranges.takeError()); return SBMemoryRegionInfoList(); } - SBMemoryRegionInfoList m_memory_region_infos; for (const auto &range : *memory_ranges) { - SBMemoryRegionInfo region_info(nullptr, - range.GetRangeBase(), range.GetRangeEnd(), range.data.lldb_permissions, /*mapped=*/true); + SBMemoryRegionInfo region_info( + nullptr, range.GetRangeBase(), range.GetRangeEnd(), + range.data.lldb_permissions, /*mapped=*/true); m_memory_region_infos.Append(region_info); } diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index cb1fb2cb5ca25..8d8d04d1b1aba 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -831,10 +831,12 @@ Status MinidumpFileBuilder::AddLinuxFileStreams() { Status MinidumpFileBuilder::AddMemoryList() { Status error; - // Note this is here for testing. In the past there has been many occasions that the 64b - // code has regressed because it's wasteful and expensive to write a 4.2gb+ on every CI run - // to get around this and to exercise this codepath we define a flag in the options object. - bool force_64b_for_non_threads = m_save_core_options.ContainsFlag(FORCE_64B_FLAG); + // Note this is here for testing. In the past there has been many occasions + // that the 64b code has regressed because it's wasteful and expensive to + // write a 4.2gb+ on every CI run to get around this and to exercise this + // codepath we define a flag in the options object. + bool force_64b_for_non_threads = + m_save_core_options.ContainsFlag(FORCE_64B_FLAG); // We first save the thread stacks to ensure they fit in the first UINT32_MAX // bytes of the core file. Thread structures in minidump files can only use @@ -1004,7 +1006,10 @@ Status MinidumpFileBuilder::ReadWriteMemoryInChunks( } if (current_addr != addr + total_bytes_read) { - LLDB_LOGF(log, "Current addr is at expected address, 0x%" PRIx64 ", expected at 0x%" PRIx64, current_addr, addr + total_bytes_read); + LLDB_LOGF(log, + "Current addr is at expected address, 0x%" PRIx64 + ", expected at 0x%" PRIx64, + current_addr, addr + total_bytes_read); } // Write to the minidump file with the chunk potentially flushing to @@ -1019,8 +1024,7 @@ Status MinidumpFileBuilder::ReadWriteMemoryInChunks( total_bytes_read += bytes_read; // If we have a partial read, report it, but only if the partial read // didn't finish reading the entire region. - if (bytes_read != data_buffer.GetByteSize() && - total_bytes_read != size) { + if (bytes_read != data_buffer.GetByteSize() && total_bytes_read != size) { LLDB_LOGF(log, "Memory region at: 0x%" PRIx64 " partial read 0x%" PRIx64 " bytes out of 0x%" PRIx64 " bytes.", diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index 58d8b59f7b843..6b6387f821590 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -155,7 +155,8 @@ SaveCoreOptions::GetThreadsToSave() const { return thread_collection; } -llvm::Expected<lldb_private::CoreFileMemoryRanges> SaveCoreOptions::GetMemoryRegionsToSave() { +llvm::Expected<lldb_private::CoreFileMemoryRanges> +SaveCoreOptions::GetMemoryRegionsToSave() { Status error; if (!m_process_sp) return Status::FromErrorString("Requires a process to be set.").takeError(); @@ -186,10 +187,12 @@ llvm::Expected<uint64_t> SaveCoreOptions::GetCurrentSizeInBytes() { if (error.Fail()) return error.takeError(); - llvm::Expected<lldb_private::CoreFileMemoryRanges> core_file_ranges_maybe = GetMemoryRegionsToSave(); + llvm::Expected<lldb_private::CoreFileMemoryRanges> core_file_ranges_maybe = + GetMemoryRegionsToSave(); if (!core_file_ranges_maybe) return core_file_ranges_maybe.takeError(); - const lldb_private::CoreFileMemoryRanges &core_file_ranges = *core_file_ranges_maybe; + const lldb_private::CoreFileMemoryRanges &core_file_ranges = + *core_file_ranges_maybe; uint64_t total_in_bytes = 0; for (auto &core_range : core_file_ranges) total_in_bytes += core_range.data.range.size(); >From fa488d0608b961395f0cb41a5b8e2002d0a5fcff Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 13:14:42 -0700 Subject: [PATCH 6/8] Python formatting --- .../TestProcessSaveCoreMinidump64b.py | 28 +++++++++++++++---- .../TestSBSaveCoreOptions.py | 1 - 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py index 5a2300188f140..b1ce13a047439 100644 --- a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py +++ b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py @@ -12,6 +12,7 @@ # Constant from MinidumpFileBuilder.h, this forces 64b for non threads FORCE_64B = "force_64b" + class ProcessSaveCoreMinidump64bTestCase(TestBase): def verify_minidump( @@ -33,19 +34,34 @@ def verify_minidump( start_addr = region.GetRegionBase() end_addr = region.GetRegionEnd() actual_process_read_error = lldb.SBError() - actual = live_proc.ReadMemory(start_addr, end_addr - start_addr, actual_process_read_error) + actual = live_proc.ReadMemory( + start_addr, end_addr - start_addr, actual_process_read_error + ) expected_process_read_error = lldb.SBError() - expected = core_proc.ReadMemory(start_addr, end_addr - start_addr, expected_process_read_error) + expected = core_proc.ReadMemory( + start_addr, end_addr - start_addr, expected_process_read_error + ) # Both processes could fail to read a given memory region, so if they both pass # compare, then we'll fail them if the core differs from the live process. - if (actual_process_read_error.Success() and expected_process_read_error.Success()): - self.assertEqual(actual, expected, "Bytes differ between live process and core") + if ( + actual_process_read_error.Success() + and expected_process_read_error.Success() + ): + self.assertEqual( + actual, expected, "Bytes differ between live process and core" + ) # Now we check if the error is the same, error isn't abnormal but they should fail for the same reason self.assertTrue( - (actual_process_read_error.Success() and expected_process_read_error.Success()) or - (actual_process_read_error.Fail() and expected_process_read_error.Fail()) + ( + actual_process_read_error.Success() + and expected_process_read_error.Success() + ) + or ( + actual_process_read_error.Fail() + and expected_process_read_error.Fail() + ) ) @skipUnlessArch("x86_64") diff --git a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py index ec889db983ea0..92ca44ecbbffc 100644 --- a/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py +++ b/lldb/test/API/python_api/sbsavecoreoptions/TestSBSaveCoreOptions.py @@ -189,7 +189,6 @@ def test_get_memory_regions_to_save(self): self.assertEqual(0, memory_list.GetSize()) options.Clear() - # Validate we get back the single region we populate options.SetStyle(lldb.eSaveCoreCustomOnly) process = self.get_basic_process() >From 2dc4a46246325ea01cb8583661c412b37547c929 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 13:56:41 -0700 Subject: [PATCH 7/8] Add docstring --- .../bindings/interface/SBSaveCoreOptionsDocstrings.i | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i index 6907164a1b95c..a44c0569f40e6 100644 --- a/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i +++ b/lldb/bindings/interface/SBSaveCoreOptionsDocstrings.i @@ -63,6 +63,18 @@ Note that currently ELF Core files are not supported." Get an SBThreadCollection of all threads marked to be saved. This collection is not sorted according to insertion order." ) lldb::SBSaveCoreOptions::GetThreadsToSave; + +%feature("docstring", " + Get an SBMemoryRegionInfoList of all the Regions that LLDB will attempt to write into the Core. Note, reading from these + regions can fail, and it's guaraunteed every region will be present. If called without a valid process or style set an empty + collection will be returned." +) lldb::SBSaveCoreOptions::GetMemoryRegionsToSave; + +%feature("docstring", " + Add a plugin specific flag to the objects option." +) lldb::SBSaveCoreOptions::AddFlag; + + %feature("docstring", " Get the current total number of bytes the core is expected to have, excluding the overhead of the core file format. Requires both a Process and a Style to be specified. An error will be returned if the provided options would result in no data being saved." >From 082ecc6cb60cc856eb746a3f4a43b84761bee41f Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Wed, 2 Jul 2025 14:02:35 -0700 Subject: [PATCH 8/8] Manual python formatting --- .../process_save_core_minidump/TestProcessSaveCoreMinidump64b.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py index b1ce13a047439..b672629ad7453 100644 --- a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py +++ b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump64b.py @@ -14,7 +14,6 @@ class ProcessSaveCoreMinidump64bTestCase(TestBase): - def verify_minidump( self, core_proc, _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits