gdb/dap/launch.py \
gdb/dap/locations.py \
gdb/dap/memory.py \
+ gdb/dap/modules.py \
gdb/dap/next.py \
gdb/dap/pause.py \
gdb/dap/scopes.py \
from . import launch
from . import locations
from . import memory
+from . import modules
from . import next
from . import pause
from . import scopes
from gdb.frames import frame_iterator
from .frames import frame_id
+from .modules import module_id
from .server import request, capability
from .startup import send_gdb_with_response, in_gdb_thread
from .state import set_thread
except gdb.error:
frame_iter = ()
for current_frame in frame_iter:
+ pc = current_frame.address()
newframe = {
"id": frame_id(current_frame),
"name": current_frame.function(),
"line": 0,
# GDB doesn't support columns.
"column": 0,
- "instructionPointerReference": hex(current_frame.address()),
+ "instructionPointerReference": hex(pc),
}
+ objfile = gdb.current_progspace().objfile_for_address(pc)
+ if objfile is not None:
+ newframe["moduleId"] = module_id(objfile)
line = current_frame.line()
if line is not None:
newframe["line"] = line
from .server import send_event
from .startup import in_gdb_thread, Invoker, log
from .breakpoint import breakpoint_descriptor
+from .modules import is_module, make_module
@in_gdb_thread
)
+@in_gdb_thread
+def _new_objfile(event):
+ if is_module(event.new_objfile):
+ send_event(
+ "module",
+ {
+ "reason": "new",
+ "module": make_module(event.new_objfile),
+ },
+ )
+
+
_suppress_cont = False
gdb.events.breakpoint_deleted.connect(_bp_deleted)
gdb.events.new_thread.connect(_new_thread)
gdb.events.cont.connect(_cont)
+gdb.events.new_objfile.connect(_new_objfile)
--- /dev/null
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+
+from .server import capability, request
+from .startup import in_gdb_thread, send_gdb_with_response
+
+
+@in_gdb_thread
+def module_id(objfile):
+ """Return the module ID for the objfile."""
+ return objfile.username
+
+
+@in_gdb_thread
+def is_module(objfile):
+ """Return True if OBJFILE represents a valid Module."""
+ return objfile.is_valid() and objfile.owner is None
+
+
+@in_gdb_thread
+def make_module(objf):
+ """Return a Module representing the objfile OBJF.
+
+ The objfile must pass the 'is_module' test."""
+ return {
+ "id": module_id(objf),
+ "name": objf.username,
+ "path": objf.filename,
+ }
+
+
+@in_gdb_thread
+def _modules(start, count):
+ # Don't count invalid objfiles or separate debug objfiles.
+ objfiles = [x for x in gdb.objfiles() if is_module(x)]
+ if count == 0:
+ # Use all items.
+ last = len(objfiles)
+ else:
+ last = start + count
+ return {
+ "modules": [make_module(x) for x in objfiles[start:last]],
+ "totalModules": len(objfiles),
+ }
+
+
+@capability("supportsModulesRequest")
+@request("modules")
+def modules(*, startModule: int = 0, moduleCount: int = 0, **args):
+ return send_gdb_with_response(lambda: _modules(startModule, moduleCount))
--- /dev/null
+/* Copyright 2023 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+int
+call_me (void (*callee) (void))
+{
+ callee ();
+ return 0;
+}
--- /dev/null
+/* Copyright 2023 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <assert.h>
+#include <stddef.h>
+
+void
+stop (void)
+{
+}
+
+int
+main (void)
+{
+ void *handle;
+ int (*func)(void (*) (void));
+
+ stop ();
+
+ handle = dlopen (SHLIB_NAME, RTLD_LAZY);
+ assert (handle != NULL);
+
+ func = (int (*)(void (*) (void))) dlsym (handle, "call_me");
+ func (stop);
+
+ return 0;
+}
--- /dev/null
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test DAP modules functionality.
+
+require allow_shlib_tests allow_dap_tests
+
+load_lib dap-support.exp
+
+standard_testfile
+
+set libname $testfile-solib
+set srcfile_lib $srcdir/$subdir/$libname.c
+set binfile_lib [standard_output_file $libname.so]
+
+if { [gdb_compile_shlib $srcfile_lib $binfile_lib {}] != "" } {
+ untested "failed to compile shared library"
+ return
+}
+
+set binfile_lib_target [gdb_download_shlib $binfile_lib]
+set define -DSHLIB_NAME=\"$binfile_lib_target\"
+
+if {[build_executable ${testfile}.exp $testfile $srcfile \
+ [list additional_flags=$define debug shlib_load]] == -1} {
+ return
+}
+
+if {[dap_launch $testfile] == ""} {
+ return
+}
+
+set obj [dap_check_request_and_response "set breakpoint on two functions" \
+ setFunctionBreakpoints \
+ {o breakpoints [a [o name [s stop]]]}]
+set fn_bpno [dap_get_breakpoint_number $obj]
+
+dap_check_request_and_response "start inferior" configurationDone
+
+dap_wait_for_event_and_check "stopped at function breakpoint" stopped \
+ "body reason" breakpoint \
+ "body hitBreakpointIds" $fn_bpno
+
+dap_check_request_and_response "continue to next stop" continue \
+ {o threadId [i 1]}
+
+
+lassign [dap_wait_for_event_and_check "module event" module \
+ "body reason" new] module_event ignore
+
+gdb_assert {[string match *$libname* [dict get $module_event body module id]]} \
+ "module.id"
+gdb_assert {[string match *$libname* [dict get $module_event body module name]]} \
+ "module.name"
+gdb_assert {[string match *$libname* [dict get $module_event body module path]]} \
+ "module.path"
+
+dap_wait_for_event_and_check "second stop at function breakpoint" stopped \
+ "body reason" breakpoint \
+ "body hitBreakpointIds" $fn_bpno
+
+set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
+ {o threadId [i 1]}] \
+ 0]
+set frame_id [dict get [lindex [dict get $bt body stackFrames] 1] moduleId]
+
+gdb_assert {[string match *$libname* $frame_id]} "module.id in stack trace"
+
+dap_shutdown