https://github.com/Jlalond updated https://github.com/llvm/llvm-project/pull/107159
>From 63a343d2613d09a866180c8bebdf4568e20fd3b7 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Thu, 29 Aug 2024 10:09:19 -0700 Subject: [PATCH 1/3] =?UTF-8?q?Reapply=20"[LLDB][SBSaveCore]=20Add=20selec?= =?UTF-8?q?table=20memory=20regions=20to=20SBSaveCor=E2=80=A6=20(#106293)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b9595324846a96dd3443359a62c70cec5aa352b8. --- lldb/include/lldb/API/SBMemoryRegionInfo.h | 2 +- lldb/include/lldb/API/SBSaveCoreOptions.h | 11 ++ lldb/include/lldb/Symbol/SaveCoreOptions.h | 11 +- lldb/include/lldb/Target/Process.h | 5 +- lldb/include/lldb/Utility/RangeMap.h | 2 + lldb/include/lldb/lldb-enumerations.h | 1 + lldb/include/lldb/lldb-forward.h | 1 + lldb/include/lldb/lldb-private-interfaces.h | 1 - lldb/source/API/SBSaveCoreOptions.cpp | 11 ++ lldb/source/Commands/CommandObjectProcess.cpp | 1 + .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 4 +- .../ObjectFile/Mach-O/ObjectFileMachO.h | 1 + .../Minidump/MinidumpFileBuilder.cpp | 33 ++-- .../ObjectFile/Minidump/MinidumpFileBuilder.h | 9 +- .../ObjectFile/Minidump/ObjectFileMinidump.h | 1 + .../ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 1 + .../ObjectFile/PECOFF/ObjectFilePECOFF.h | 1 + lldb/source/Symbol/SaveCoreOptions.cpp | 14 ++ lldb/source/Target/Process.cpp | 70 +++++++- .../TestProcessSaveCoreMinidump.py | 155 ++++++++++++++++++ 20 files changed, 304 insertions(+), 31 deletions(-) diff --git a/lldb/include/lldb/API/SBMemoryRegionInfo.h b/lldb/include/lldb/API/SBMemoryRegionInfo.h index be55de4ead1fa8..f9a5dc993d7cb6 100644 --- a/lldb/include/lldb/API/SBMemoryRegionInfo.h +++ b/lldb/include/lldb/API/SBMemoryRegionInfo.h @@ -120,7 +120,7 @@ class LLDB_API SBMemoryRegionInfo { private: friend class SBProcess; friend class SBMemoryRegionInfoList; - + friend class SBSaveCoreOptions; friend class lldb_private::ScriptInterpreter; lldb_private::MemoryRegionInfo &ref(); diff --git a/lldb/include/lldb/API/SBSaveCoreOptions.h b/lldb/include/lldb/API/SBSaveCoreOptions.h index ba48ba5eaea5a0..c076d3ce6f7575 100644 --- a/lldb/include/lldb/API/SBSaveCoreOptions.h +++ b/lldb/include/lldb/API/SBSaveCoreOptions.h @@ -80,6 +80,17 @@ class LLDB_API SBSaveCoreOptions { /// \return True if the thread was removed, false if it was not in the list. bool RemoveThread(lldb::SBThread thread); + /// Add a memory region to save in the core file. + /// + /// \param region The memory region to save. + /// \returns An empty SBError upon success, or an error if the region is + /// invalid. + /// \note Ranges that overlapped will be unioned into a single region, this + /// also supercedes stack minification. Specifying full regions and a + /// non-custom core style will include the specified regions and union them + /// with all style specific regions. + SBError AddMemoryRegionToSave(const SBMemoryRegionInfo ®ion); + /// Reset all options. void Clear(); diff --git a/lldb/include/lldb/Symbol/SaveCoreOptions.h b/lldb/include/lldb/Symbol/SaveCoreOptions.h index f4fed4676fa4ae..d90d08026016dc 100644 --- a/lldb/include/lldb/Symbol/SaveCoreOptions.h +++ b/lldb/include/lldb/Symbol/SaveCoreOptions.h @@ -10,13 +10,15 @@ #define LLDB_SOURCE_PLUGINS_OBJECTFILE_SaveCoreOPTIONS_H #include "lldb/Utility/FileSpec.h" -#include "lldb/lldb-forward.h" -#include "lldb/lldb-types.h" +#include "lldb/Utility/RangeMap.h" #include <optional> +#include <set> #include <string> #include <unordered_set> +using MemoryRanges = lldb_private::RangeVector<lldb::addr_t, lldb::addr_t>; + namespace lldb_private { class SaveCoreOptions { @@ -38,8 +40,12 @@ class SaveCoreOptions { Status AddThread(lldb::ThreadSP thread_sp); bool RemoveThread(lldb::ThreadSP thread_sp); bool ShouldThreadBeSaved(lldb::tid_t tid) const; + bool HasSpecifiedThreads() const; Status EnsureValidConfiguration(lldb::ProcessSP process_sp) const; + const MemoryRanges &GetCoreFileMemoryRanges() const; + + void AddMemoryRegionToSave(const lldb_private::MemoryRegionInfo ®ion); void Clear(); @@ -51,6 +57,7 @@ class SaveCoreOptions { std::optional<lldb::SaveCoreStyle> m_style; lldb::ProcessSP m_process_sp; std::unordered_set<lldb::tid_t> m_threads_to_save; + MemoryRanges m_regions_to_save; }; } // namespace lldb_private diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index a7de991104434d..6506f8f9c16167 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -35,6 +35,7 @@ #include "lldb/Host/ProcessLaunchInfo.h" #include "lldb/Host/ProcessRunLock.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/Memory.h" @@ -731,7 +732,9 @@ class Process : public std::enable_shared_from_this<Process>, } }; - using CoreFileMemoryRanges = std::vector<CoreFileMemoryRange>; + using CoreFileMemoryRanges = + lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, + CoreFileMemoryRange>; /// Helper function for Process::SaveCore(...) that calculates the address /// ranges that should be saved. This allows all core file plug-ins to save diff --git a/lldb/include/lldb/Utility/RangeMap.h b/lldb/include/lldb/Utility/RangeMap.h index 8cc382bcc046ce..c636348129b647 100644 --- a/lldb/include/lldb/Utility/RangeMap.h +++ b/lldb/include/lldb/Utility/RangeMap.h @@ -450,6 +450,8 @@ class RangeDataVector { void Append(const Entry &entry) { m_entries.emplace_back(entry); } + void Append(B &&b, S &&s, T &&t) { m_entries.emplace_back(Entry(b, s, t)); } + bool Erase(uint32_t start, uint32_t end) { if (start >= end || end > m_entries.size()) return false; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 7bfde8b9de1271..938f6e3abe8f2a 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -1222,6 +1222,7 @@ enum SaveCoreStyle { eSaveCoreFull = 1, eSaveCoreDirtyOnly = 2, eSaveCoreStackOnly = 3, + eSaveCoreCustomOnly = 4, }; /// Events that might happen during a trace session. diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 337eff696fcf3f..5fb288ad43af48 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -207,6 +207,7 @@ class StackFrameRecognizer; class StackFrameRecognizerManager; class StackID; class Status; +class SaveCoreOptions; class StopInfo; class Stoppoint; class StoppointCallbackContext; diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h index b3c8cda899b95e..5bac5cd3e86b59 100644 --- a/lldb/include/lldb/lldb-private-interfaces.h +++ b/lldb/include/lldb/lldb-private-interfaces.h @@ -9,7 +9,6 @@ #ifndef LLDB_LLDB_PRIVATE_INTERFACES_H #define LLDB_LLDB_PRIVATE_INTERFACES_H -#include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private-enumerations.h" diff --git a/lldb/source/API/SBSaveCoreOptions.cpp b/lldb/source/API/SBSaveCoreOptions.cpp index 2cd431611ef558..5e75aa911b650b 100644 --- a/lldb/source/API/SBSaveCoreOptions.cpp +++ b/lldb/source/API/SBSaveCoreOptions.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/API/SBSaveCoreOptions.h" +#include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/Host/FileSystem.h" #include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/Utility/Instrumentation.h" @@ -90,6 +91,16 @@ bool SBSaveCoreOptions::RemoveThread(lldb::SBThread thread) { return m_opaque_up->RemoveThread(thread.GetSP()); } +lldb::SBError +SBSaveCoreOptions::AddMemoryRegionToSave(const SBMemoryRegionInfo ®ion) { + LLDB_INSTRUMENT_VA(this, region); + // Currently add memory region can't fail, so we always return a success + // SBerror, but because these API's live forever, this is the most future + // proof thing to do. + m_opaque_up->AddMemoryRegionToSave(region.ref()); + return SBError(); +} + void SBSaveCoreOptions::Clear() { LLDB_INSTRUMENT_VA(this); m_opaque_up->Clear(); diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 25eb633f1e6dad..5b0f4f66f248b6 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -25,6 +25,7 @@ #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Interpreter/OptionGroupPythonClassWithDict.h" #include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 2004622e547be9..e756eddb5f9a86 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -6568,7 +6568,9 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp, const uint32_t addr_byte_size = target_arch.GetAddressByteSize(); const ByteOrder byte_order = target_arch.GetByteOrder(); std::vector<llvm::MachO::segment_command_64> segment_load_commands; - for (const auto &core_range : core_ranges) { + for (const auto &core_range_info : core_ranges) { + // TODO: Refactor RangeDataVector to have a data iterator. + const auto &core_range = core_range_info.data; uint32_t cmd_type = LC_SEGMENT_64; uint32_t segment_size = sizeof(llvm::MachO::segment_command_64); if (addr_byte_size == 4) { diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h index 27bc237aaac48d..be87112df7d898 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -12,6 +12,7 @@ #include "lldb/Core/Address.h" #include "lldb/Host/SafeMachO.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/FileSpecList.h" #include "lldb/Utility/RangeMap.h" diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 13355afb58dbd1..96180bf0356e9a 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -828,25 +828,32 @@ Status MinidumpFileBuilder::AddMemoryList() { // 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 // in accessible with a 32 bit offset. - Process::CoreFileMemoryRanges ranges_32; - Process::CoreFileMemoryRanges ranges_64; + std::vector<Process::CoreFileMemoryRange> ranges_32; + std::vector<Process::CoreFileMemoryRange> ranges_64; Process::CoreFileMemoryRanges all_core_memory_ranges; error = m_process_sp->CalculateCoreFileSaveRanges(m_save_core_options, all_core_memory_ranges); + + std::vector<Process::CoreFileMemoryRange> all_core_memory_vec; + // Extract all the data into just a vector of data. So we can mutate this in + // place. + for (const auto &core_range : all_core_memory_ranges) + all_core_memory_vec.push_back(core_range.data); + if (error.Fail()) return error; // Start by saving all of the stacks and ensuring they fit under the 32b // limit. uint64_t total_size = GetCurrentDataEndOffset(); - auto iterator = all_core_memory_ranges.begin(); - while (iterator != all_core_memory_ranges.end()) { + auto iterator = all_core_memory_vec.begin(); + while (iterator != all_core_memory_vec.end()) { if (m_saved_stack_ranges.count(iterator->range.start()) > 0) { // We don't save stacks twice. ranges_32.push_back(*iterator); total_size += iterator->range.size() + sizeof(llvm::minidump::MemoryDescriptor); - iterator = all_core_memory_ranges.erase(iterator); + iterator = all_core_memory_vec.erase(iterator); } else { iterator++; } @@ -866,11 +873,11 @@ Status MinidumpFileBuilder::AddMemoryList() { // Then anything overflow extends into 64b addressable space. // All core memeroy ranges will either container nothing on stacks only // or all the memory ranges including stacks - if (!all_core_memory_ranges.empty()) - total_size += 256 + (all_core_memory_ranges.size() * + if (!all_core_memory_vec.empty()) + total_size += 256 + (all_core_memory_vec.size() * sizeof(llvm::minidump::MemoryDescriptor_64)); - for (const auto &core_range : all_core_memory_ranges) { + for (const auto &core_range : all_core_memory_vec) { 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. @@ -955,15 +962,15 @@ Status MinidumpFileBuilder::DumpDirectories() const { } static uint64_t -GetLargestRangeSize(const Process::CoreFileMemoryRanges &ranges) { +GetLargestRangeSize(const std::vector<Process::CoreFileMemoryRange> &ranges) { uint64_t max_size = 0; for (const auto &core_range : ranges) max_size = std::max(max_size, core_range.range.size()); return max_size; } -Status -MinidumpFileBuilder::AddMemoryList_32(Process::CoreFileMemoryRanges &ranges) { +Status MinidumpFileBuilder::AddMemoryList_32( + std::vector<Process::CoreFileMemoryRange> &ranges) { std::vector<MemoryDescriptor> descriptors; Status error; if (ranges.size() == 0) @@ -1039,8 +1046,8 @@ MinidumpFileBuilder::AddMemoryList_32(Process::CoreFileMemoryRanges &ranges) { return error; } -Status -MinidumpFileBuilder::AddMemoryList_64(Process::CoreFileMemoryRanges &ranges) { +Status MinidumpFileBuilder::AddMemoryList_64( + std::vector<Process::CoreFileMemoryRange> &ranges) { Status error; if (ranges.empty()) return error; diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h index 762de83db5a39c..8651cddeedb216 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h @@ -23,6 +23,7 @@ #include <utility> #include <variant> +#include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/DataBufferHeap.h" @@ -119,10 +120,10 @@ class MinidumpFileBuilder { // trigger a flush. lldb_private::Status AddData(const void *data, uint64_t size); // Add MemoryList stream, containing dumps of important memory segments - lldb_private::Status - AddMemoryList_64(lldb_private::Process::CoreFileMemoryRanges &ranges); - lldb_private::Status - AddMemoryList_32(lldb_private::Process::CoreFileMemoryRanges &ranges); + lldb_private::Status AddMemoryList_64( + std::vector<lldb_private::Process::CoreFileMemoryRange> &ranges); + lldb_private::Status AddMemoryList_32( + std::vector<lldb_private::Process::CoreFileMemoryRange> &ranges); // Update the thread list on disk with the newly emitted stack RVAs. lldb_private::Status FixThreadStacks(); lldb_private::Status FlushBufferToDisk(); diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h index b76fcd0052a8a8..2f45f01558e667 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h @@ -21,6 +21,7 @@ #define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_OBJECTFILEMINIDUMP_H #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/Utility/ArchSpec.h" class ObjectFileMinidump : public lldb_private::PluginInterface { diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index 9d01089745dfc9..8d9c919bc9b101 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -17,6 +17,7 @@ #include "lldb/Interpreter/OptionValueDictionary.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SaveCoreOptions.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h index 8bccf3be3e5f63..4f4dedf773c5ba 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h @@ -13,6 +13,7 @@ #include <vector> #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SaveCoreOptions.h" #include "llvm/Object/COFF.h" class ObjectFilePECOFF : public lldb_private::ObjectFile { diff --git a/lldb/source/Symbol/SaveCoreOptions.cpp b/lldb/source/Symbol/SaveCoreOptions.cpp index 35943726f2e4ef..8d9aadece2152d 100644 --- a/lldb/source/Symbol/SaveCoreOptions.cpp +++ b/lldb/source/Symbol/SaveCoreOptions.cpp @@ -102,6 +102,19 @@ bool SaveCoreOptions::ShouldThreadBeSaved(lldb::tid_t tid) const { return m_threads_to_save.count(tid) > 0; } +bool SaveCoreOptions::HasSpecifiedThreads() const { + return !m_threads_to_save.empty(); +} + +void SaveCoreOptions::AddMemoryRegionToSave( + const lldb_private::MemoryRegionInfo ®ion) { + m_regions_to_save.Insert(region.GetRange(), /*combine=*/true); +} + +const MemoryRanges &SaveCoreOptions::GetCoreFileMemoryRanges() const { + return m_regions_to_save; +} + Status SaveCoreOptions::EnsureValidConfiguration( lldb::ProcessSP process_sp) const { Status error; @@ -131,4 +144,5 @@ void SaveCoreOptions::Clear() { m_style = std::nullopt; m_threads_to_save.clear(); m_process_sp.reset(); + m_regions_to_save.Clear(); } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index ae64f6f261bad7..e063c4774f4a2e 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6529,14 +6529,14 @@ static bool AddDirtyPages(const MemoryRegionInfo ®ion, } else { // Add previous contiguous range and init the new range with the // current dirty page. - ranges.push_back({range, lldb_permissions}); + ranges.Append(range.start(), range.end(), {range, lldb_permissions}); range = llvm::AddressRange(page_addr, page_addr + page_size); } } } // The last range if (!range.empty()) - ranges.push_back({range, lldb_permissions}); + ranges.Append(range.start(), range.end(), {range, lldb_permissions}); return true; } @@ -6557,7 +6557,10 @@ static void AddRegion(const MemoryRegionInfo ®ion, bool try_dirty_pages, return; if (try_dirty_pages && AddDirtyPages(region, ranges)) return; - ranges.push_back(CreateCoreFileMemoryRange(region)); + + ranges.Append(region.GetRange().GetRangeBase(), + region.GetRange().GetByteSize(), + CreateCoreFileMemoryRange(region)); } static void SaveOffRegionsWithStackPointers( @@ -6607,7 +6610,7 @@ static void GetCoreFileSaveRangesFull(Process &process, std::set<addr_t> &stack_ends) { // Don't add only dirty pages, add full regions. -const bool try_dirty_pages = false; + const bool try_dirty_pages = false; for (const auto ®ion : regions) if (stack_ends.count(region.GetRange().GetRangeEnd()) == 0) AddRegion(region, try_dirty_pages, ranges); @@ -6663,6 +6666,49 @@ static void GetCoreFileSaveRangesStackOnly( } } +static void GetUserSpecifiedCoreFileSaveRanges( + Process &process, const MemoryRegionInfos ®ions, + const SaveCoreOptions &options, Process::CoreFileMemoryRanges &ranges) { + const auto &option_ranges = options.GetCoreFileMemoryRanges(); + if (option_ranges.IsEmpty()) + return; + + for (const auto &range : regions) { + auto entry = option_ranges.FindEntryThatContains(range.GetRange()); + if (entry) + ranges.Append(range.GetRange().GetRangeBase(), + range.GetRange().GetByteSize(), + CreateCoreFileMemoryRange(range)); + } +} + +static Status +FinalizeCoreFileSaveRanges(Process::CoreFileMemoryRanges &ranges) { + Status error; + ranges.Sort(); + for (size_t i = ranges.GetSize() - 1; i > 0; i--) { + auto region = ranges.GetMutableEntryAtIndex(i); + auto next_region = ranges.GetMutableEntryAtIndex(i - 1); + if (next_region->GetRangeEnd() >= region->GetRangeBase() && + region->GetRangeBase() <= next_region->GetRangeEnd() && + region->data.lldb_permissions == next_region->data.lldb_permissions) { + const addr_t base = + std::min(region->GetRangeBase(), next_region->GetRangeBase()); + const addr_t byte_size = + std::max(region->GetRangeEnd(), next_region->GetRangeEnd()) - base; + next_region->SetRangeBase(base); + next_region->SetByteSize(byte_size); + if (!ranges.Erase(i, i + 1)) { + error = Status::FromErrorString( + "Core file memory ranges mutated outside of " + "CalculateCoreFileSaveRanges"); + return error; + } + } + } + return error; +} + Status Process::CalculateCoreFileSaveRanges(const SaveCoreOptions &options, CoreFileMemoryRanges &ranges) { lldb_private::MemoryRegionInfos regions; @@ -6678,11 +6724,18 @@ Status Process::CalculateCoreFileSaveRanges(const SaveCoreOptions &options, "callers must set the core_style to something other than " "eSaveCoreUnspecified"); + GetUserSpecifiedCoreFileSaveRanges(*this, regions, options, ranges); + std::set<addr_t> stack_ends; - SaveOffRegionsWithStackPointers(*this, options, regions, ranges, stack_ends); + // For fully custom set ups, we don't want to even look at threads if there + // are no threads specified. + if (core_style != lldb::eSaveCoreCustomOnly || options.HasSpecifiedThreads()) + SaveOffRegionsWithStackPointers(*this, options, regions, ranges, + stack_ends); switch (core_style) { case eSaveCoreUnspecified: + case eSaveCoreCustomOnly: break; case eSaveCoreFull: @@ -6701,10 +6754,11 @@ Status Process::CalculateCoreFileSaveRanges(const SaveCoreOptions &options, if (err.Fail()) return err; - if (ranges.empty()) - return Status("no valid address ranges found for core style"); + if (ranges.IsEmpty()) + return Status::FromErrorString( + "no valid address ranges found for core style"); - return Status(); // Success! + return FinalizeCoreFileSaveRanges(ranges); } std::vector<ThreadSP> diff --git a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py index ea59aef004aff5..eb1259bfc74d1e 100644 --- a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py +++ b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py @@ -305,9 +305,12 @@ def test_save_linux_mini_dump_default_options(self): thread_id = thread.GetThreadID() expected_threads.append(thread_id) stacks_to_sp_map[thread_id] = thread.GetFrameAtIndex(0).GetSP() +<<<<<<< HEAD stacks_to_registers_map[thread_id] = thread.GetFrameAtIndex( 0 ).GetRegisters() +======= +>>>>>>> 32f571ab967c (Reapply "[LLDB][SBSaveCore] Add selectable memory regions to SBSaveCor… (#106293)) # This is almost identical to the single thread test case because # minidump defaults to stacks only, so we want to see if the @@ -325,10 +328,162 @@ def test_save_linux_mini_dump_default_options(self): expected_modules, expected_threads, stacks_to_sp_map, +<<<<<<< HEAD stacks_to_registers_map, +======= +>>>>>>> 32f571ab967c (Reapply "[LLDB][SBSaveCore] Add selectable memory regions to SBSaveCor… (#106293)) ) finally: self.assertTrue(self.dbg.DeleteTarget(target)) if os.path.isfile(default_value_file): os.unlink(default_value_file) + + @skipUnlessArch("x86_64") + @skipUnlessPlatform(["linux"]) + def test_save_linux_minidump_one_region(self): + """Test that we can save a Linux mini dump with one region in sbsavecore regions""" + + self.build() + exe = self.getBuildArtifact("a.out") + one_region_file = self.getBuildArtifact("core.one_region.dmp") + try: + target = self.dbg.CreateTarget(exe) + process = target.LaunchSimple( + None, None, self.get_process_working_directory() + ) + self.assertState(process.GetState(), lldb.eStateStopped) + + memory_region = lldb.SBMemoryRegionInfo() + memory_list = process.GetMemoryRegions() + memory_list.GetMemoryRegionAtIndex(0, memory_region) + + # This is almost identical to the single thread test case because + # minidump defaults to stacks only, so we want to see if the + # default options work as expected. + options = lldb.SBSaveCoreOptions() + file_spec = lldb.SBFileSpec(one_region_file) + options.SetOutputFile(file_spec) + options.SetPluginName("minidump") + options.AddMemoryRegionToSave(memory_region) + options.SetStyle(lldb.eSaveCoreCustomOnly) + error = process.SaveCore(options) + print(f"Error: {error.GetCString()}") + self.assertTrue(error.Success(), error.GetCString()) + + core_target = self.dbg.CreateTarget(None) + core_proc = core_target.LoadCore(one_region_file) + core_memory_list = core_proc.GetMemoryRegions() + # Note because the /proc/pid maps are included on linux, we can't + # depend on size for validation, so we'll ensure the first region + # is present and then assert we fail on the second. + core_memory_region = lldb.SBMemoryRegionInfo() + core_memory_list.GetMemoryRegionAtIndex(0, core_memory_region) + self.assertEqual( + core_memory_region.GetRegionBase(), memory_region.GetRegionBase() + ) + self.assertEqual( + core_memory_region.GetRegionEnd(), memory_region.GetRegionEnd() + ) + + region_two = lldb.SBMemoryRegionInfo() + core_memory_list.GetMemoryRegionAtIndex(1, region_two) + err = lldb.SBError() + content = core_proc.ReadMemory(region_two.GetRegionBase(), 1, err) + self.assertTrue(err.Fail(), "Should fail to read memory") + + finally: + self.assertTrue(self.dbg.DeleteTarget(target)) + if os.path.isfile(one_region_file): + os.unlink(one_region_file) + + @skipUnlessArch("x86_64") + @skipUnlessPlatform(["linux"]) + def test_save_minidump_custom_save_style(self): + """Test that verifies a custom and unspecified save style fails for + containing no data to save""" + + self.build() + exe = self.getBuildArtifact("a.out") + custom_file = self.getBuildArtifact("core.custom.dmp") + try: + target = self.dbg.CreateTarget(exe) + process = target.LaunchSimple( + None, None, self.get_process_working_directory() + ) + self.assertState(process.GetState(), lldb.eStateStopped) + + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(custom_file)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreCustomOnly) + + error = process.SaveCore(options) + self.assertTrue(error.Fail()) + self.assertEqual( + error.GetCString(), "no valid address ranges found for core style" + ) + + finally: + self.assertTrue(self.dbg.DeleteTarget(target)) + if os.path.isfile(custom_file): + os.unlink(custom_file) + + def save_core_with_region(self, process, region_index): + try: + custom_file = self.getBuildArtifact("core.custom.dmp") + memory_region = lldb.SBMemoryRegionInfo() + memory_list = process.GetMemoryRegions() + memory_list.GetMemoryRegionAtIndex(0, memory_region) + options = lldb.SBSaveCoreOptions() + options.SetOutputFile(lldb.SBFileSpec(custom_file)) + options.SetPluginName("minidump") + options.SetStyle(lldb.eSaveCoreFull) + + error = process.SaveCore(options) + self.assertTrue(error.Success()) + core_target = self.dbg.CreateTarget(None) + core_proc = core_target.LoadCore(custom_file) + core_memory_list = core_proc.GetMemoryRegions() + # proc/pid/ maps are included on linux, so we can't depend on size + # for validation, we make a set of all the ranges, + # and ensure no duplicates! + range_set = set() + for x in range(core_memory_list.GetSize()): + core_memory_region = lldb.SBMemoryRegionInfo() + core_memory_list.GetMemoryRegionAtIndex(x, core_memory_region) + mem_tuple = ( + core_memory_region.GetRegionBase(), + core_memory_region.GetRegionEnd(), + ) + self.assertTrue( + mem_tuple not in range_set, "Duplicate memory region found" + ) + range_set.add(mem_tuple) + finally: + if os.path.isfile(custom_file): + os.unlink(custom_file) + + @skipUnlessArch("x86_64") + @skipUnlessPlatform(["linux"]) + def test_save_minidump_custom_save_style_duplicated_regions(self): + """Test that verifies a custom and unspecified save style fails for + containing no data to save""" + + self.build() + exe = self.getBuildArtifact("a.out") + try: + target = self.dbg.CreateTarget(exe) + process = target.LaunchSimple( + None, None, self.get_process_working_directory() + ) + self.assertState(process.GetState(), lldb.eStateStopped) + + memory_list = process.GetMemoryRegions() + # Test that we don't duplicate regions, by duplicating regions + # at various indices. + self.save_core_with_region(process, 0) + self.save_core_with_region(process, len(memory_list) - 1) + + finally: + self.assertTrue(self.dbg.DeleteTarget(target)) >From 6b40db1c986dedcaea50b2eeb3ad1e5815cbc1e9 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Tue, 3 Sep 2024 15:15:18 -0700 Subject: [PATCH 2/3] Move the CoreFileMemoryRanges to it's own class, and add some unit tests. Fix the big where we only update the range on data, not the entry list --- .../lldb/Target/CoreFileMemoryRanges.h | 49 ++++++++ lldb/include/lldb/Target/Process.h | 26 +---- .../ObjectFile/Mach-O/ObjectFileMachO.cpp | 2 +- .../Minidump/MinidumpFileBuilder.cpp | 14 +-- .../ObjectFile/Minidump/MinidumpFileBuilder.h | 4 +- lldb/source/Target/CMakeLists.txt | 1 + lldb/source/Target/CoreFileMemoryRanges.cpp | 48 ++++++++ lldb/source/Target/Process.cpp | 45 ++------ .../TestProcessSaveCoreMinidump.py | 6 - lldb/unittests/Process/Utility/CMakeLists.txt | 1 + .../Utility/CoreFileMemoryRangesTest.cpp | 105 ++++++++++++++++++ 11 files changed, 224 insertions(+), 77 deletions(-) create mode 100644 lldb/include/lldb/Target/CoreFileMemoryRanges.h create mode 100644 lldb/source/Target/CoreFileMemoryRanges.cpp create mode 100644 lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp diff --git a/lldb/include/lldb/Target/CoreFileMemoryRanges.h b/lldb/include/lldb/Target/CoreFileMemoryRanges.h new file mode 100644 index 00000000000000..705ad9bab8a944 --- /dev/null +++ b/lldb/include/lldb/Target/CoreFileMemoryRanges.h @@ -0,0 +1,49 @@ +//===-- CoreFileMemoryRanges.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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/Status.h" + +#include "llvm/ADT/AddressRanges.h" + +#ifndef LLDB_TARGET_COREFILEMEMORYRANGES_H +#define LLDB_TARGET_COREFILEMEMORYRANGES_H + +namespace lldb_private { + +struct CoreFileMemoryRange { + llvm::AddressRange range; /// The address range to save into the core file. + uint32_t lldb_permissions; /// A bit set of lldb::Permissions bits. + + bool operator==(const CoreFileMemoryRange &rhs) const { + return range == rhs.range && lldb_permissions == rhs.lldb_permissions; + } + + bool operator!=(const CoreFileMemoryRange &rhs) const { + return !(*this == rhs); + } + + bool operator<(const CoreFileMemoryRange &rhs) const { + if (range < rhs.range) + return true; + if (range == rhs.range) + return lldb_permissions < rhs.lldb_permissions; + return false; + } +}; + + +class CoreFileMemoryRanges : public lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, CoreFileMemoryRange> { + public: + /// Finalize and merge all overlapping ranges in this collection. Ranges + /// will be seperated based on permissions. + Status FinalizeCoreFileSaveRanges(); +}; +} // namespace lldb_private + +#endif // LLDB_TARGET_COREFILEMEMORYRANGES_H diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 6506f8f9c16167..c9df4bd1aa2b0b 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -36,6 +36,7 @@ #include "lldb/Host/ProcessRunLock.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SaveCoreOptions.h" +#include "lldb/Target/CoreFileMemoryRanges.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/InstrumentationRuntime.h" #include "lldb/Target/Memory.h" @@ -711,31 +712,6 @@ class Process : public std::enable_shared_from_this<Process>, /// is not supported by the plugin, error otherwise. virtual llvm::Expected<bool> SaveCore(llvm::StringRef outfile); - struct CoreFileMemoryRange { - llvm::AddressRange range; /// The address range to save into the core file. - uint32_t lldb_permissions; /// A bit set of lldb::Permissions bits. - - bool operator==(const CoreFileMemoryRange &rhs) const { - return range == rhs.range && lldb_permissions == rhs.lldb_permissions; - } - - bool operator!=(const CoreFileMemoryRange &rhs) const { - return !(*this == rhs); - } - - bool operator<(const CoreFileMemoryRange &rhs) const { - if (range < rhs.range) - return true; - if (range == rhs.range) - return lldb_permissions < rhs.lldb_permissions; - return false; - } - }; - - using CoreFileMemoryRanges = - lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, - CoreFileMemoryRange>; - /// Helper function for Process::SaveCore(...) that calculates the address /// ranges that should be saved. This allows all core file plug-ins to save /// consistent memory ranges given a \a core_style. diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index e756eddb5f9a86..3284119b6dbd40 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -6562,7 +6562,7 @@ bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp, } if (make_core) { - Process::CoreFileMemoryRanges core_ranges; + CoreFileMemoryRanges core_ranges; error = process_sp->CalculateCoreFileSaveRanges(options, core_ranges); if (error.Success()) { const uint32_t addr_byte_size = target_arch.GetAddressByteSize(); diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 96180bf0356e9a..e621de24d12ec6 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -828,13 +828,13 @@ Status MinidumpFileBuilder::AddMemoryList() { // 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 // in accessible with a 32 bit offset. - std::vector<Process::CoreFileMemoryRange> ranges_32; - std::vector<Process::CoreFileMemoryRange> ranges_64; - Process::CoreFileMemoryRanges all_core_memory_ranges; + std::vector<CoreFileMemoryRange> ranges_32; + std::vector<CoreFileMemoryRange> ranges_64; + CoreFileMemoryRanges all_core_memory_ranges; error = m_process_sp->CalculateCoreFileSaveRanges(m_save_core_options, all_core_memory_ranges); - std::vector<Process::CoreFileMemoryRange> all_core_memory_vec; + std::vector<CoreFileMemoryRange> all_core_memory_vec; // Extract all the data into just a vector of data. So we can mutate this in // place. for (const auto &core_range : all_core_memory_ranges) @@ -962,7 +962,7 @@ Status MinidumpFileBuilder::DumpDirectories() const { } static uint64_t -GetLargestRangeSize(const std::vector<Process::CoreFileMemoryRange> &ranges) { +GetLargestRangeSize(const std::vector<CoreFileMemoryRange> &ranges) { uint64_t max_size = 0; for (const auto &core_range : ranges) max_size = std::max(max_size, core_range.range.size()); @@ -970,7 +970,7 @@ GetLargestRangeSize(const std::vector<Process::CoreFileMemoryRange> &ranges) { } Status MinidumpFileBuilder::AddMemoryList_32( - std::vector<Process::CoreFileMemoryRange> &ranges) { + std::vector<CoreFileMemoryRange> &ranges) { std::vector<MemoryDescriptor> descriptors; Status error; if (ranges.size() == 0) @@ -1047,7 +1047,7 @@ Status MinidumpFileBuilder::AddMemoryList_32( } Status MinidumpFileBuilder::AddMemoryList_64( - std::vector<Process::CoreFileMemoryRange> &ranges) { + std::vector<CoreFileMemoryRange> &ranges) { Status error; if (ranges.empty()) return error; diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h index 8651cddeedb216..d7417dd26d796c 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h @@ -121,9 +121,9 @@ class MinidumpFileBuilder { lldb_private::Status AddData(const void *data, uint64_t size); // Add MemoryList stream, containing dumps of important memory segments lldb_private::Status AddMemoryList_64( - std::vector<lldb_private::Process::CoreFileMemoryRange> &ranges); + std::vector<lldb_private::CoreFileMemoryRange> &ranges); lldb_private::Status AddMemoryList_32( - std::vector<lldb_private::Process::CoreFileMemoryRange> &ranges); + std::vector<lldb_private::CoreFileMemoryRange> &ranges); // Update the thread list on disk with the newly emitted stack RVAs. lldb_private::Status FixThreadStacks(); lldb_private::Status FlushBufferToDisk(); diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index a42c44b761dc56..a6d2eace975420 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -11,6 +11,7 @@ add_lldb_library(lldbTarget ABI.cpp AssertFrameRecognizer.cpp DynamicRegisterInfo.cpp + CoreFileMemoryRanges.cpp ExecutionContext.cpp InstrumentationRuntime.cpp InstrumentationRuntimeStopInfo.cpp diff --git a/lldb/source/Target/CoreFileMemoryRanges.cpp b/lldb/source/Target/CoreFileMemoryRanges.cpp new file mode 100644 index 00000000000000..c935a3afafe393 --- /dev/null +++ b/lldb/source/Target/CoreFileMemoryRanges.cpp @@ -0,0 +1,48 @@ +//===-- CoreFileMemoryRanges.cpp --------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/CoreFileMemoryRanges.h" + +using namespace lldb; +using namespace lldb_private; + +Status CoreFileMemoryRanges::FinalizeCoreFileSaveRanges() { + Status error; + std::vector<size_t> indexes_to_remove; + this->Sort(); + for (size_t i = this->GetSize() - 1; i > 0; i--) { + auto region = this->GetMutableEntryAtIndex(i); + auto next_region = this->GetMutableEntryAtIndex(i - 1); + if (next_region->GetRangeEnd() >= region->GetRangeBase() && + region->GetRangeBase() <= next_region->GetRangeEnd() && + region->data.lldb_permissions == next_region->data.lldb_permissions) { + const addr_t base = + std::min(region->GetRangeBase(), next_region->GetRangeBase()); + const addr_t byte_size = + std::max(region->GetRangeEnd(), next_region->GetRangeEnd()) - base; + + next_region->SetRangeBase(base); + next_region->SetByteSize(byte_size); + + // Because this is a range data vector, the entry has a base as well + // as the data contained in the entry. So we have to update both. + // And llvm::AddressRange isn't mutable so we have to create a new one. + llvm::AddressRange range (base, base + byte_size); + const CoreFileMemoryRange core_range = {range, next_region->data.lldb_permissions}; + next_region->data = core_range; + if (!this->Erase(i, i + 1)) { + error = Status::FromErrorString( + "Core file memory ranges mutated outside of " + "CalculateCoreFileSaveRanges"); + return error; + } + } + } + + return error; +} diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index e063c4774f4a2e..88c305a5512190 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6498,7 +6498,7 @@ Status Process::WriteMemoryTags(lldb::addr_t addr, size_t len, } // Create a CoreFileMemoryRange from a MemoryRegionInfo -static Process::CoreFileMemoryRange +static CoreFileMemoryRange CreateCoreFileMemoryRange(const MemoryRegionInfo ®ion) { const addr_t addr = region.GetRange().GetRangeBase(); llvm::AddressRange range(addr, addr + region.GetRange().GetByteSize()); @@ -6509,7 +6509,7 @@ CreateCoreFileMemoryRange(const MemoryRegionInfo ®ion) { // were added. Return false if the dirty page information is not valid or in // the region. static bool AddDirtyPages(const MemoryRegionInfo ®ion, - Process::CoreFileMemoryRanges &ranges) { + CoreFileMemoryRanges &ranges) { const auto &dirty_page_list = region.GetDirtyPageList(); if (!dirty_page_list) return false; @@ -6548,7 +6548,7 @@ static bool AddDirtyPages(const MemoryRegionInfo ®ion, // will be added to \a ranges, else the entire range will be added to \a // ranges. static void AddRegion(const MemoryRegionInfo ®ion, bool try_dirty_pages, - Process::CoreFileMemoryRanges &ranges) { + CoreFileMemoryRanges &ranges) { // Don't add empty ranges. if (region.GetRange().GetByteSize() == 0) return; @@ -6565,7 +6565,7 @@ static void AddRegion(const MemoryRegionInfo ®ion, bool try_dirty_pages, static void SaveOffRegionsWithStackPointers( Process &process, const SaveCoreOptions &core_options, - const MemoryRegionInfos ®ions, Process::CoreFileMemoryRanges &ranges, + const MemoryRegionInfos ®ions, CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { const bool try_dirty_pages = true; @@ -6606,7 +6606,7 @@ static void SaveOffRegionsWithStackPointers( // for a full core file style. static void GetCoreFileSaveRangesFull(Process &process, const MemoryRegionInfos ®ions, - Process::CoreFileMemoryRanges &ranges, + CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { // Don't add only dirty pages, add full regions. @@ -6622,7 +6622,7 @@ static void GetCoreFileSaveRangesFull(Process &process, // page information fall back to saving out all ranges with write permissions. static void GetCoreFileSaveRangesDirtyOnly( Process &process, const MemoryRegionInfos ®ions, - Process::CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { + CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { // Iterate over the regions and find all dirty pages. bool have_dirty_page_info = false; @@ -6653,7 +6653,7 @@ static void GetCoreFileSaveRangesDirtyOnly( // stack region. static void GetCoreFileSaveRangesStackOnly( Process &process, const MemoryRegionInfos ®ions, - Process::CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { + CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { const bool try_dirty_pages = true; // Some platforms support annotating the region information that tell us that // it comes from a thread stack. So look for those regions first. @@ -6668,7 +6668,7 @@ static void GetCoreFileSaveRangesStackOnly( static void GetUserSpecifiedCoreFileSaveRanges( Process &process, const MemoryRegionInfos ®ions, - const SaveCoreOptions &options, Process::CoreFileMemoryRanges &ranges) { + const SaveCoreOptions &options, CoreFileMemoryRanges &ranges) { const auto &option_ranges = options.GetCoreFileMemoryRanges(); if (option_ranges.IsEmpty()) return; @@ -6682,33 +6682,6 @@ static void GetUserSpecifiedCoreFileSaveRanges( } } -static Status -FinalizeCoreFileSaveRanges(Process::CoreFileMemoryRanges &ranges) { - Status error; - ranges.Sort(); - for (size_t i = ranges.GetSize() - 1; i > 0; i--) { - auto region = ranges.GetMutableEntryAtIndex(i); - auto next_region = ranges.GetMutableEntryAtIndex(i - 1); - if (next_region->GetRangeEnd() >= region->GetRangeBase() && - region->GetRangeBase() <= next_region->GetRangeEnd() && - region->data.lldb_permissions == next_region->data.lldb_permissions) { - const addr_t base = - std::min(region->GetRangeBase(), next_region->GetRangeBase()); - const addr_t byte_size = - std::max(region->GetRangeEnd(), next_region->GetRangeEnd()) - base; - next_region->SetRangeBase(base); - next_region->SetByteSize(byte_size); - if (!ranges.Erase(i, i + 1)) { - error = Status::FromErrorString( - "Core file memory ranges mutated outside of " - "CalculateCoreFileSaveRanges"); - return error; - } - } - } - return error; -} - Status Process::CalculateCoreFileSaveRanges(const SaveCoreOptions &options, CoreFileMemoryRanges &ranges) { lldb_private::MemoryRegionInfos regions; @@ -6758,7 +6731,7 @@ Status Process::CalculateCoreFileSaveRanges(const SaveCoreOptions &options, return Status::FromErrorString( "no valid address ranges found for core style"); - return FinalizeCoreFileSaveRanges(ranges); + return ranges.FinalizeCoreFileSaveRanges(); } std::vector<ThreadSP> diff --git a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py index eb1259bfc74d1e..bf57dcf4d6531b 100644 --- a/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py +++ b/lldb/test/API/functionalities/process_save_core_minidump/TestProcessSaveCoreMinidump.py @@ -305,12 +305,9 @@ def test_save_linux_mini_dump_default_options(self): thread_id = thread.GetThreadID() expected_threads.append(thread_id) stacks_to_sp_map[thread_id] = thread.GetFrameAtIndex(0).GetSP() -<<<<<<< HEAD stacks_to_registers_map[thread_id] = thread.GetFrameAtIndex( 0 ).GetRegisters() -======= ->>>>>>> 32f571ab967c (Reapply "[LLDB][SBSaveCore] Add selectable memory regions to SBSaveCor… (#106293)) # This is almost identical to the single thread test case because # minidump defaults to stacks only, so we want to see if the @@ -328,10 +325,7 @@ def test_save_linux_mini_dump_default_options(self): expected_modules, expected_threads, stacks_to_sp_map, -<<<<<<< HEAD stacks_to_registers_map, -======= ->>>>>>> 32f571ab967c (Reapply "[LLDB][SBSaveCore] Add selectable memory regions to SBSaveCor… (#106293)) ) finally: diff --git a/lldb/unittests/Process/Utility/CMakeLists.txt b/lldb/unittests/Process/Utility/CMakeLists.txt index 651f871621fdfc..ec0ff95d073b92 100644 --- a/lldb/unittests/Process/Utility/CMakeLists.txt +++ b/lldb/unittests/Process/Utility/CMakeLists.txt @@ -18,6 +18,7 @@ add_lldb_unittest(ProcessUtilityTests LinuxProcMapsTest.cpp MemoryTagManagerAArch64MTETest.cpp RegisterContextTest.cpp + CoreFileMemoryRangesTest.cpp ${PLATFORM_SOURCES} LINK_LIBS diff --git a/lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp b/lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp new file mode 100644 index 00000000000000..2bec705feac72a --- /dev/null +++ b/lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp @@ -0,0 +1,105 @@ +//===-- CoreFileMemoryRangesTests.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 "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "lldb/lldb-types.h" +#include "lldb/Target/CoreFileMemoryRanges.h" + +using namespace lldb_private; + +TEST(CoreFileMemoryRangesTest, MapOverlappingRanges) { + lldb_private::CoreFileMemoryRanges ranges; + const lldb::addr_t start_addr = 0x1000; + const lldb::addr_t increment_addr = 0x1000; + const size_t iterations = 10; + for (size_t i = 0; i < iterations; i++) { + const lldb::addr_t start = start_addr + (i * increment_addr); + const lldb::addr_t end = start + increment_addr; + // Arbitrary value + const uint32_t permissions = 0x3; + llvm::AddressRange range(start, end); + const CoreFileMemoryRange core_range = {range, permissions}; + // The range data is Start, Size, While the range is start-end. + CoreFileMemoryRanges::Entry entry = {start, end - start, core_range}; + ranges.Append(entry); + } + + Status error = ranges.FinalizeCoreFileSaveRanges(); + EXPECT_TRUE(error.Success()); + ASSERT_THAT(1, ranges.GetSize()); + const auto range = ranges.GetEntryAtIndex(0); + ASSERT_TRUE(range); + ASSERT_THAT(start_addr, range->GetRangeBase()); + ASSERT_THAT(start_addr + (iterations * increment_addr), range->GetRangeEnd()); +} + +TEST(CoreFileMemoryRangesTest, RangesSplitByPermissions) { + lldb_private::CoreFileMemoryRanges ranges; + const lldb::addr_t start_addr = 0x1000; + const lldb::addr_t increment_addr = 0x1000; + const size_t iterations = 10; + for (size_t i = 0; i < iterations; i++) { + const lldb::addr_t start = start_addr + (i * increment_addr); + const lldb::addr_t end = start + increment_addr; + const uint32_t permissions = i; + llvm::AddressRange range(start, end); + const CoreFileMemoryRange core_range = {range, permissions}; + // The range data is Start, Size, While the range is start-end. + CoreFileMemoryRanges::Entry entry = {start, end - start, core_range}; + ranges.Append(entry); + } + + Status error = ranges.FinalizeCoreFileSaveRanges(); + EXPECT_TRUE(error.Success()); + ASSERT_THAT(10, ranges.GetSize()); + const auto range = ranges.GetEntryAtIndex(0); + ASSERT_TRUE(range); + ASSERT_THAT(start_addr, range->GetRangeBase()); + ASSERT_THAT(start_addr + increment_addr, range->GetRangeEnd()); +} + +TEST(CoreFileMemoryRangesTest, MapPartialOverlappingRanges) { + lldb_private::CoreFileMemoryRanges ranges; + const lldb::addr_t start_addr = 0x1000; + const lldb::addr_t increment_addr = 0x1000; + const size_t iterations = 10; + for (size_t i = 0; i < iterations; i++) { + const lldb::addr_t start = start_addr + (i * increment_addr); + const lldb::addr_t end = start + increment_addr; + // Arbitrary value + const uint32_t permissions = 0x3; + llvm::AddressRange range(start, end); + const CoreFileMemoryRange core_range = {range, permissions}; + // The range data is Start, Size, While the range is start-end. + CoreFileMemoryRanges::Entry entry = {start, end - start, core_range}; + ranges.Append(entry); + } + + const lldb::addr_t unique_start = 0x7fff0000; + const lldb::addr_t unique_end = unique_start + increment_addr; + llvm::AddressRange range(unique_start, unique_end); + const uint32_t permissions = 0x3; + const CoreFileMemoryRange core_range = {range, permissions}; + // The range data is Start, Size, While the range is start-end. + CoreFileMemoryRanges::Entry entry = {unique_start, unique_end - unique_start, core_range}; + ranges.Append(entry); + + Status error = ranges.FinalizeCoreFileSaveRanges(); + EXPECT_TRUE(error.Success()); + ASSERT_THAT(2, ranges.GetSize()); + const auto merged_range = ranges.GetEntryAtIndex(0); + ASSERT_TRUE(merged_range); + ASSERT_THAT(start_addr, merged_range->GetRangeBase()); + ASSERT_THAT(start_addr + (iterations * increment_addr), merged_range->GetRangeEnd()); + const auto unique_range = ranges.GetEntryAtIndex(1); + ASSERT_TRUE(unique_range); + ASSERT_THAT(unique_start, unique_range->GetRangeBase()); + ASSERT_THAT(unique_end, unique_range->GetRangeEnd()); +} >From 7e31143a2fc07bf009a095b932a7d42379be0068 Mon Sep 17 00:00:00 2001 From: Jacob Lalonde <jalalo...@fb.com> Date: Tue, 3 Sep 2024 17:15:48 -0700 Subject: [PATCH 3/3] Run gcf --- .../lldb/Target/CoreFileMemoryRanges.h | 13 ++-- .../ObjectFile/Minidump/MinidumpFileBuilder.h | 8 +-- lldb/source/Target/CoreFileMemoryRanges.cpp | 59 ++++++++++--------- lldb/source/Target/Process.cpp | 30 ++++++---- .../Utility/CoreFileMemoryRangesTest.cpp | 11 ++-- 5 files changed, 65 insertions(+), 56 deletions(-) diff --git a/lldb/include/lldb/Target/CoreFileMemoryRanges.h b/lldb/include/lldb/Target/CoreFileMemoryRanges.h index 705ad9bab8a944..503ecd691e5948 100644 --- a/lldb/include/lldb/Target/CoreFileMemoryRanges.h +++ b/lldb/include/lldb/Target/CoreFileMemoryRanges.h @@ -37,12 +37,13 @@ struct CoreFileMemoryRange { } }; - -class CoreFileMemoryRanges : public lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, CoreFileMemoryRange> { - public: - /// Finalize and merge all overlapping ranges in this collection. Ranges - /// will be seperated based on permissions. - Status FinalizeCoreFileSaveRanges(); +class CoreFileMemoryRanges + : public lldb_private::RangeDataVector<lldb::addr_t, lldb::addr_t, + CoreFileMemoryRange> { +public: + /// Finalize and merge all overlapping ranges in this collection. Ranges + /// will be seperated based on permissions. + Status FinalizeCoreFileSaveRanges(); }; } // namespace lldb_private diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h index d7417dd26d796c..71001e26c00e91 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h @@ -120,10 +120,10 @@ class MinidumpFileBuilder { // trigger a flush. lldb_private::Status AddData(const void *data, uint64_t size); // Add MemoryList stream, containing dumps of important memory segments - lldb_private::Status AddMemoryList_64( - std::vector<lldb_private::CoreFileMemoryRange> &ranges); - lldb_private::Status AddMemoryList_32( - std::vector<lldb_private::CoreFileMemoryRange> &ranges); + lldb_private::Status + AddMemoryList_64(std::vector<lldb_private::CoreFileMemoryRange> &ranges); + lldb_private::Status + AddMemoryList_32(std::vector<lldb_private::CoreFileMemoryRange> &ranges); // Update the thread list on disk with the newly emitted stack RVAs. lldb_private::Status FixThreadStacks(); lldb_private::Status FlushBufferToDisk(); diff --git a/lldb/source/Target/CoreFileMemoryRanges.cpp b/lldb/source/Target/CoreFileMemoryRanges.cpp index c935a3afafe393..c244b5890ac364 100644 --- a/lldb/source/Target/CoreFileMemoryRanges.cpp +++ b/lldb/source/Target/CoreFileMemoryRanges.cpp @@ -12,37 +12,38 @@ using namespace lldb; using namespace lldb_private; Status CoreFileMemoryRanges::FinalizeCoreFileSaveRanges() { - Status error; - std::vector<size_t> indexes_to_remove; - this->Sort(); - for (size_t i = this->GetSize() - 1; i > 0; i--) { - auto region = this->GetMutableEntryAtIndex(i); - auto next_region = this->GetMutableEntryAtIndex(i - 1); - if (next_region->GetRangeEnd() >= region->GetRangeBase() && - region->GetRangeBase() <= next_region->GetRangeEnd() && - region->data.lldb_permissions == next_region->data.lldb_permissions) { - const addr_t base = - std::min(region->GetRangeBase(), next_region->GetRangeBase()); - const addr_t byte_size = - std::max(region->GetRangeEnd(), next_region->GetRangeEnd()) - base; + Status error; + std::vector<size_t> indexes_to_remove; + this->Sort(); + for (size_t i = this->GetSize() - 1; i > 0; i--) { + auto region = this->GetMutableEntryAtIndex(i); + auto next_region = this->GetMutableEntryAtIndex(i - 1); + if (next_region->GetRangeEnd() >= region->GetRangeBase() && + region->GetRangeBase() <= next_region->GetRangeEnd() && + region->data.lldb_permissions == next_region->data.lldb_permissions) { + const addr_t base = + std::min(region->GetRangeBase(), next_region->GetRangeBase()); + const addr_t byte_size = + std::max(region->GetRangeEnd(), next_region->GetRangeEnd()) - base; - next_region->SetRangeBase(base); - next_region->SetByteSize(byte_size); + next_region->SetRangeBase(base); + next_region->SetByteSize(byte_size); - // Because this is a range data vector, the entry has a base as well - // as the data contained in the entry. So we have to update both. - // And llvm::AddressRange isn't mutable so we have to create a new one. - llvm::AddressRange range (base, base + byte_size); - const CoreFileMemoryRange core_range = {range, next_region->data.lldb_permissions}; - next_region->data = core_range; - if (!this->Erase(i, i + 1)) { - error = Status::FromErrorString( - "Core file memory ranges mutated outside of " - "CalculateCoreFileSaveRanges"); - return error; - } + // Because this is a range data vector, the entry has a base as well + // as the data contained in the entry. So we have to update both. + // And llvm::AddressRange isn't mutable so we have to create a new one. + llvm::AddressRange range(base, base + byte_size); + const CoreFileMemoryRange core_range = { + range, next_region->data.lldb_permissions}; + next_region->data = core_range; + if (!this->Erase(i, i + 1)) { + error = Status::FromErrorString( + "Core file memory ranges mutated outside of " + "CalculateCoreFileSaveRanges"); + return error; } } - - return error; + } + + return error; } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 88c305a5512190..94bcd0a0715e53 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -6563,10 +6563,11 @@ static void AddRegion(const MemoryRegionInfo ®ion, bool try_dirty_pages, CreateCoreFileMemoryRange(region)); } -static void SaveOffRegionsWithStackPointers( - Process &process, const SaveCoreOptions &core_options, - const MemoryRegionInfos ®ions, CoreFileMemoryRanges &ranges, - std::set<addr_t> &stack_ends) { +static void SaveOffRegionsWithStackPointers(Process &process, + const SaveCoreOptions &core_options, + const MemoryRegionInfos ®ions, + CoreFileMemoryRanges &ranges, + std::set<addr_t> &stack_ends) { const bool try_dirty_pages = true; // Before we take any dump, we want to save off the used portions of the @@ -6620,9 +6621,10 @@ static void GetCoreFileSaveRangesFull(Process &process, // least some dirty pages, as some OS versions don't support reporting what // pages are dirty within an memory region. If no memory regions have dirty // page information fall back to saving out all ranges with write permissions. -static void GetCoreFileSaveRangesDirtyOnly( - Process &process, const MemoryRegionInfos ®ions, - CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { +static void GetCoreFileSaveRangesDirtyOnly(Process &process, + const MemoryRegionInfos ®ions, + CoreFileMemoryRanges &ranges, + std::set<addr_t> &stack_ends) { // Iterate over the regions and find all dirty pages. bool have_dirty_page_info = false; @@ -6651,9 +6653,10 @@ static void GetCoreFileSaveRangesDirtyOnly( // dirty regions as this will make the core file smaller. If the process // doesn't support dirty regions, then it will fall back to adding the full // stack region. -static void GetCoreFileSaveRangesStackOnly( - Process &process, const MemoryRegionInfos ®ions, - CoreFileMemoryRanges &ranges, std::set<addr_t> &stack_ends) { +static void GetCoreFileSaveRangesStackOnly(Process &process, + const MemoryRegionInfos ®ions, + CoreFileMemoryRanges &ranges, + std::set<addr_t> &stack_ends) { const bool try_dirty_pages = true; // Some platforms support annotating the region information that tell us that // it comes from a thread stack. So look for those regions first. @@ -6666,9 +6669,10 @@ static void GetCoreFileSaveRangesStackOnly( } } -static void GetUserSpecifiedCoreFileSaveRanges( - Process &process, const MemoryRegionInfos ®ions, - const SaveCoreOptions &options, CoreFileMemoryRanges &ranges) { +static void GetUserSpecifiedCoreFileSaveRanges(Process &process, + const MemoryRegionInfos ®ions, + const SaveCoreOptions &options, + CoreFileMemoryRanges &ranges) { const auto &option_ranges = options.GetCoreFileMemoryRanges(); if (option_ranges.IsEmpty()) return; diff --git a/lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp b/lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp index 2bec705feac72a..7f1254acf6f53a 100644 --- a/lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp +++ b/lldb/unittests/Process/Utility/CoreFileMemoryRangesTest.cpp @@ -1,4 +1,5 @@ -//===-- CoreFileMemoryRangesTests.cpp ---------------------------------------------===// +//===-- CoreFileMemoryRangesTests.cpp +//---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,8 +10,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "lldb/lldb-types.h" #include "lldb/Target/CoreFileMemoryRanges.h" +#include "lldb/lldb-types.h" using namespace lldb_private; @@ -88,7 +89,8 @@ TEST(CoreFileMemoryRangesTest, MapPartialOverlappingRanges) { const uint32_t permissions = 0x3; const CoreFileMemoryRange core_range = {range, permissions}; // The range data is Start, Size, While the range is start-end. - CoreFileMemoryRanges::Entry entry = {unique_start, unique_end - unique_start, core_range}; + CoreFileMemoryRanges::Entry entry = {unique_start, unique_end - unique_start, + core_range}; ranges.Append(entry); Status error = ranges.FinalizeCoreFileSaveRanges(); @@ -97,7 +99,8 @@ TEST(CoreFileMemoryRangesTest, MapPartialOverlappingRanges) { const auto merged_range = ranges.GetEntryAtIndex(0); ASSERT_TRUE(merged_range); ASSERT_THAT(start_addr, merged_range->GetRangeBase()); - ASSERT_THAT(start_addr + (iterations * increment_addr), merged_range->GetRangeEnd()); + ASSERT_THAT(start_addr + (iterations * increment_addr), + merged_range->GetRangeEnd()); const auto unique_range = ranges.GetEntryAtIndex(1); ASSERT_TRUE(unique_range); ASSERT_THAT(unique_start, unique_range->GetRangeBase()); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits