diff --git a/lldb/include/lldb/Target/MemoryRegionInfo.h b/lldb/include/lldb/Target/MemoryRegionInfo.h index dc37a7dbeda52..2957709692307 100644 --- a/lldb/include/lldb/Target/MemoryRegionInfo.h +++ b/lldb/include/lldb/Target/MemoryRegionInfo.h @@ -15,6 +15,7 @@ #include "lldb/Utility/ConstString.h" #include "lldb/Utility/RangeMap.h" +#include "lldb/Utility/StreamString.h" #include "llvm/Support/FormatProviders.h" namespace lldb_private { @@ -141,6 +142,35 @@ class MemoryRegionInfo { m_dirty_pages = std::move(pagelist); } + std::string Dump() const { + lldb_private::StreamString stream; + stream << "["; + stream.PutHex64(GetRange().GetRangeBase()); + stream << "-"; + stream.PutHex64(GetRange().GetRangeEnd()); + stream << ") "; + if (m_read == eYes) + stream << "r"; + else if (m_read == eNo) + stream << "-"; + else + stream << "?"; + if (m_write == eYes) + stream << "w"; + else if (m_write == eNo) + stream << "-"; + else + stream << "?"; + if (m_execute == eYes) + stream << "x"; + else if (m_execute == eNo) + stream << "-"; + else + stream << "?"; + + return stream.GetString().str(); + } + protected: RangeType m_range; OptionalBool m_read = eDontKnow; diff --git a/lldb/include/lldb/Utility/RangeMap.h b/lldb/include/lldb/Utility/RangeMap.h index e701ae1ba96c8..81be259b5366d 100644 --- a/lldb/include/lldb/Utility/RangeMap.h +++ b/lldb/include/lldb/Utility/RangeMap.h @@ -648,6 +648,27 @@ class RangeDataVector { return nullptr; } + const Entry *FindEntryThatContainsOrPrior(B addr) const { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert(IsSorted()); +#endif + if (!m_entries.empty()) { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = llvm::lower_bound( + m_entries, addr, [](const Entry &lhs, B rhs_base) -> bool { + return lhs.GetRangeEnd() <= rhs_base; + }); + + if (pos != end && pos->Contains(addr)) + return &(*pos); + + if (pos != begin) + return &(*std::prev(pos)); + } + return nullptr; + } + uint32_t FindEntryIndexThatContainsOrFollows(B addr) const { #ifdef ASSERT_RANGEMAP_ARE_SORTED assert(IsSorted()); diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp index 58ebb7be11994..16eeb99d91ad8 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.cpp @@ -711,3 +711,29 @@ MinidumpParser::GetMemoryRegionInfo(const MemoryRegionInfos ®ions, region.SetMapped(MemoryRegionInfo::eNo); return region; } + +std::optional +MinidumpParser::GetClosestPriorRegion(lldb::addr_t load_addr) { + if (m_memory_ranges.IsEmpty()) + PopulateMemoryRanges(); + + const MemoryRangeVector::Entry *entry = + m_memory_ranges.FindEntryThatContainsOrPrior(load_addr); + if (!entry) + return std::nullopt; + + return entry->data; +} + +std::optional +MinidumpParser::GetClosestFollowingRegion(lldb::addr_t load_addr) { + if (m_memory_ranges.IsEmpty()) + PopulateMemoryRanges(); + + const MemoryRangeVector::Entry *entry = + m_memory_ranges.FindEntryThatContainsOrFollows(load_addr); + if (!entry) + return std::nullopt; + + return entry->data; +} diff --git a/lldb/source/Plugins/Process/minidump/MinidumpParser.h b/lldb/source/Plugins/Process/minidump/MinidumpParser.h index 3b7d33daca717..ac8fc682925ee 100644 --- a/lldb/source/Plugins/Process/minidump/MinidumpParser.h +++ b/lldb/source/Plugins/Process/minidump/MinidumpParser.h @@ -107,6 +107,12 @@ class MinidumpParser { llvm::Expected> GetMemory(lldb::addr_t addr, size_t size); + // Pair of functions to get relative memory regions from the minidump file. + // These aren't for evaluating the data, but checking the ranges stored in the + // minidump and their permissions. + std::optional GetClosestPriorRegion(lldb::addr_t addr); + std::optional GetClosestFollowingRegion(lldb::addr_t addr); + /// Returns a list of memory regions and a flag indicating whether the list is /// complete (includes all regions mapped into the process memory). std::pair BuildMemoryRegions(); diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp index 17a421a722743..79ccafe7a561c 100644 --- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp @@ -601,9 +601,41 @@ bool ProcessMinidump::GetProcessInfo(ProcessInstanceInfo &info) { return true; } +std::optional +ProcessMinidump::TryGetMemoryRegionInCore( + lldb::addr_t addr, + std::optional &closest_prior_region, + std::optional &closest_following_region) { + // Ensure memory regions are built! + BuildMemoryRegions(); + + // First try to find the region that contains the address (if any + std::optional addr_region_maybe = + m_minidump_parser->FindMemoryRange(addr); + if (addr_region_maybe) { + MemoryRegionInfo region = MinidumpParser::GetMemoryRegionInfo( + *m_memory_regions, addr_region_maybe->start); + return region; + } + + // If we didn't find a region, try to find the closest prior and following + std::optional closest_prior_range_maybe = + m_minidump_parser->GetClosestPriorRegion(addr); + if (closest_prior_range_maybe) + closest_prior_region = MinidumpParser::GetMemoryRegionInfo( + *m_memory_regions, closest_prior_range_maybe->start); + + std::optional closest_following_range_maybe = + m_minidump_parser->GetClosestFollowingRegion(addr); + if (closest_following_range_maybe) + closest_following_region = MinidumpParser::GetMemoryRegionInfo( + *m_memory_regions, closest_following_range_maybe->start); + + return std::nullopt; +} + // For minidumps there's no runtime generated code so we don't need JITLoader(s) // Avoiding them will also speed up minidump loading since JITLoaders normally -// try to set up symbolic breakpoints, which in turn may force loading more // debug information than needed. JITLoaderList &ProcessMinidump::GetJITLoaders() { if (!m_jit_loaders_up) { @@ -961,6 +993,73 @@ class CommandObjectProcessMinidumpDump : public CommandObjectParsed { } }; +class CommandObjectProcessMinidumpCheckMemory : public CommandObjectParsed { +public: + CommandObjectProcessMinidumpCheckMemory(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "process plugin check-memory", + "Check if a memory address is stored in the " + "Minidump, or the closest ranges.", + nullptr) { + CommandArgumentEntry arg1; + CommandArgumentData addr_arg; + addr_arg.arg_type = eArgTypeAddressOrExpression; + + arg1.push_back(addr_arg); + m_arguments.push_back(arg1); + } + + void DoExecute(Args &command, CommandReturnObject &result) override { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) { + result.AppendErrorWithFormat("'%s' Requires a memory address", + m_cmd_name.c_str()); + return; + } + + Status error; + lldb::addr_t address = OptionArgParser::ToAddress( + &m_exe_ctx, command[0].ref(), LLDB_INVALID_ADDRESS, &error); + + if (error.Fail() || address == LLDB_INVALID_ADDRESS) { + result.AppendErrorWithFormat("'%s' Is an invalid address.", + command[0].c_str()); + return; + } + + ProcessMinidump *process = static_cast( + m_interpreter.GetExecutionContext().GetProcessPtr()); + + result.SetStatus(eReturnStatusSuccessFinishResult); + std::optional closest_prior_region; + std::optional closest_following_region; + std::optional memory_region_maybe = + process->TryGetMemoryRegionInCore(address, closest_prior_region, + closest_following_region); + + if (memory_region_maybe) { + result.AppendMessageWithFormat( + "Address found in Minidump. Address located in range: %s", + memory_region_maybe->Dump().c_str()); + return; + } + + lldb_private::StreamString result_stream; + result_stream << "Address not found in Minidump" << "\n"; + if (closest_prior_region) + result_stream << "Closest prior range: " + << closest_prior_region->Dump().c_str() << "\n"; + else + result_stream << "No prior range found" << "\n"; + if (closest_following_region) + result_stream << "Closest following range: " + << closest_following_region->Dump().c_str() << "\n"; + else + result_stream << "No following range found" << "\n"; + + result.AppendMessage(result_stream.GetString()); + } +}; + class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword { public: CommandObjectMultiwordProcessMinidump(CommandInterpreter &interpreter) @@ -969,6 +1068,9 @@ class CommandObjectMultiwordProcessMinidump : public CommandObjectMultiword { "process plugin []") { LoadSubCommand("dump", CommandObjectSP(new CommandObjectProcessMinidumpDump(interpreter))); + LoadSubCommand("check-memory", + CommandObjectSP(new CommandObjectProcessMinidumpCheckMemory( + interpreter))); } ~CommandObjectMultiwordProcessMinidump() override = default; diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h index ad8d0ed7a4832..f5a3a87260521 100644 --- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h +++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h @@ -74,6 +74,11 @@ class ProcessMinidump : public PostMortemProcess { size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error) override; + std::optional TryGetMemoryRegionInCore( + lldb::addr_t addr, + std::optional &closest_prior_region, + std::optional &closest_following_region); + ArchSpec GetArchitecture(); Status diff --git a/lldb/test/Shell/Minidump/check-memory.yaml b/lldb/test/Shell/Minidump/check-memory.yaml new file mode 100644 index 0000000000000..cce315fb687fe --- /dev/null +++ b/lldb/test/Shell/Minidump/check-memory.yaml @@ -0,0 +1,52 @@ +# Check that looking up a memory region not present in the Minidump fails +# even if it's in the /proc//maps file. + +# RUN: yaml2obj %s -o %t +# RUN: %lldb -c %t -o "process plugin check-memory 0x1000" \ +# RUN: -o "process plugin check-memory 0x800" \ +# RUN: -o "process plugin check-memory 0x1500" \ +# RUN: -o "process plugin check-memory 0x2400" | FileCheck %s + +# CHECK-LABEL: (lldb) process plugin check-memory 0x1000 +# CHECK-NEXT: Address found in Minidump. Address located in range: [0000000000001000-0000000000001100) r?? +# CHECK-LABEL: (lldb) process plugin check-memory 0x800 +# CHECK-NEXT: Address not found in Minidump +# CHECK-NEXT: No prior range found +# CHECK-NEXT: Closest following range: [0000000000001000-0000000000001100) r?? +# CHECK-LABEL: (lldb) process plugin check-memory 0x1500 +# CHECK-NEXT: Address not found in Minidump +# CHECK-NEXT: Closest prior range: [0000000000001000-0000000000001100) r?? +# CHECK-NEXT: Closest following range: [0000000000002000-0000000000002200) r?? +# CHECK-LABEL: (lldb) process plugin check-memory 0x2400 +# CHECK-NEXT: Address not found in Minidump +# CHECK-NEXT: Closest prior range: [0000000000002000-0000000000002200) r?? +# CHECK-NEXT: No following range found + +--- !minidump +Streams: + - Type: SystemInfo + Processor Arch: AMD64 + Processor Level: 6 + Processor Revision: 15876 + Number of Processors: 40 + Platform ID: Linux + CSD Version: 'Linux 3.13.0-91-generic #138-Ubuntu SMP Fri Jun 24 17:00:34 UTC 2016 x86_64' + CPU: + Vendor ID: GenuineIntel + Version Info: 0x00000000 + Feature Info: 0x00000000 + - Type: LinuxProcStatus + Text: | + Name: test-yaml + Umask: 0002 + State: t (tracing stop) + Pid: 8567 + - Type: Memory64List + Memory Ranges: + - Start of Memory Range: 0x1000 + Data Size: 0x100 + Content : '' + - Start of Memory Range: 0x2000 + Data Size: 0x200 + Content : '' +...