Index: source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h =================================================================== --- source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h +++ source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.h @@ -70,6 +70,9 @@ bool IsAlive() override; + bool + WarnBeforeDetach () const override; + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; @@ -91,6 +94,19 @@ lldb_private::ThreadList &new_thread_list) override; private: + // Describes a range of memory captured in the mini dump. + struct Range { + lldb::addr_t start; // virtual address of the beginning of the range + size_t size; // size of the range in bytes + const uint8_t *ptr; // absolute pointer to the first byte of the range + }; + + // If the mini dump has a memory range that contains the desired address, it + // returns true with the details of the range in *range_out. Otherwise, it + // returns false. + bool + FindMemoryRange(lldb::addr_t addr, Range *range_out) const; + lldb_private::Error MapMiniDumpIntoMemory(const char *file); @@ -107,7 +123,7 @@ // checks. If there's a failure (e.g., if the requested stream doesn't exist), // the function returns nullptr and sets *size_out to 0. void * - FindDumpStream(unsigned stream_number, size_t *size_out); + FindDumpStream(unsigned stream_number, size_t *size_out) const; // Isolate the data to keep Windows-specific types out of this header. Can't // use the typical pimpl idiom because the implementation of this class also Index: source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp =================================================================== --- source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp +++ source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp @@ -227,19 +227,42 @@ return true; } +bool +ProcessWinMiniDump::WarnBeforeDetach () const +{ + // Since this is post-mortem debugging, there's no need to warn the user + // that quitting the debugger will terminate the process. + return false; +} + size_t ProcessWinMiniDump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) { // Don't allow the caching that lldb_private::Process::ReadMemory does - // since in core files we have it all cached our our core file anyway. + // since we have it all cached our our dump file anyway. return DoReadMemory(addr, buf, size, error); } size_t ProcessWinMiniDump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Error &error) { - // TODO - return 0; + // I don't have a sense of how frequently this is called or how many memory + // ranges a mini dump typically has, so I'm not sure if searching for the + // appropriate range linearly each time is stupid. Perhaps we should build + // an index for faster lookups. + Range range = {0}; + if (!FindMemoryRange(addr, &range)) + { + return 0; + } + + // There's at least some overlap between the beginning of the desired range + // (addr) and the current range. Figure out where the overlap begins and + // how much overlap there is, then copy it to the destination buffer. + const size_t offset = range.start - addr; + const size_t overlap = std::min(size, range.size - offset); + std::memcpy(buf, range.ptr + offset, overlap); + return overlap; } void @@ -307,6 +330,54 @@ } } +bool +ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const +{ + size_t stream_size = 0; + auto mem_list_stream = static_cast(FindDumpStream(MemoryListStream, &stream_size)); + if (mem_list_stream) + { + for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR &mem_desc = mem_list_stream->MemoryRanges[i]; + const MINIDUMP_LOCATION_DESCRIPTOR &loc_desc = mem_desc.Memory; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = loc_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast(m_data_up->m_base_addr) + loc_desc.Rva; + return true; + } + } + } + + // Some mini dumps have a Memory64ListStream that captures all the heap + // memory. We can't exactly use the same loop as above, because the mini + // dump uses slightly different data structures to describe those. + auto mem_list64_stream = static_cast(FindDumpStream(Memory64ListStream, &stream_size)); + if (mem_list64_stream) + { + size_t base_rva = mem_list64_stream->BaseRva; + for (ULONG32 i = 0; i < mem_list64_stream->NumberOfMemoryRanges; ++i) { + const MINIDUMP_MEMORY_DESCRIPTOR64 &mem_desc = mem_list64_stream->MemoryRanges[i]; + const lldb::addr_t range_start = mem_desc.StartOfMemoryRange; + const size_t range_size = mem_desc.DataSize; + if (range_start <= addr && addr < range_start + range_size) + { + range_out->start = range_start; + range_out->size = range_size; + range_out->ptr = reinterpret_cast(m_data_up->m_base_addr) + base_rva; + return true; + } + base_rva += range_size; + } + } + + return false; +} + + Error ProcessWinMiniDump::MapMiniDumpIntoMemory(const char *file) { @@ -397,7 +468,8 @@ } void * -ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) { +ProcessWinMiniDump::FindDumpStream(unsigned stream_number, size_t *size_out) const +{ void *stream = nullptr; *size_out = 0;