[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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
|
||||
lldb_private::ThreadCollection::collection GetThreadsToSave() const;
|
||||
|
||||
llvm::Expected<uint64_t> GetCurrentSizeInBytes();
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 : ''
|
||||
|
||||
Reference in New Issue
Block a user