DavidSpickett created this revision. Herald added a subscriber: dang. DavidSpickett requested review of this revision. Herald added a project: LLDB. Herald added a subscriber: lldb-commits.
This adds an option to the memory region command to print all regions at once. Like you can do by starting at address 0 and repeating the command manually. memory region [-a] [<address-expression>] (lldb) memory region --all [0x0000000000000000-0x0000000000400000) --- [0x0000000000400000-0x0000000000401000) r-x <...>/a.out PT_LOAD[0] <...> [0x0000fffffffdf000-0x0001000000000000) rw- [stack] [0x0001000000000000-0xffffffffffffffff) --- The output matches exactly what you'd get from repeating the command. Including that it shows unmapped areas between the mapped regions. (this is why Process GetMemoryRegions is not used, that skips unmapped areas) Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D111791 Files: lldb/source/Commands/CommandObjectMemory.cpp lldb/source/Commands/Options.td lldb/test/API/functionalities/memory-region/TestMemoryRegion.py
Index: lldb/test/API/functionalities/memory-region/TestMemoryRegion.py =================================================================== --- lldb/test/API/functionalities/memory-region/TestMemoryRegion.py +++ lldb/test/API/functionalities/memory-region/TestMemoryRegion.py @@ -41,24 +41,40 @@ self.assertFalse(result.Succeeded()) self.assertRegexpMatches(result.GetError(), "Usage: memory region ADDR") + # We allow --all or an address argument, not both + interp.HandleCommand("memory region --all 0", result) + self.assertFalse(result.Succeeded()) + self.assertRegexpMatches(result.GetError(), + "The --all argument cannot be used when an address argument is given") + # Test that when the address fails to parse, we show an error and do not continue interp.HandleCommand("memory region not_an_address", result) self.assertFalse(result.Succeeded()) self.assertEqual(result.GetError(), "error: invalid address argument \"not_an_address\": address expression \"not_an_address\" evaluation failed\n") + # Accumulate the results to compare with the --all output + all_regions = "" + # Now let's print the memory region starting at 0 which should always work. interp.HandleCommand("memory region 0x0", result) self.assertTrue(result.Succeeded()) self.assertRegexpMatches(result.GetOutput(), "\\[0x0+-") + all_regions += result.GetOutput() # Keep printing memory regions until we printed all of them. while True: interp.HandleCommand("memory region", result) if not result.Succeeded(): break + all_regions += result.GetOutput() # Now that we reached the end, 'memory region' should again print the usage. interp.HandleCommand("memory region", result) self.assertFalse(result.Succeeded()) self.assertRegexpMatches(result.GetError(), "Usage: memory region ADDR") + + # --all should match what repeating the command gives you + interp.HandleCommand("memory region --all", result) + self.assertTrue(result.Succeeded()) + self.assertEqual(result.GetOutput(), all_regions) Index: lldb/source/Commands/Options.td =================================================================== --- lldb/source/Commands/Options.td +++ lldb/source/Commands/Options.td @@ -511,6 +511,11 @@ Desc<"Start writing bytes from an offset within the input file.">; } +let Command = "memory region" in { + def memory_region_all : Option<"all", "a">, + Desc<"Show all memory regions.">; +} + let Command = "memory tag write" in { def memory_write_end_addr : Option<"end-addr", "e">, Group<1>, Arg<"AddressOrExpression">, Desc< Index: lldb/source/Commands/CommandObjectMemory.cpp =================================================================== --- lldb/source/Commands/CommandObjectMemory.cpp +++ lldb/source/Commands/CommandObjectMemory.cpp @@ -1608,8 +1608,45 @@ // CommandObjectMemoryRegion #pragma mark CommandObjectMemoryRegion +#define LLDB_OPTIONS_memory_region +#include "CommandOptions.inc" + class CommandObjectMemoryRegion : public CommandObjectParsed { public: + class OptionGroupMemoryRegion : public OptionGroup { + public: + OptionGroupMemoryRegion() : OptionGroup(), m_all(false, false) {} + + ~OptionGroupMemoryRegion() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::makeArrayRef(g_memory_region_options); + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, + ExecutionContext *execution_context) override { + Status status; + const int short_option = g_memory_region_options[option_idx].short_option; + + switch (short_option) { + case 'a': + m_all.SetCurrentValue(true); + m_all.SetOptionWasSet(); + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return status; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_all.Clear(); + } + + OptionValueBoolean m_all; + }; + CommandObjectMemoryRegion(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "memory region", "Get information on the memory region containing " @@ -1617,11 +1654,73 @@ "memory region ADDR", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched), - m_prev_end_addr(LLDB_INVALID_ADDRESS) {} + m_prev_end_addr(LLDB_INVALID_ADDRESS), m_option_group(), + m_memory_region_options() { + // Address + m_arguments.push_back(CommandArgumentEntry{ + CommandArgumentData(eArgTypeAddressOrExpression, eArgRepeatOptional)}); + + m_option_group.Append(&m_memory_region_options); + m_option_group.Finalize(); + } ~CommandObjectMemoryRegion() override = default; + Options *GetOptions() override { return &m_option_group; } + protected: + void DumpRegion(CommandReturnObject &result, Target &target, + MemoryRegionInfo &range_info, lldb::addr_t load_addr) { + lldb_private::Address addr; + ConstString name = range_info.GetName(); + ConstString section_name; + lldb::addr_t resolve_addr = load_addr; + if (m_memory_region_options.m_all) + resolve_addr = range_info.GetRange().GetRangeBase(); + + if (target.ResolveLoadAddress(resolve_addr, addr)) { + SectionSP section_sp(addr.GetSection()); + if (section_sp) { + // Got the top most section, not the deepest section + while (section_sp->GetParent()) + section_sp = section_sp->GetParent(); + section_name = section_sp->GetName(); + } + } + + result.AppendMessageWithFormatv( + "[{0:x16}-{1:x16}) {2:r}{3:w}{4:x}{5}{6}{7}{8}", + range_info.GetRange().GetRangeBase(), + range_info.GetRange().GetRangeEnd(), range_info.GetReadable(), + range_info.GetWritable(), range_info.GetExecutable(), name ? " " : "", + name, section_name ? " " : "", section_name); + MemoryRegionInfo::OptionalBool memory_tagged = range_info.GetMemoryTagged(); + if (memory_tagged == MemoryRegionInfo::OptionalBool::eYes) + result.AppendMessage("memory tagging: enabled"); + + const llvm::Optional<std::vector<addr_t>> &dirty_page_list = + range_info.GetDirtyPageList(); + if (dirty_page_list.hasValue()) { + const size_t page_count = dirty_page_list.getValue().size(); + result.AppendMessageWithFormat( + "Modified memory (dirty) page list provided, %zu entries.\n", + page_count); + if (page_count > 0) { + bool print_comma = false; + result.AppendMessageWithFormat("Dirty pages: "); + for (size_t i = 0; i < page_count; i++) { + if (print_comma) + result.AppendMessageWithFormat(", "); + else + print_comma = true; + result.AppendMessageWithFormat("0x%" PRIx64, + dirty_page_list.getValue()[i]); + } + result.AppendMessageWithFormat(".\n"); + } + } + } + bool DoExecute(Args &command, CommandReturnObject &result) override { ProcessSP process_sp = m_exe_ctx.GetProcessSP(); if (!process_sp) { @@ -1635,13 +1734,22 @@ m_prev_end_addr = LLDB_INVALID_ADDRESS; const size_t argc = command.GetArgumentCount(); - if (argc > 1 || (argc == 0 && load_addr == LLDB_INVALID_ADDRESS)) { - result.AppendErrorWithFormat("'%s' takes one argument:\nUsage: %s\n", - m_cmd_name.c_str(), m_cmd_syntax.c_str()); + if ((argc == 0 && !m_memory_region_options.m_all && + load_addr == LLDB_INVALID_ADDRESS) || + argc > 1) { + result.AppendErrorWithFormat( + "'%s' takes one address argument or --all:\nUsage: %s\n", + m_cmd_name.c_str(), m_cmd_syntax.c_str()); return false; } if (argc == 1) { + if (m_memory_region_options.m_all) { + result.AppendError("The --all argument cannot be used when an address " + "argument is given"); + return false; + } + auto load_addr_str = command[0].ref(); load_addr = OptionArgParser::ToAddress(&m_exe_ctx, load_addr_str, LLDB_INVALID_ADDRESS, &error); @@ -1652,56 +1760,36 @@ } } - lldb_private::MemoryRegionInfo range_info; - error = process_sp->GetMemoryRegionInfo(load_addr, range_info); - if (error.Success()) { - lldb_private::Address addr; - ConstString name = range_info.GetName(); - ConstString section_name; - if (process_sp->GetTarget().ResolveLoadAddress(load_addr, addr)) { - SectionSP section_sp(addr.GetSection()); - if (section_sp) { - // Got the top most section, not the deepest section - while (section_sp->GetParent()) - section_sp = section_sp->GetParent(); - section_name = section_sp->GetName(); + lldb_private::MemoryRegionInfos region_list; + if (m_memory_region_options.m_all) { + // We don't use GetMemoryRegions here because it doesn't include unmapped + // areas like repeating the command would. So instead, emulate doing that. + lldb::addr_t addr = 0; + while (error.Success()) { + lldb_private::MemoryRegionInfo region_info; + error = process_sp->GetMemoryRegionInfo(addr, region_info); + + if (error.Success()) { + region_list.push_back(region_info); + addr = region_info.GetRange().GetRangeEnd(); } } - result.AppendMessageWithFormatv( - "[{0:x16}-{1:x16}) {2:r}{3:w}{4:x}{5}{6}{7}{8}", - range_info.GetRange().GetRangeBase(), - range_info.GetRange().GetRangeEnd(), range_info.GetReadable(), - range_info.GetWritable(), range_info.GetExecutable(), name ? " " : "", - name, section_name ? " " : "", section_name); - MemoryRegionInfo::OptionalBool memory_tagged = - range_info.GetMemoryTagged(); - if (memory_tagged == MemoryRegionInfo::OptionalBool::eYes) - result.AppendMessage("memory tagging: enabled"); - - const llvm::Optional<std::vector<addr_t>> &dirty_page_list = - range_info.GetDirtyPageList(); - if (dirty_page_list.hasValue()) { - const size_t page_count = dirty_page_list.getValue().size(); - result.AppendMessageWithFormat( - "Modified memory (dirty) page list provided, %zu entries.\n", - page_count); - if (page_count > 0) { - bool print_comma = false; - result.AppendMessageWithFormat("Dirty pages: "); - for (size_t i = 0; i < page_count; i++) { - if (print_comma) - result.AppendMessageWithFormat(", "); - else - print_comma = true; - result.AppendMessageWithFormat("0x%" PRIx64, - dirty_page_list.getValue()[i]); - } - result.AppendMessageWithFormat(".\n"); - } + // Even if we read nothing, don't error for --all + error.Clear(); + } else { + lldb_private::MemoryRegionInfo region_info; + error = process_sp->GetMemoryRegionInfo(load_addr, region_info); + if (error.Success()) + region_list.push_back(region_info); + } + + if (error.Success()) { + for (MemoryRegionInfo &range_info : region_list) { + DumpRegion(result, process_sp->GetTarget(), range_info, load_addr); + m_prev_end_addr = range_info.GetRange().GetRangeEnd(); } - m_prev_end_addr = range_info.GetRange().GetRangeEnd(); result.SetStatus(eReturnStatusSuccessFinishResult); return true; } @@ -1718,6 +1806,9 @@ } lldb::addr_t m_prev_end_addr; + + OptionGroupOptions m_option_group; + OptionGroupMemoryRegion m_memory_region_options; }; // CommandObjectMemory
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits