Skip to content

Commit 1cfd90c

Browse files
committed
Update MinidumpFileBuilder to read and write in chunks
1 parent fbf0276 commit 1cfd90c

File tree

2 files changed

+97
-40
lines changed

2 files changed

+97
-40
lines changed

lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -969,12 +969,82 @@ Status MinidumpFileBuilder::DumpDirectories() const {
969969
return error;
970970
}
971971

972-
static uint64_t
973-
GetLargestRangeSize(const std::vector<CoreFileMemoryRange> &ranges) {
974-
uint64_t max_size = 0;
975-
for (const auto &core_range : ranges)
976-
max_size = std::max(max_size, core_range.range.size());
977-
return max_size;
972+
Status MinidumpFileBuilder::ReadWriteMemoryInChunks(
973+
const lldb_private::CoreFileMemoryRange &range, uint64_t *bytes_read) {
974+
Log *log = GetLog(LLDBLog::Object);
975+
lldb::addr_t addr = range.range.start();
976+
lldb::addr_t size = range.range.size();
977+
// First we set the byte tally to 0, so if we do exit gracefully
978+
// the caller doesn't think the random garbage on the stack is a
979+
// success.
980+
if (bytes_read)
981+
*bytes_read = 0;
982+
983+
uint64_t bytes_remaining = size;
984+
uint64_t total_bytes_read = 0;
985+
auto data_up = std::make_unique<DataBufferHeap>(
986+
std::min(bytes_remaining, MAX_WRITE_CHUNK_SIZE), 0);
987+
Status error;
988+
while (bytes_remaining > 0) {
989+
// Get the next read chunk size as the minimum of the remaining bytes and
990+
// the write chunk max size.
991+
const size_t bytes_to_read =
992+
std::min(bytes_remaining, MAX_WRITE_CHUNK_SIZE);
993+
const size_t bytes_read_for_chunk =
994+
m_process_sp->ReadMemory(range.range.start() + total_bytes_read,
995+
data_up->GetBytes(), bytes_to_read, error);
996+
if (error.Fail() || bytes_read_for_chunk == 0) {
997+
LLDB_LOGF(log,
998+
"Failed to read memory region at: %" PRIx64
999+
". Bytes read: %zu, error: %s",
1000+
addr, bytes_read_for_chunk, error.AsCString());
1001+
// If we've only read one byte we can just give up and return
1002+
if (total_bytes_read == 0)
1003+
return Status();
1004+
1005+
// If we've read some bytes, we stop trying to read more and return
1006+
// this best effort attempt
1007+
bytes_remaining = 0;
1008+
} else if (bytes_read_for_chunk != bytes_to_read) {
1009+
LLDB_LOGF(
1010+
log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",
1011+
addr, size);
1012+
1013+
// If we've read some bytes, we stop trying to read more and return
1014+
// this best effort attempt
1015+
bytes_remaining = 0;
1016+
}
1017+
1018+
// Write to the minidump file with the chunk potentially flushing to disk.
1019+
// this is the only place we want to return a true error, so that we can
1020+
// fail. If we get an error writing to disk we can't easily gaurauntee
1021+
// that we won't corrupt the minidump.
1022+
error = AddData(data_up->GetBytes(), bytes_read_for_chunk);
1023+
if (error.Fail())
1024+
return error;
1025+
1026+
// This check is so we don't overflow when the error code above sets the
1027+
// bytes to read to 0 (the graceful exit condition).
1028+
if (bytes_remaining > 0)
1029+
bytes_remaining -= bytes_read_for_chunk;
1030+
1031+
total_bytes_read += bytes_read_for_chunk;
1032+
// If the caller wants a tally back of the bytes_read, update it as we
1033+
// write. We do this in the loop so if we encounter an error we can
1034+
// report the accurate total.
1035+
if (bytes_read)
1036+
*bytes_read += bytes_read_for_chunk;
1037+
1038+
// We clear the heap per loop, without checking if we
1039+
// read the expected bytes this is so we don't allocate
1040+
// more than the MAX_WRITE_CHUNK_SIZE. But we do check if
1041+
// this is even worth clearing before we return and
1042+
// destruct the heap.
1043+
if (bytes_remaining > 0)
1044+
data_up->Clear();
1045+
}
1046+
1047+
return error;
9781048
}
9791049

9801050
Status
@@ -987,8 +1057,6 @@ MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges,
9871057

9881058
Log *log = GetLog(LLDBLog::Object);
9891059
size_t region_index = 0;
990-
auto data_up =
991-
std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);
9921060
for (const auto &core_range : ranges) {
9931061
// Take the offset before we write.
9941062
const offset_t offset_for_data = GetCurrentDataEndOffset();
@@ -1003,18 +1071,15 @@ MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges,
10031071
++region_index;
10041072

10051073
progress.Increment(1, "Adding Memory Range " + core_range.Dump());
1006-
const size_t bytes_read =
1007-
m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
1008-
if (error.Fail() || bytes_read == 0) {
1009-
LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",
1010-
bytes_read, error.AsCString());
1011-
// Just skip sections with errors or zero bytes in 32b mode
1074+
uint64_t bytes_read;
1075+
error = ReadWriteMemoryInChunks(core_range, &bytes_read);
1076+
if (error.Fail())
1077+
return error;
1078+
1079+
// If we completely failed to read this range
1080+
// we can just omit any of the book keeping.
1081+
if (bytes_read == 0)
10121082
continue;
1013-
} else if (bytes_read != size) {
1014-
LLDB_LOGF(
1015-
log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",
1016-
addr, size);
1017-
}
10181083

