[LLDB][SBSaveCoreOptions] Add new API to expose the expected core size in bytes (#138169)

My current internal work requires some sensitivity to IO usage. I had a
work around to calculate the expected size of a Minidump, but I've added
this PR so an automated system could look at the expected size of an
LLDB generated Minidump and then choose if it has the space or wants to
generate it.

There are some prerequisites to calculating the correct size, so I have
the API take a reference for an SBError, I originally tried to return an
SBError and instead take a uint64_t reference, but this made the API
very difficult to use in python.

Added a test case as well.
This commit is contained in:
Jacob Lalonde
2025-05-09 15:49:54 -07:00
committed by GitHub
parent 0eae457be3
commit 7517a1bb48
7 changed files with 123 additions and 0 deletions

View File

@@ -63,6 +63,11 @@ 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 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."
) lldb::SBSaveCoreOptions::GetCurrentSizeInBytes;
%feature("docstring", "
Unset all options."
) lldb::SBSaveCoreOptions::Clear;

View File

@@ -119,6 +119,19 @@ public:
/// an empty collection will be returned.
SBThreadCollection GetThreadsToSave() const;
/// 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.
///
/// \note
/// This can cause some modification of the underlying data store
/// as regions with no permissions, or invalid permissions will be removed
/// and stacks will be minified up to their stack pointer + the redzone.
///
/// \returns
/// The expected size of the data contained in the core in bytes.
uint64_t GetCurrentSizeInBytes(SBError &error);
/// Reset all options.
void Clear();

View File

@@ -49,6 +49,8 @@ public:
lldb_private::ThreadCollection::collection GetThreadsToSave() const;
llvm::Expected<uint64_t> GetCurrentSizeInBytes();
void Clear();
private:

View File

@@ -114,6 +114,20 @@ void SBSaveCoreOptions::Clear() {
m_opaque_up->Clear();
}
uint64_t SBSaveCoreOptions::GetCurrentSizeInBytes(SBError &error) {
LLDB_INSTRUMENT_VA(this, error);
llvm::Expected<uint64_t> expected_bytes =
m_opaque_up->GetCurrentSizeInBytes();
if (!expected_bytes) {
error =
SBError(lldb_private::Status::FromError(expected_bytes.takeError()));
return 0;
}
// Clear the error, so if the clearer uses it we set it to success.
error.Clear();
return *expected_bytes;
}
lldb_private::SaveCoreOptions &SBSaveCoreOptions::ref() const {
return *m_opaque_up.get();
}

View File

@@ -145,6 +145,27 @@ SaveCoreOptions::GetThreadsToSave() const {
return thread_collection;
}
llvm::Expected<uint64_t> SaveCoreOptions::GetCurrentSizeInBytes() {
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();
uint64_t total_in_bytes = 0;
for (auto &core_range : ranges)
total_in_bytes += core_range.data.range.size();
return total_in_bytes;
}
void SaveCoreOptions::ClearProcessSpecificData() {
// Deliberately not following the formatter style here to indicate that
// this method will be expanded in the future.

View File

@@ -104,3 +104,63 @@ class SBSaveCoreOptionsAPICase(TestBase):
thread_collection = options.GetThreadsToSave()
self.assertEqual(thread_collection.GetSize(), 3)
self.assertIn(middle_thread, thread_collection)
def test_get_current_size_in_bytes(self):
"""
Tests that ensures GetCurrentSizeInBytes properly returns an error without a process,
and the readable regions with a process.
"""
options = lldb.SBSaveCoreOptions()
options.SetStyle(lldb.eSaveCoreCustomOnly)
process = self.get_basic_process()
memory_range = lldb.SBMemoryRegionInfo()
# Add the memory range of 0x1000-0x1100
process.GetMemoryRegionInfo(0x1000, memory_range)
options.AddMemoryRegionToSave(memory_range)
# Check that we fail when we have no process set
# even though we added a memory region.
error = lldb.SBError()
total = options.GetCurrentSizeInBytes(error)
self.assertTrue(error.Fail(), error.GetCString())
# Check that we don't get an error now that we've added a process
options.SetProcess(process)
total = options.GetCurrentSizeInBytes(error)
self.assertTrue(error.Success(), error.GetCString())
# Validate the size returned is the same size as the single region we added.
expected_size = memory_range.GetRegionEnd() - memory_range.GetRegionBase()
self.assertEqual(total, expected_size)
def test_get_total_in_bytes_missing_requirements(self):
"""
Tests the matrix of error responses that GetCurrentSizeInBytes
"""
options = lldb.SBSaveCoreOptions()
# No process, no style returns an error.
error = lldb.SBError()
total = options.GetCurrentSizeInBytes(error)
self.assertTrue(error.Fail(), error.GetCString())
# No process returns an error
options.SetStyle(lldb.eSaveCoreCustomOnly)
total = options.GetCurrentSizeInBytes(error)
self.assertTrue(error.Fail(), error.GetCString())
options.Clear()
# No style returns an error
process = self.get_basic_process()
options.SetProcess(process)
total = options.GetCurrentSizeInBytes(error)
self.assertTrue(error.Fail(), error.GetCString())
# Options that result in no valid data returns an error.
options.SetStyle(lldb.eSaveCoreCustomOnly)
total = options.GetCurrentSizeInBytes(error)
self.assertTrue(error.Fail(), error.GetCString())

View File

@@ -34,3 +34,11 @@ Streams:
Stack:
Start of Memory Range: 0x00007FFFC8DFF000
Content: 'BAADBEEF'
- Type: Memory64List
Memory Ranges:
- Start of Memory Range: 0x1000
Data Size: 0x100
Content : ''
- Start of Memory Range: 0x2000
Data Size: 0x200
Content : ''