summaryrefslogtreecommitdiffstats
path: root/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Plugins/Process/minidump/MinidumpParser.cpp')
-rw-r--r--lldb/source/Plugins/Process/minidump/MinidumpParser.cpp200
1 files changed, 200 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
index 2ccea6839cb..abb0810c813 100644
--- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
+++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp
@@ -11,8 +11,11 @@
#include "MinidumpParser.h"
// Other libraries and framework includes
+#include "lldb/Target/MemoryRegionInfo.h"
+
// C includes
// C++ includes
+#include <map>
using namespace lldb_private;
using namespace minidump;
@@ -100,6 +103,14 @@ llvm::ArrayRef<MinidumpThread> MinidumpParser::GetThreads() {
return MinidumpThread::ParseThreadList(data);
}
+llvm::ArrayRef<uint8_t>
+MinidumpParser::GetThreadContext(const MinidumpThread &td) {
+ if (td.thread_context.rva + td.thread_context.data_size > GetData().size())
+ return llvm::None;
+
+ return GetData().slice(td.thread_context.rva, td.thread_context.data_size);
+}
+
const MinidumpSystemInfo *MinidumpParser::GetSystemInfo() {
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::SystemInfo);
@@ -216,6 +227,42 @@ llvm::ArrayRef<MinidumpModule> MinidumpParser::GetModuleList() {
return MinidumpModule::ParseModuleList(data);
}
+std::vector<const MinidumpModule *> MinidumpParser::GetFilteredModuleList() {
+ llvm::ArrayRef<MinidumpModule> modules = GetModuleList();
+ // mapping module_name to pair(load_address, pointer to module struct in
+ // memory)
+ llvm::StringMap<std::pair<uint64_t, const MinidumpModule *>> lowest_addr;
+
+ std::vector<const MinidumpModule *> filtered_modules;
+
+ llvm::Optional<std::string> name;
+ std::string module_name;
+
+ for (const auto &module : modules) {
+ name = GetMinidumpString(module.module_name_rva);
+
+ if (!name)
+ continue;
+
+ module_name = name.getValue();
+
+ auto iter = lowest_addr.end();
+ bool exists;
+ std::tie(iter, exists) = lowest_addr.try_emplace(
+ module_name, std::make_pair(module.base_of_image, &module));
+
+ if (exists && module.base_of_image < iter->second.first)
+ iter->second = std::make_pair(module.base_of_image, &module);
+ }
+
+ filtered_modules.reserve(lowest_addr.size());
+ for (const auto &module : lowest_addr) {
+ filtered_modules.push_back(module.second.second);
+ }
+
+ return filtered_modules;
+}
+
const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::Exception);
@@ -224,3 +271,156 @@ const MinidumpExceptionStream *MinidumpParser::GetExceptionStream() {
return MinidumpExceptionStream::Parse(data);
}
+
+llvm::Optional<minidump::Range>
+MinidumpParser::FindMemoryRange(lldb::addr_t addr) {
+ llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryList);
+ llvm::ArrayRef<uint8_t> data64 = GetStream(MinidumpStreamType::Memory64List);
+
+ if (data.empty() && data64.empty())
+ return llvm::None;
+
+ if (!data.empty()) {
+ llvm::ArrayRef<MinidumpMemoryDescriptor> memory_list =
+ MinidumpMemoryDescriptor::ParseMemoryList(data);
+
+ if (memory_list.empty())
+ return llvm::None;
+
+ for (const auto &memory_desc : memory_list) {
+ const MinidumpLocationDescriptor &loc_desc = memory_desc.memory;
+ const lldb::addr_t range_start = memory_desc.start_of_memory_range;
+ const size_t range_size = loc_desc.data_size;
+
+ if (loc_desc.rva + loc_desc.data_size > GetData().size())
+ return llvm::None;
+
+ if (range_start <= addr && addr < range_start + range_size) {
+ return minidump::Range(range_start,
+ GetData().slice(loc_desc.rva, range_size));
+ }
+ }
+ }
+
+ // Some Minidumps have a Memory64ListStream that captures all the heap
+ // memory (full-memory Minidumps). We can't exactly use the same loop as
+ // above, because the Minidump uses slightly different data structures to
+ // describe those
+
+ if (!data64.empty()) {
+ llvm::ArrayRef<MinidumpMemoryDescriptor64> memory64_list;
+ uint64_t base_rva;
+ std::tie(memory64_list, base_rva) =
+ MinidumpMemoryDescriptor64::ParseMemory64List(data64);
+
+ if (memory64_list.empty())
+ return llvm::None;
+
+ for (const auto &memory_desc64 : memory64_list) {
+ const lldb::addr_t range_start = memory_desc64.start_of_memory_range;
+ const size_t range_size = memory_desc64.data_size;
+
+ if (base_rva + range_size > GetData().size())
+ return llvm::None;
+
+ if (range_start <= addr && addr < range_start + range_size) {
+ return minidump::Range(range_start,
+ GetData().slice(base_rva, range_size));
+ }
+ base_rva += range_size;
+ }
+ }
+
+ return llvm::None;
+}
+
+llvm::ArrayRef<uint8_t> MinidumpParser::GetMemory(lldb::addr_t addr,
+ size_t size) {
+ // I don't have a sense of how frequently this is called or how many memory
+ // ranges a Minidump 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.
+ llvm::Optional<minidump::Range> range = FindMemoryRange(addr);
+ if (!range)
+ return {};
+
+ // 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.
+
+ const size_t offset = addr - range->start;
+
+ if (addr < range->start || offset >= range->range_ref.size())
+ return {};
+
+ const size_t overlap = std::min(size, range->range_ref.size() - offset);
+ return range->range_ref.slice(offset, overlap);
+}
+
+llvm::Optional<MemoryRegionInfo>
+MinidumpParser::GetMemoryRegionInfo(lldb::addr_t load_addr) {
+ MemoryRegionInfo info;
+ llvm::ArrayRef<uint8_t> data = GetStream(MinidumpStreamType::MemoryInfoList);
+ if (data.empty())
+ return llvm::None;
+
+ std::vector<const MinidumpMemoryInfo *> mem_info_list =
+ MinidumpMemoryInfo::ParseMemoryInfoList(data);
+ if (mem_info_list.empty())
+ return llvm::None;
+
+ const auto yes = MemoryRegionInfo::eYes;
+ const auto no = MemoryRegionInfo::eNo;
+
+ const MinidumpMemoryInfo *next_entry = nullptr;
+ for (const auto &entry : mem_info_list) {
+ const auto head = entry->base_address;
+ const auto tail = head + entry->region_size;
+
+ if (head <= load_addr && load_addr < tail) {
+ info.GetRange().SetRangeBase(
+ (entry->state != uint32_t(MinidumpMemoryInfoState::MemFree))
+ ? head
+ : load_addr);
+ info.GetRange().SetRangeEnd(tail);
+
+ const uint32_t PageNoAccess =
+ static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageNoAccess);
+ info.SetReadable((entry->protect & PageNoAccess) == 0 ? yes : no);
+
+ const uint32_t PageWritable =
+ static_cast<uint32_t>(MinidumpMemoryProtectionContants::PageWritable);
+ info.SetWritable((entry->protect & PageWritable) != 0 ? yes : no);
+
+ const uint32_t PageExecutable = static_cast<uint32_t>(
+ MinidumpMemoryProtectionContants::PageExecutable);
+ info.SetExecutable((entry->protect & PageExecutable) != 0 ? yes : no);
+
+ const uint32_t MemFree =
+ static_cast<uint32_t>(MinidumpMemoryInfoState::MemFree);
+ info.SetMapped((entry->state != MemFree) ? yes : no);
+
+ return info;
+ } else if (head > load_addr &&
+ (next_entry == nullptr || head < next_entry->base_address)) {
+ // 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->base_address
+ : LLDB_INVALID_ADDRESS);
+ info.SetReadable(no);
+ info.SetWritable(no);
+ info.SetExecutable(no);
+ info.SetMapped(no);
+
+ // 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.
+ return info;
+}
OpenPOWER on IntegriCloud