From d77cb2b1fcb92f0cea1eacbe32ad1da24d1b84b7 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Wed, 20 Sep 2023 13:41:50 +0200 Subject: [PATCH] monitor: add 'qXfer:memory-map:read' command` Fixes #5004 --- repos/os/run/monitor_gdb.run | 38 ++++++++++ repos/os/src/monitor/gdb_stub.h | 82 +++++++++++++++++++++ repos/os/src/monitor/inferior_pd.h | 19 ++--- repos/os/src/monitor/monitored_region_map.h | 62 +++++++++++++++- 4 files changed, 188 insertions(+), 13 deletions(-) diff --git a/repos/os/run/monitor_gdb.run b/repos/os/run/monitor_gdb.run index eeb4bbaa5b..3e93f1d462 100644 --- a/repos/os/run/monitor_gdb.run +++ b/repos/os/run/monitor_gdb.run @@ -193,6 +193,26 @@ if {![regexp {Genode::Lock::lock} $output] || exit -1 } +puts "\n" +puts "----- test: memory map -----" +puts "" + +send "info mem\n" +run_genode_until {\(gdb\)} 20 $gdb_id + +if {![regexp {..*y.*0x0000000000004000 0x0000000000006000 rw nocache} $output] || + ![regexp {..*y.*0x0000000000008000 0x0000000000010000 rw nocache} $output] || + ![regexp {..*y.*0x0000000000010000 0x0000000000012000 rw nocache} $output] || + ![regexp {..*y.*0x0000000000030000 0x00000000000..000 rw nocache} $output] || + ![regexp {7.*y.*0x0000000001000000 0x000000000100.000 rw nocache} $output] || + ![regexp {8.*y.*0x000000000100.000 0x000000000100.000 rw nocache} $output] || + ![regexp {9.*y.*0x00000000400fb000 0x00000000400ff000 rw nocache} $output] || + ![regexp {1..*y.*0x00000000401ef000 0x00000000401ff000 rw nocache} $output] || + ![regexp {1..*y.*0x00000000402f.000 0x00000000402ff000 rw nocache} $output]} { + puts stderr "*** Error: memory map is not as expected" + exit -1 +} + puts "\n" puts "----- test: stack trace of second inferior -----" puts "" @@ -217,4 +237,22 @@ if {![regexp {Genode::Signal_receiver::block_for_signal} $output] } { exit -1 } +puts "\n" +puts "----- test: memory map of second inferior -----" +puts "" + +send "info mem\n" +run_genode_until {\(gdb\)} 20 $gdb_id + +if {![regexp {..*y.*0x0000000000008000 0x0000000000010000 rw nocache} $output] || + ![regexp {..*y.*0x0000000000010000 0x0000000000012000 rw nocache} $output] || + ![regexp {..*y.*0x0000000000030000 0x00000000000..000 ro nocache} $output] || + ![regexp {5.*y.*0x0000000001000000 0x000000000100.000 ro nocache} $output] || + ![regexp {6.*y.*0x000000000100.000 0x000000000100.000 rw nocache} $output] || + ![regexp {7.*y.*0x00000000400fb000 0x00000000400ff000 rw nocache} $output] || + ![regexp {..*y.*0x00000000401ef000 0x00000000401ff000 rw nocache} $output]} { + puts stderr "*** Error: memory map of second inferior is not as expected" + exit -1 +} + puts "" diff --git a/repos/os/src/monitor/gdb_stub.h b/repos/os/src/monitor/gdb_stub.h index bde38df1b0..445af88318 100644 --- a/repos/os/src/monitor/gdb_stub.h +++ b/repos/os/src/monitor/gdb_stub.h @@ -53,6 +53,78 @@ struct Monitor::Gdb::State : Noncopyable } }; + struct Memory_map + { + char _buf[1024*16] { }; + size_t _len = 0; + + Memory_map(Inferior_pd &inferior) + { + typedef String<16> Value; + + Xml_generator xml(_buf, sizeof(_buf), "memory-map", [&] { + + inferior._address_space.for_each_region( + [&] (Monitored_region_map::Region const ®ion) { + + if (region.cap == inferior._linker_area.dataspace()) { + + inferior._linker_area.for_each_region( + [&] (Monitored_region_map::Region const + &linker_area_region) { + xml.node("memory", [&] { + xml.attribute("type", + linker_area_region.writeable ? + "ram" : "rom"); + xml.attribute("start", + Value(Hex(region.addr + + linker_area_region.addr))); + xml.attribute("length", + Value(Hex(linker_area_region.size))); + }); + }); + + return; + } + + if (region.cap == inferior._stack_area.dataspace()) { + + inferior._stack_area.for_each_region( + [&] (Monitored_region_map::Region const + &stack_area_region) { + xml.node("memory", [&] { + xml.attribute("type", + stack_area_region.writeable ? + "ram" : "rom"); + xml.attribute("start", + Value(Hex(region.addr + + stack_area_region.addr))); + xml.attribute("length", + Value(Hex(stack_area_region.size))); + }); + }); + + return; + } + + xml.node("memory", [&] { + xml.attribute("type", region.writeable ? "ram" : "rom"); + xml.attribute("start", Value(Hex(region.addr))); + xml.attribute("length", Value(Hex(region.size))); + }); + }); + }); + + _len = strlen(_buf); + } + + void with_bytes(auto const &fn) const + { + Const_byte_range_ptr const ptr { _buf, _len }; + fn(ptr); + } + }; + Memory_accessor &_memory_accessor; struct Current : Noncopyable @@ -239,6 +311,7 @@ struct qSupported : Command_with_separator print(out, "vContSupported+;"); print(out, "qXfer:features:read+;"); /* XML target descriptions */ print(out, "qXfer:threads:read+;"); + print(out, "qXfer:memory-map:read+;"); print(out, "multiprocess+;"); print(out, "QNonStop+;"); print(out, "swbreak+;"); @@ -304,6 +377,15 @@ struct qXfer : Command_with_separator handled = true; }); + with_skipped_prefix(args, "memory-map:read::", [&] (Const_byte_range_ptr const &args) { + if (state.current_defined()) { + State::Memory_map const memory_map(state._current->pd); + memory_map.with_bytes([&] (Const_byte_range_ptr const &bytes) { + _send_window(out, bytes, Window::from_args(args)); }); + } + handled = true; + }); + if (!handled) warning("GDB ", name, " command unsupported: ", Cstring(args.start, args.num_bytes)); } diff --git a/repos/os/src/monitor/inferior_pd.h b/repos/os/src/monitor/inferior_pd.h index b685e9dabb..344e4e68fb 100644 --- a/repos/os/src/monitor/inferior_pd.h +++ b/repos/os/src/monitor/inferior_pd.h @@ -28,15 +28,6 @@ struct Monitor::Inferior_pd : Monitored_pd_session { Inferiors::Element _inferiors_elem; - Monitored_region_map _address_space { - _ep, _real.call(), "address space" }; - - Monitored_region_map _stack_area { - _ep, _real.call(), "stack area" }; - - Monitored_region_map _linker_area { - _ep, _real.call(), "linker area" }; - Threads _threads { }; Threads::Id _last_thread_id { }; @@ -47,6 +38,16 @@ struct Monitor::Inferior_pd : Monitored_pd_session Allocator &_alloc; /* used for allocating 'Ram_ds' objects */ Ram_allocator &_wx_ram; /* RAM used for writeable text segments */ + Monitored_region_map _address_space { + _ep, _real.call(), "address space", + _alloc }; + + Monitored_region_map _stack_area { + _ep, _real.call(), "stack area", _alloc }; + + Monitored_region_map _linker_area { + _ep, _real.call(), "linker area", _alloc }; + struct Policy { bool wait; /* wait for GDB continue command */ diff --git a/repos/os/src/monitor/monitored_region_map.h b/repos/os/src/monitor/monitored_region_map.h index 2f0eeddb53..b0794b6b81 100644 --- a/repos/os/src/monitor/monitored_region_map.h +++ b/repos/os/src/monitor/monitored_region_map.h @@ -27,8 +27,6 @@ namespace Monitor { struct Monitored_region_map; } struct Monitor::Monitored_region_map : Monitored_rpc_object { - using Monitored_rpc_object::Monitored_rpc_object; - /* see the comment in base/include/region_map/client.h */ Dataspace_capability _rm_ds_cap { }; @@ -97,6 +95,40 @@ struct Monitor::Monitored_region_map : Monitored_rpc_object _writeable_text_segments.construct(alloc, ram, local_rm); } + struct Region : List::Element + { + Dataspace_capability cap; + addr_t addr; + size_t size; + bool writeable; + + Region(Dataspace_capability cap, addr_t addr, size_t size, + bool writeable) + : cap(cap), addr(addr), size(size), writeable(writeable) { } + }; + + List _regions { }; + + void for_each_region(auto const &fn) const + { + for (Region const *region = _regions.first(); region; region = region->next()) + fn(*region); + } + + Allocator &_alloc; + + Monitored_region_map(Entrypoint &ep, Capability real, + Name const &name, Allocator &alloc) + : Monitored_rpc_object(ep, real, name), + _alloc(alloc) { } + + ~Monitored_region_map() + { + while (Region *region = _regions.first()) { + _regions.remove(region); + destroy(_alloc, region); + } + } /************************** ** Region_map interface ** @@ -114,13 +146,35 @@ struct Monitor::Monitored_region_map : Monitored_rpc_object writeable = true; } - return _real.call(ds, size, offset, use_local_addr, local_addr, - executable, writeable); + Local_addr attached_addr = _real.call(ds, size, offset, + use_local_addr, + local_addr, + executable, + writeable); + size_t region_size = size ? size : + (Dataspace_client(ds).size() - offset); + enum { PAGE_SIZE_LOG2 = 12 }; + region_size = align_addr(region_size, PAGE_SIZE_LOG2); + + _regions.insert(new (_alloc) Region(ds, (addr_t)attached_addr, + region_size, writeable)); + return attached_addr; } void detach(Local_addr local_addr) override { _real.call(local_addr); + + addr_t addr = (addr_t)local_addr; + + for (Region *region = _regions.first(); region; region = region->next()) { + if ((addr >= region->addr) && + (addr <= (region->addr + region->size - 1))) { + _regions.remove(region); + destroy(_alloc, region); + break; + } + } } void fault_handler(Signal_context_capability) override