From 97be487728c242feb197410872ca191fc5f5de0f Mon Sep 17 00:00:00 2001 From: Howard Hellyer Date: Mon, 18 Jul 2016 08:25:59 +0000 Subject: [PATCH] Implement GetMemoryRegions() for Windows Minidumps and live processes. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Summary: This patch fills in the implementation of GetMemoryRegions() on the Windows live process and minidump implementations of lldb_private::Process (ProcessWindowsLive::GetMemoryRegionInfo and ProcessWinMiniDump::Impl::GetMemoryRegionInfo.) The GetMemoryRegions API was added under: http://reviews.llvm.org/D20565 The existing Windows implementations didn’t fill in the start and end addresses within MemoryRegionInfo. This patch fixes that and adds support for the new mapped flag on MemoryRegionInfo that says whether a memory range is mapped into the process address space or not. The behaviour of both live and core implementations should match the behaviour documented on Process::GetMemoryRegionInfo (in Process.h) which in turn should match the behaviour of the qMemoryRegionInfo query documented in lldb-gdb-remote.txt. Reviewers: clayborg, amccarth Subscribers: amccarth, lldb-commits Differential Revision: https://reviews.llvm.org/D22352 llvm-svn: 275778 --- .../Process/Windows/Live/ProcessWindowsLive.cpp | 71 ++++++++++++++++++---- .../Windows/MiniDump/ProcessWinMiniDump.cpp | 23 ++++++- 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp b/lldb/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp index fe6ff11..300e0ca 100644 --- a/lldb/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp +++ b/lldb/source/Plugins/Process/Windows/Live/ProcessWindowsLive.cpp @@ -748,6 +748,7 @@ ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo & { Error error; llvm::sys::ScopedLock lock(m_mutex); + info.Clear(); if (!m_session_data) { @@ -755,7 +756,6 @@ ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo & WINERR_IFALL(WINDOWS_LOG_MEMORY, error.AsCString()); return error; } - HostProcess process = m_session_data->m_debugger->GetProcess(); lldb::process_t handle = process.GetNativeProcess().GetSystemHandle(); if (handle == nullptr || handle == LLDB_INVALID_PROCESS) @@ -772,22 +772,67 @@ ProcessWindowsLive::GetMemoryRegionInfo(lldb::addr_t vm_addr, MemoryRegionInfo & SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info)); if (result == 0) { - error.SetError(::GetLastError(), eErrorTypeWin32); - WINERR_IFALL(WINDOWS_LOG_MEMORY, - "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", - error.GetError(), vm_addr); - return error; + if (::GetLastError() == ERROR_INVALID_PARAMETER) + { + // ERROR_INVALID_PARAMETER is returned if VirtualQueryEx is called with an address + // past the highest accessible address. We should return a range from the vm_addr + // to LLDB_INVALID_ADDRESS + info.GetRange().SetRangeBase(vm_addr); + info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); + info.SetReadable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + info.SetMapped(MemoryRegionInfo::eNo); + return error; + } + else + { + error.SetError(::GetLastError(), eErrorTypeWin32); + WINERR_IFALL(WINDOWS_LOG_MEMORY, + "VirtualQueryEx returned error %u while getting memory region info for address 0x%I64x", + error.GetError(), vm_addr); + return error; + } + } + + // Protect bits are only valid for MEM_COMMIT regions. + if (mem_info.State == MEM_COMMIT) { + const bool readable = IsPageReadable(mem_info.Protect); + const bool executable = IsPageExecutable(mem_info.Protect); + const bool writable = IsPageWritable(mem_info.Protect); + info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + } + else + { + info.SetReadable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + } + + // AllocationBase is defined for MEM_COMMIT and MEM_RESERVE but not MEM_FREE. + if (mem_info.State != MEM_FREE) { + info.GetRange().SetRangeBase(reinterpret_cast(mem_info.AllocationBase)); + info.GetRange().SetRangeEnd(reinterpret_cast(mem_info.BaseAddress) + mem_info.RegionSize); + info.SetMapped(MemoryRegionInfo::eYes); + } + else + { + // In the unmapped case we need to return the distance to the next block of memory. + // VirtualQueryEx nearly does that except that it gives the distance from the start + // of the page containing vm_addr. + SYSTEM_INFO data; + GetSystemInfo(&data); + DWORD page_offset = vm_addr % data.dwPageSize; + info.GetRange().SetRangeBase(vm_addr); + info.GetRange().SetByteSize(mem_info.RegionSize - page_offset); + info.SetMapped(MemoryRegionInfo::eNo); } - const bool readable = IsPageReadable(mem_info.Protect); - const bool executable = IsPageExecutable(mem_info.Protect); - const bool writable = IsPageWritable(mem_info.Protect); - info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); - info.SetExecutable(executable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); - info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); error.SetError(::GetLastError(), eErrorTypeWin32); WINLOGV_IFALL(WINDOWS_LOG_MEMORY, "Memory region info for address 0x%I64u: readable=%s, executable=%s, writable=%s", - BOOL_STR(readable), BOOL_STR(executable), BOOL_STR(writable)); + BOOL_STR(info.GetReadable()), BOOL_STR(info.GetExecutable()), BOOL_STR(info.GetWritable())); return error; } diff --git a/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp b/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp index f09872f..0583966 100644 --- a/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp +++ b/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp @@ -277,6 +277,7 @@ ProcessWinMiniDump::Impl::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_priva { Error error; size_t size; + info.Clear(); const auto list = reinterpret_cast(FindDumpStream(MemoryInfoListStream, &size)); if (list == nullptr || size < sizeof(MINIDUMP_MEMORY_INFO_LIST)) { @@ -296,6 +297,8 @@ ProcessWinMiniDump::Impl::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_priva return error; } + const MINIDUMP_MEMORY_INFO *next_entry = nullptr; + for (int i = 0; i < list->NumberOfEntries; ++i) { const auto entry = reinterpret_cast(reinterpret_cast(list) + @@ -304,16 +307,34 @@ ProcessWinMiniDump::Impl::GetMemoryRegionInfo(lldb::addr_t load_addr, lldb_priva const auto tail = head + entry->RegionSize; if (head <= load_addr && load_addr < tail) { + info.GetRange().SetRangeBase((entry->State != MEM_FREE) ? head : load_addr); + info.GetRange().SetRangeEnd(tail); info.SetReadable(IsPageReadable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); info.SetWritable(IsPageWritable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); info.SetExecutable(IsPageExecutable(entry->Protect) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); + info.SetMapped((entry->State != MEM_FREE) ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo); return error; } + else if (head > load_addr && (next_entry == nullptr || head < next_entry->BaseAddress) ) + { + // In case there is no region containing load_addr keep track of the nearest region + // after load_addr so we can return the distance to it. + next_entry = entry; + } } + + // No containing region found. Create an unmapped region that extends to the next region + // or LLDB_INVALID_ADDRESS + info.GetRange().SetRangeBase(load_addr); + info.GetRange().SetRangeEnd((next_entry != nullptr)?next_entry->BaseAddress:LLDB_INVALID_ADDRESS); + info.SetReadable(MemoryRegionInfo::eNo); + info.SetWritable(MemoryRegionInfo::eNo); + info.SetExecutable(MemoryRegionInfo::eNo); + info.SetMapped(MemoryRegionInfo::eNo); + // Note that the memory info list doesn't seem to contain ranges in kernel space, // so if you're walking a stack that has kernel frames, the stack may appear // truncated. - error.SetErrorString("address is not in a known range"); return error; } -- 2.7.4