[lldb] [llvm] [LLDB][Minidump] Add 64b support to LLDB's minidump file builder. (PR #95312)

Greg Clayton via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 17 17:33:10 PDT 2024


================
@@ -821,47 +908,285 @@ Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const {
   Status error;
   size_t bytes_written;
 
-  bytes_written = header_size;
-  error = core_file->Write(&header, bytes_written);
-  if (error.Fail() || bytes_written != header_size) {
-    if (bytes_written != header_size)
+  m_core_file->SeekFromStart(0);
+  bytes_written = HEADER_SIZE;
+  error = m_core_file->Write(&header, bytes_written);
+  if (error.Fail() || bytes_written != HEADER_SIZE) {
+    if (bytes_written != HEADER_SIZE)
       error.SetErrorStringWithFormat(
-          "unable to write the header (written %zd/%zd)", bytes_written,
-          header_size);
+          "Unable to write the minidump header (written %zd/%zd)",
+          bytes_written, HEADER_SIZE);
     return error;
   }
+  return error;
+}
 
-  // write data
-  bytes_written = m_data.GetByteSize();
-  error = core_file->Write(m_data.GetBytes(), bytes_written);
-  if (error.Fail() || bytes_written != m_data.GetByteSize()) {
-    if (bytes_written != m_data.GetByteSize())
-      error.SetErrorStringWithFormat(
-          "unable to write the data (written %zd/%" PRIu64 ")", bytes_written,
-          m_data.GetByteSize());
-    return error;
-  }
+size_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {
+  return m_data.GetByteSize() + m_saved_data_size;
+}
 
-  // write directories
+Status MinidumpFileBuilder::DumpDirectories() const {
+  Status error;
+  size_t bytes_written;
+  m_core_file->SeekFromStart(HEADER_SIZE);
   for (const Directory &dir : m_directories) {
-    bytes_written = directory_size;
-    error = core_file->Write(&dir, bytes_written);
-    if (error.Fail() || bytes_written != directory_size) {
-      if (bytes_written != directory_size)
+    bytes_written = DIRECTORY_SIZE;
+    error = m_core_file->Write(&dir, bytes_written);
+    if (error.Fail() || bytes_written != DIRECTORY_SIZE) {
+      if (bytes_written != DIRECTORY_SIZE)
         error.SetErrorStringWithFormat(
             "unable to write the directory (written %zd/%zd)", bytes_written,
-            directory_size);
+            DIRECTORY_SIZE);
       return error;
     }
   }
 
   return error;
 }
 