10191084
MemoryDescriptor descriptor;
10201085
descriptor.StartOfMemoryRange =
@@ -1026,11 +1091,6 @@ MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges,
10261091
descriptors.push_back(descriptor);
10271092
if (m_thread_by_range_end.count(end) > 0)
10281093
m_thread_by_range_end[end].Stack = descriptor;
1029-
1030-
// Add the data to the buffer, flush as needed.
1031-
error = AddData(data_up->GetBytes(), bytes_read);
1032-
if (error.Fail())
1033-
return error;
10341094
}
10351095

10361096
// Add a directory that references this list
@@ -1106,8 +1166,6 @@ MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges,
11061166

11071167
Log *log = GetLog(LLDBLog::Object);
11081168
size_t region_index = 0;
1109-
auto data_up =
1110-
std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);
11111169
for (const auto &core_range : ranges) {
11121170
const addr_t addr = core_range.range.start();
11131171
const addr_t size = core_range.range.size();
@@ -1120,27 +1178,19 @@ MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges,
11201178
++region_index;
11211179

11221180
progress.Increment(1, "Adding Memory Range " + core_range.Dump());
1123-
const size_t bytes_read =
1124-
m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
1125-
if (error.Fail()) {
1126-
LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",
1127-
bytes_read, error.AsCString());
1128-
error.Clear();
1181+
uint64_t bytes_read;
1182+
error = ReadWriteMemoryInChunks(core_range, &bytes_read);
1183+
if (error.Fail())
1184+
return error;
1185+
1186+
if (bytes_read == 0) {
11291187
cleanup_required = true;
11301188
descriptors[region_index].DataSize = 0;
11311189
}
11321190
if (bytes_read != size) {
1133-
LLDB_LOGF(
1134-
log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",
1135-
addr, size);
11361191
cleanup_required = true;
11371192
descriptors[region_index].DataSize = bytes_read;
11381193
}
1139-
1140-
// Add the data to the buffer, flush as needed.
1141-
error = AddData(data_up->GetBytes(), bytes_read);
1142-
if (error.Fail())
1143-
return error;
11441194
}
11451195

11461196
// Early return if there is no cleanup needed.

lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ class MinidumpFileBuilder {
142142
lldb_private::Status AddDirectory(llvm::minidump::StreamType type,
143143
uint64_t stream_size);
144144
lldb::offset_t GetCurrentDataEndOffset() const;
145+
146+
// Read a memory region from the process and write it to the file
147+
// in fixed size chunks.
148+
lldb_private::Status
149+
ReadWriteMemoryInChunks(const lldb_private::CoreFileMemoryRange &range,
150+
uint64_t *bytes_read);
151+
145152
// Stores directories to fill in later
146153
std::vector<llvm::minidump::Directory> m_directories;
147154
// When we write off the threads for the first time, we need to clean them up

0 commit comments

Comments
 (0)