-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[LLDB][Minidump]Update MinidumpFileBuilder to read and write in chunks #129307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
0d40497
5c782cd
a40d40d
50304ee
60979a1
4818677
c5b1f49
0d31bb6
aa23a39
13581a6
a85c33e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -969,6 +969,64 @@ Status MinidumpFileBuilder::DumpDirectories() const { | |
| return error; | ||
| } | ||
|
|
||
| Status MinidumpFileBuilder::ReadWriteMemoryInChunks( | ||
Jlalond marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| lldb_private::DataBufferHeap &data_buffer, | ||
| const lldb_private::CoreFileMemoryRange &range, uint64_t &bytes_read) { | ||
|
|
||
| Log *log = GetLog(LLDBLog::Object); | ||
| Status addDataError; | ||
| Process::ReadMemoryChunkCallback callback = | ||
| [&](Status &error, const void *buf, lldb::addr_t current_addr, | ||
| uint64_t bytes_to_read, | ||
| uint64_t bytes_read_for_chunk) -> lldb_private::IterationAction { | ||
Jlalond marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (error.Fail() || bytes_read_for_chunk == 0) { | ||
| LLDB_LOGF(log, | ||
| "Failed to read memory region at: %" PRIx64 | ||
| ". Bytes read: %zu, error: %s", | ||
| current_addr, bytes_read_for_chunk, error.AsCString()); | ||
Jlalond marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // If we failed in a memory read, we would normally want to skip | ||
| // this entire region, if we had already written to the minidump | ||
| // file, we can't easily rewind that state. | ||
| // | ||
| // So if we do encounter an error while reading, we just return | ||
| // immediately, any prior bytes read will still be included but | ||
| // any bytes partially read before the error are ignored. | ||
| return lldb_private::IterationAction::Stop; | ||
| } | ||
|
|
||
| // 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. | ||
| // If we get an error writing to disk we can't easily guarauntee that we | ||
| // won't corrupt the minidump. | ||
| addDataError = AddData(buf, bytes_read_for_chunk); | ||
| if (addDataError.Fail()) | ||
| return lldb_private::IterationAction::Stop; | ||
|
|
||
| if (bytes_read_for_chunk != bytes_to_read) { | ||
|
||
| LLDB_LOGF(log, | ||
| "Memory region at: %" PRIx64 " partiall read %" PRIx64 | ||
| " bytes out of %" PRIx64 " bytes.", | ||
| current_addr, bytes_read_for_chunk, | ||
| bytes_to_read - bytes_read_for_chunk); | ||
Jlalond marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // If we've read some bytes, we stop trying to read more and return | ||
| // this best effort attempt | ||
| return lldb_private::IterationAction::Stop; | ||
| } | ||
|
|
||
| // No problems, keep going! | ||
| return lldb_private::IterationAction::Continue; | ||
| }; | ||
|
|
||
| const lldb::addr_t addr = range.range.start(); | ||
| const lldb::addr_t size = range.range.size(); | ||
| bytes_read = m_process_sp->ReadMemoryInChunks( | ||
| addr, data_buffer.GetBytes(), data_buffer.GetByteSize(), size, callback); | ||
| return addDataError; | ||
| } | ||
|
|
||
| static uint64_t | ||
| GetLargestRangeSize(const std::vector<CoreFileMemoryRange> &ranges) { | ||
| uint64_t max_size = 0; | ||
|
|
@@ -987,8 +1045,8 @@ MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges, | |
|
|
||
| Log *log = GetLog(LLDBLog::Object); | ||
| size_t region_index = 0; | ||
| auto data_up = | ||
| std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0); | ||
| lldb_private::DataBufferHeap data_buffer( | ||
| std::min(GetLargestRangeSize(ranges), MAX_WRITE_CHUNK_SIZE), 0); | ||
| for (const auto &core_range : ranges) { | ||
| // Take the offset before we write. | ||
| const offset_t offset_for_data = GetCurrentDataEndOffset(); | ||
|
|
@@ -1003,18 +1061,15 @@ MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges, | |
| ++region_index; | ||
|
|
||
| progress.Increment(1, "Adding Memory Range " + core_range.Dump()); | ||
| const size_t bytes_read = | ||
| m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); | ||
| if (error.Fail() || bytes_read == 0) { | ||
| LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s", | ||
| bytes_read, error.AsCString()); | ||
| // Just skip sections with errors or zero bytes in 32b mode | ||
| uint64_t bytes_read = 0; | ||
| error = ReadWriteMemoryInChunks(data_buffer, core_range, bytes_read); | ||
| if (error.Fail()) | ||
| return error; | ||
|
|
||
| // If we completely failed to read this range | ||
| // we can just omit any of the book keeping. | ||
| if (bytes_read == 0) | ||
| continue; | ||
| } else if (bytes_read != size) { | ||
| LLDB_LOGF( | ||
| log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes", | ||
| addr, size); | ||
| } | ||
|
|
||
| MemoryDescriptor descriptor; | ||
| descriptor.StartOfMemoryRange = | ||
|
|
@@ -1026,11 +1081,6 @@ MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges, | |
| descriptors.push_back(descriptor); | ||
| if (m_thread_by_range_end.count(end) > 0) | ||
| m_thread_by_range_end[end].Stack = descriptor; | ||
|
|
||
| // Add the data to the buffer, flush as needed. | ||
| error = AddData(data_up->GetBytes(), bytes_read); | ||
| if (error.Fail()) | ||
| return error; | ||
| } | ||
|
|
||
| // Add a directory that references this list | ||
|
|
@@ -1088,6 +1138,8 @@ MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges, | |
| list_header.BaseRVA = memory_ranges_base_rva; | ||
| m_data.AppendData(&list_header, sizeof(llvm::minidump::Memory64ListHeader)); | ||
|
|
||
| lldb_private::DataBufferHeap data_buffer( | ||
| std::min(GetLargestRangeSize(ranges), MAX_WRITE_CHUNK_SIZE), 0); | ||
| bool cleanup_required = false; | ||
| std::vector<MemoryDescriptor_64> descriptors; | ||
| // Enumerate the ranges and create the memory descriptors so we can append | ||
|
|
@@ -1106,8 +1158,6 @@ MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges, | |
|
|
||
| Log *log = GetLog(LLDBLog::Object); | ||
| size_t region_index = 0; | ||
| auto data_up = | ||
| std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0); | ||
| for (const auto &core_range : ranges) { | ||
| const addr_t addr = core_range.range.start(); | ||
| const addr_t size = core_range.range.size(); | ||
|
|
@@ -1120,27 +1170,19 @@ MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges, | |
| ++region_index; | ||
|
|
||
| progress.Increment(1, "Adding Memory Range " + core_range.Dump()); | ||
| const size_t bytes_read = | ||
| m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); | ||
| if (error.Fail()) { | ||
| LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s", | ||
| bytes_read, error.AsCString()); | ||
| error.Clear(); | ||
| uint64_t bytes_read = 0; | ||
| error = ReadWriteMemoryInChunks(data_buffer, core_range, bytes_read); | ||
| if (error.Fail()) | ||
| return error; | ||
|
|
||
| if (bytes_read == 0) { | ||
| cleanup_required = true; | ||
| descriptors[region_index].DataSize = 0; | ||
| } | ||
| if (bytes_read != size) { | ||
| LLDB_LOGF( | ||
| log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes", | ||
| addr, size); | ||
| cleanup_required = true; | ||
| descriptors[region_index].DataSize = bytes_read; | ||
| } | ||
|
|
||
| // Add the data to the buffer, flush as needed. | ||
| error = AddData(data_up->GetBytes(), bytes_read); | ||
| if (error.Fail()) | ||
| return error; | ||
| } | ||
|
|
||
| // Early return if there is no cleanup needed. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.