-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[LLDB] Process minidump is in memory check command #149401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-lldb Author: Jacob Lalonde (Jlalond) ChangesThis is a follow up to #149206, where I tried to make it more apparent for the user when a memory range is absent or if an error occurred on reading. I was happy with the improvements, but because of To make it easier for both me and users to debug. I've created a new command that will return if an address is present in the Minidump. If it isn't we try to get the closets prior and subsequent (I phrased as following to align with RangeMap.h) regions to make it easier to say what memory is available and which is not. This does open up the discussion on is the current memory region behavior a good user experience but I'll deem this outside the scope of this patch. Full diff: https://github.com/llvm/llvm-project/pull/149401.diff 7 Files Affected:
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..da17aee1fde1d 100644
--- a/lldb/include/lldb/Utility/RangeMap.h
+++ b/lldb/include/lldb/Utility/RangeMap.h
@@ -648,6 +648,26 @@ 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 pos = llvm::lower_bound(
+ m_entries, addr, [](const Entry &lhs, B rhs_base) -> bool {
+ return lhs.GetRangeEnd() <= rhs_base;
+ });
+
+ if (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<minidump::Range>
+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<minidump::Range>
+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<llvm::ArrayRef<uint8_t>> 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<Range> GetClosestPriorRegion(lldb::addr_t addr);
+ std::optional<Range> 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<MemoryRegionInfos, bool> 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<lldb_private::MemoryRegionInfo>
+ProcessMinidump::TryGetMemoryRegionInCore(
+ lldb::addr_t addr,
+ std::optional<lldb_private::MemoryRegionInfo> &closest_prior_region,
+ std::optional<lldb_private::MemoryRegionInfo> &closest_following_region) {
+ // Ensure memory regions are built!
+ BuildMemoryRegions();
+
+ // First try to find the region that contains the address (if any
+ std::optional<minidump::Range> 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<minidump::Range> 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<minidump::Range> 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<ProcessMinidump *>(
+ m_interpreter.GetExecutionContext().GetProcessPtr());
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ std::optional<lldb_private::MemoryRegionInfo> closest_prior_region;
+ std::optional<lldb_private::MemoryRegionInfo> closest_following_region;
+ std::optional<lldb_private::MemoryRegionInfo> 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 <subcommand> [<subcommand-options>]") {
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<lldb_private::MemoryRegionInfo> TryGetMemoryRegionInCore(
+ lldb::addr_t addr,
+ std::optional<lldb_private::MemoryRegionInfo> &closest_prior_region,
+ std::optional<lldb_private::MemoryRegionInfo> &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/<pid>/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 : ''
+...
|
@@ -141,6 +142,35 @@ class MemoryRegionInfo { | |||
m_dirty_pages = std::move(pagelist); | |||
} | |||
|
|||
std::string Dump() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @JDevlieghere is there a better area to print MemoryRegionInfo? I must be reinventing the wheel but there isn't an existing method on MemoryRegion?
class CommandObjectProcessMinidumpCheckMemory : public CommandObjectParsed { | ||
public: | ||
CommandObjectProcessMinidumpCheckMemory(CommandInterpreter &interpreter) | ||
: CommandObjectParsed(interpreter, "process plugin check-memory", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bulbazord Hey Alex, do you have any idea on a better name than check-memory
? Thanks!
This is a follow up to #149206, where I tried to make it more apparent for the user when a memory range is absent or if an error occurred on reading. I was happy with the improvements, but because of
/proc/pid/maps
being used to identify the memory regions it was still frustrating that you'd see a memory range asrw-
and then not be able to read it!To make it easier for both me and users to debug. I've created a new command that will return if an address is present in the Minidump. If it isn't we try to get the closets prior and subsequent (I phrased as following to align with RangeMap.h) regions to make it easier to say what memory is available and which is not. This does open up the discussion on is the current memory region behavior a good user experience but I'll deem this outside the scope of this patch.