-size_t MinidumpFileBuilder::GetDirectoriesNum() const {
-  return m_directories.size();
+static size_t GetLargestRange(const Process::CoreFileMemoryRanges &ranges) {
+  size_t max_size = 0;
+  for (const auto &core_range : ranges)
+    max_size = std::max(max_size, core_range.range.size());
+  return max_size;
 }
 
-size_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {
-  return sizeof(llvm::minidump::Header) + m_data.GetByteSize();
+Status
+MinidumpFileBuilder::AddMemoryList_32(Process::CoreFileMemoryRanges &ranges) {
+  std::vector<MemoryDescriptor> descriptors;
+  Status error;
+  if (ranges.size() == 0)
+    return error;
+
+  Log *log = GetLog(LLDBLog::Object);
+  size_t region_index = 0;
+  auto data_up = std::make_unique<DataBufferHeap>(GetLargestRange(ranges), 0);
+  for (const auto &core_range : ranges) {
+    // Take the offset before we write.
+    const size_t offset_for_data = GetCurrentDataEndOffset();
+    const addr_t addr = core_range.range.start();
+    const addr_t size = core_range.range.size();
+    const addr_t end = core_range.range.end();
+
+    LLDB_LOGF(log,
+              "AddMemoryList %zu/%zu reading memory for region "
+              "(%zu bytes) [%zx, %zx)",
+              region_index, ranges.size(), size, addr, addr + size);
+    ++region_index;
+
+    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
+      continue;
+    } else if (bytes_read != size) {
+      LLDB_LOGF(log, "Memory region at: %zu failed to read %zu bytes", addr,
+                size);
+    }
+
+    MemoryDescriptor descriptor;
+    descriptor.StartOfMemoryRange =
+        static_cast<llvm::support::ulittle64_t>(addr);
+    descriptor.Memory.DataSize =
+        static_cast<llvm::support::ulittle32_t>(bytes_read);
+    descriptor.Memory.RVA =
+        static_cast<llvm::support::ulittle32_t>(offset_for_data);
+    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
+  // With a size of the number of ranges as a 32 bit num
+  // And then the size of all the ranges
+  error = AddDirectory(StreamType::MemoryList,
+                       sizeof(llvm::support::ulittle32_t) +
+                           descriptors.size() *
+                               sizeof(llvm::minidump::MemoryDescriptor));
+  if (error.Fail())
+    return error;
+
+  llvm::support::ulittle32_t memory_ranges_num =
+      static_cast<llvm::support::ulittle32_t>(descriptors.size());
+  m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t));
+  // For 32b we can get away with writing off the descriptors after the data.
+  // This means no cleanup loop needed.
+  m_data.AppendData(descriptors.data(),
+                    descriptors.size() * sizeof(MemoryDescriptor));
+
+  return error;
+}
+
+Status
+MinidumpFileBuilder::AddMemoryList_64(Process::CoreFileMemoryRanges &ranges) {
+  Status error;
+  if (ranges.empty())
+    return error;
+
+  error = AddDirectory(StreamType::Memory64List,
+                       (sizeof(llvm::support::ulittle64_t) * 2) +
+                           ranges.size() *
+                               sizeof(llvm::minidump::MemoryDescriptor_64));
+  if (error.Fail())
+    return error;
+
+  llvm::support::ulittle64_t memory_ranges_num =
+      static_cast<llvm::support::ulittle64_t>(ranges.size());
+  m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle64_t));
+  // Capture the starting offset for all the descriptors so we can clean them up
+  // if needed.
+  offset_t starting_offset =
+      GetCurrentDataEndOffset() + sizeof(llvm::support::ulittle64_t);
+  // The base_rva needs to start after the directories, which is right after
+  // this 8 byte variable.
+  offset_t base_rva =
+      starting_offset +
+      (ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64));
+  llvm::support::ulittle64_t memory_ranges_base_rva =
+      static_cast<llvm::support::ulittle64_t>(base_rva);
+  m_data.AppendData(&memory_ranges_base_rva,
+                    sizeof(llvm::support::ulittle64_t));
+
+  bool cleanup_required = false;
+  std::vector<MemoryDescriptor_64> descriptors;
+  // Enumerate the ranges and create the memory descriptors so we can append
+  // them first
+  for (const auto core_range : ranges) {
+    // Add the space required to store the memory descriptor
+    MemoryDescriptor_64 memory_desc;
+    memory_desc.StartOfMemoryRange =
+        static_cast<llvm::support::ulittle64_t>(core_range.range.start());
+    memory_desc.DataSize =
+        static_cast<llvm::support::ulittle64_t>(core_range.range.size());
+    descriptors.push_back(memory_desc);
+    // Now write this memory descriptor to the buffer.
+    m_data.AppendData(&memory_desc, sizeof(MemoryDescriptor_64));
+  }
+
+  Log *log = GetLog(LLDBLog::Object);
+  size_t region_index = 0;
+  auto data_up = std::make_unique<DataBufferHeap>(GetLargestRange(ranges), 0);
+  for (const auto &core_range : ranges) {
+    const addr_t addr = core_range.range.start();
+    const addr_t size = core_range.range.size();
+
+    LLDB_LOGF(log,
+              "AddMemoryList_64 %zu/%zu reading memory for region "
+              "(%zu bytes) "
+              "[%zx, %zx)",
+              region_index, ranges.size(), size, addr, addr + size);
+    ++region_index;
+
+    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();
+      cleanup_required = true;
+      descriptors[region_index].DataSize = 0;
+    }
+    if (bytes_read != size) {
+      LLDB_LOGF(log, "Memory region at: %zu failed to read %zu 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.
+  if (!cleanup_required) {
+    return error;
+  } else {
+    // Flush to disk we can make the fixes in place.
+    FlushBufferToDisk();
+    // Fixup the descriptors that were not read correctly.
+    m_core_file->SeekFromStart(starting_offset);
+    size_t bytes_written = sizeof(MemoryDescriptor_64) * descriptors.size();
+    error = m_core_file->Write(descriptors.data(), bytes_written);
+    if (error.Fail() ||
+        bytes_written != sizeof(MemoryDescriptor_64) * descriptors.size()) {
+      error.SetErrorStringWithFormat(
+          "unable to write the memory descriptors (written %zd/%zd)",
+          bytes_written, sizeof(MemoryDescriptor_64) * descriptors.size());
+    }
+
+    return error;
+  }
+}
+
+Status MinidumpFileBuilder::AddData(const void *data, addr_t size) {
+  // This should also get chunked, because worst case we copy over a big
+  // object / memory range, say 5gb. In that case, we'd have to allocate 10gb
+  // 5 gb for the buffer we're copying from, and then 5gb for the buffer we're
+  // copying to. Which will be short lived and immedaitely go to disk, the goal
+  // here is to limit the number of bytes we need to host in memory at any given
+  // time.
+  m_data.AppendData(data, size);
+  if (m_data.GetByteSize() > MAX_WRITE_CHUNK_SIZE) {
+    return FlushBufferToDisk();
+  }
----------------
clayborg wrote:

remove `{}` per llvm coding guidelines

https://github.com/llvm/llvm-project/pull/95312


More information about the llvm-commits mailing list