aboutsummaryrefslogtreecommitdiff
path: root/gdb/python/lib
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python/lib')
-rw-r--r--gdb/python/lib/gdb/__init__.py371
-rw-r--r--gdb/python/lib/gdb/command/frame_filters.py23
-rw-r--r--gdb/python/lib/gdb/command/missing_files.py10
-rw-r--r--gdb/python/lib/gdb/command/pretty_printers.py10
-rw-r--r--gdb/python/lib/gdb/command/xmethods.py8
-rw-r--r--gdb/python/lib/gdb/dap/__init__.py1
-rw-r--r--gdb/python/lib/gdb/dap/breakpoint.py47
-rw-r--r--gdb/python/lib/gdb/dap/completions.py7
-rw-r--r--gdb/python/lib/gdb/dap/evaluate.py6
-rw-r--r--gdb/python/lib/gdb/dap/events.py11
-rw-r--r--gdb/python/lib/gdb/dap/frames.py6
-rw-r--r--gdb/python/lib/gdb/dap/globalvars.py1
-rw-r--r--gdb/python/lib/gdb/dap/launch.py1
-rw-r--r--gdb/python/lib/gdb/dap/next.py27
-rw-r--r--gdb/python/lib/gdb/dap/scopes.py3
-rw-r--r--gdb/python/lib/gdb/dap/server.py128
-rw-r--r--gdb/python/lib/gdb/dap/sources.py9
-rw-r--r--gdb/python/lib/gdb/dap/startup.py10
-rw-r--r--gdb/python/lib/gdb/dap/threads.py23
-rw-r--r--gdb/python/lib/gdb/dap/varref.py13
-rw-r--r--gdb/python/lib/gdb/disassembler.py18
-rw-r--r--gdb/python/lib/gdb/printing.py19
-rw-r--r--gdb/python/lib/gdb/styling.py1
23 files changed, 554 insertions, 199 deletions
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 4ea5d06..d635b94 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -19,17 +19,31 @@ import sys
import threading
import traceback
from contextlib import contextmanager
+from importlib import reload
-# Python 3 moved "reload"
-if sys.version_info >= (3, 4):
- from importlib import reload
-else:
- from imp import reload
-
-import _gdb
-
+# The star import imports _gdb names. When the names are used locally, they
+# trigger F405 warnings unless added to the explicit import list.
# Note that two indicators are needed here to silence flake8.
from _gdb import * # noqa: F401,F403
+from _gdb import (
+ COMMAND_NONE,
+ INTENSITY_BOLD,
+ INTENSITY_DIM,
+ INTENSITY_NORMAL,
+ PARAM_COLOR,
+ PARAM_ENUM,
+ STDERR,
+ STDOUT,
+ Color,
+ Command,
+ Parameter,
+ Style,
+ execute,
+ flush,
+ parameter,
+ selected_inferior,
+ write,
+)
# isort: split
@@ -60,14 +74,14 @@ class _GdbFile(object):
self.write(line)
def flush(self):
- _gdb.flush(stream=self.stream)
+ flush(stream=self.stream)
def write(self, s):
- _gdb.write(s, stream=self.stream)
+ write(s, stream=self.stream)
-sys.stdout = _GdbFile(_gdb.STDOUT)
-sys.stderr = _GdbFile(_gdb.STDERR)
+sys.stdout = _GdbFile(STDOUT)
+sys.stderr = _GdbFile(STDERR)
# Default prompt hook does nothing.
prompt_hook = None
@@ -189,7 +203,7 @@ def GdbSetPythonDirectory(dir):
def current_progspace():
"Return the current Progspace."
- return _gdb.selected_inferior().progspace
+ return selected_inferior().progspace
def objfiles():
@@ -226,14 +240,14 @@ def set_parameter(name, value):
value = "on"
else:
value = "off"
- _gdb.execute("set " + name + " " + str(value), to_string=True)
+ execute("set " + name + " " + str(value), to_string=True)
@contextmanager
def with_parameter(name, value):
"""Temporarily set the GDB parameter NAME to VALUE.
Note that this is a context manager."""
- old_value = _gdb.parameter(name)
+ old_value = parameter(name)
set_parameter(name, value)
try:
# Nothing that useful to return.
@@ -392,3 +406,330 @@ def _handle_missing_objfile(pspace, buildid, filename):
return _handle_missing_files(
pspace, "objfile", lambda h: h(pspace, buildid, filename)
)
+
+
+class ParameterPrefix:
+ # A wrapper around gdb.Command for creating set/show prefixes.
+ #
+ # When creating a gdb.Parameter sub-classes, it is sometimes necessary
+ # to first create a gdb.Command object in order to create the needed
+ # command prefix. However, for parameters, we actually need two
+ # prefixes, a 'set' prefix, and a 'show' prefix. With this helper
+ # class, a single instance of this class will create both prefixes at
+ # once.
+ #
+ # It is important that this class-level documentation not be a __doc__
+ # string. Users are expected to sub-class this ParameterPrefix class
+ # and add their own documentation. If they don't, then GDB will
+ # generate a suitable doc string. But, if this (parent) class has a
+ # __doc__ string of its own, then sub-classes will inherit that __doc__
+ # string, and GDB will not understand that it needs to generate one.
+
+ class _PrefixCommand(Command):
+ """A gdb.Command used to implement both the set and show prefixes.
+
+ This documentation string is not used as the prefix command
+ documentation as it is overridden in the __init__ method below."""
+
+ # This private method is connected to the 'invoke' attribute within
+ # this _PrefixCommand object if the containing ParameterPrefix
+ # object has an invoke_set or invoke_show method.
+ #
+ # This method records within self.__delegate which _PrefixCommand
+ # object is currently active, and then calls the correct invoke
+ # method on the delegat object (the ParameterPrefix sub-class
+ # object).
+ #
+ # Recording the currently active _PrefixCommand object is important;
+ # if from the invoke method the user calls dont_repeat, then this is
+ # forwarded to the currently active _PrefixCommand object.
+ def __invoke(self, args, from_tty):
+
+ # A helper class for use as part of a Python 'with' block.
+ # Records which gdb.Command object is currently running its
+ # invoke method.
+ class MarkActiveCallback:
+ # The CMD is a _PrefixCommand object, and the DELEGATE is
+ # the ParameterPrefix class, or sub-class object. At this
+ # point we simple record both of these within the
+ # MarkActiveCallback object.
+ def __init__(self, cmd, delegate):
+ self.__cmd = cmd
+ self.__delegate = delegate
+
+ # Record the currently active _PrefixCommand object within
+ # the outer ParameterPrefix sub-class object.
+ def __enter__(self):
+ self.__delegate.active_prefix = self.__cmd
+
+ # Once the invoke method has completed, then clear the
+ # _PrefixCommand object that was stored into the outer
+ # ParameterPrefix sub-class object.
+ def __exit__(self, exception_type, exception_value, traceback):
+ self.__delegate.active_prefix = None
+
+ # The self.__cb attribute is set when the _PrefixCommand object
+ # is created, and is either invoke_set or invoke_show within the
+ # ParameterPrefix sub-class object.
+ assert callable(self.__cb)
+
+ # Record the currently active _PrefixCommand object within the
+ # ParameterPrefix sub-class object, then call the relevant
+ # invoke method within the ParameterPrefix sub-class object.
+ with MarkActiveCallback(self, self.__delegate):
+ self.__cb(args, from_tty)
+
+ @staticmethod
+ def __find_callback(delegate, mode):
+ """The MODE is either 'set' or 'show'. Look for an invoke_MODE method
+ on DELEGATE, if a suitable method is found, then return it, otherwise,
+ return None.
+ """
+ cb = getattr(delegate, "invoke_" + mode, None)
+ if callable(cb):
+ return cb
+ return None
+
+ def __init__(self, mode, name, cmd_class, delegate, doc=None):
+ """Setup this gdb.Command. Mode is a string, either 'set' or 'show'.
+ NAME is the name for this prefix command, that is, the
+ words that appear after both 'set' and 'show' in the
+ command name. CMD_CLASS is the usual enum. And DELEGATE
+ is the gdb.ParameterPrefix object this prefix is part of.
+ """
+ assert mode == "set" or mode == "show"
+ if doc is None:
+ self.__doc__ = delegate.__doc__
+ else:
+ self.__doc__ = doc
+ self.__cb = self.__find_callback(delegate, mode)
+ self.__delegate = delegate
+ if self.__cb is not None:
+ self.invoke = self.__invoke
+ super().__init__(mode + " " + name, cmd_class, prefix=True)
+
+ def __init__(self, name, cmd_class, doc=None):
+ """Create a _PrefixCommand for both the set and show prefix commands.
+ NAME is the command name without either the leading 'set ' or
+ 'show ' strings, and CMD_CLASS is the usual enum value.
+ """
+ self.active_prefix = None
+ self._set_prefix_cmd = self._PrefixCommand("set", name, cmd_class, self, doc)
+ self._show_prefix_cmd = self._PrefixCommand("show", name, cmd_class, self, doc)
+
+ # When called from within an invoke method the self.active_prefix
+ # attribute should be set to a gdb.Command sub-class (a _PrefixCommand
+ # object, see above). Forward the dont_repeat call to this object to
+ # register the actual command as none repeating.
+ def dont_repeat(self):
+ if self.active_prefix is not None:
+ self.active_prefix.dont_repeat()
+
+
+class StyleParameterSet:
+ """Create new style parameters.
+
+ A style parameter is a set of parameters that start with 'set style ...'
+ and 'show style ...'. For example 'filename' is a style parameter, and
+ 'disassembler symbol' is another style parameter.
+
+ The name of the style parameter is really a prefix command. Under this
+ we must have two commands 'foreground' and 'background', which are color
+ parameters. A third, optional command 'intensity', is an enum with
+ values 'normal', 'bold', and 'dim'.
+
+ A StyleParameterSet is initialised with a name, e.g. 'filename' or
+ 'disassembler symbol'. The StyleParameterSet creates the prefix
+ commands in the 'set style' and 'show style' name space, and then adds
+ the 'foreground', 'background', and optionally, the 'intensity'
+ commands.
+
+ If you want a whole new style group, similar to 'disassembler',
+ then you need to add this yourself first, then StyleParameterSet
+ can be used to create styles within the new prefix group.
+
+ The 'value' attribute on this object can be used to get and set a
+ gdb.Style object which controls all aspects of this style.
+
+ For readability, the alias 'style' is the same as 'value'.
+ """
+
+ def __init__(self, name, add_intensity=True, doc=None):
+ # The STYLE_NAME is something like 'filename' is 'set style
+ # filename ...', and PARAM_NAME is one of 'foreground',
+ # 'background', or 'intensity'. The DESC_TEXT is the long
+ # form used in help text, like 'foreground color' or 'display
+ # intensity'. The DEFAULT_VALUE is used to set the SELF.value
+ # attribute. And PARAM_TYPE is a gdb.PARAM_* constant. The
+ # ARGS is used for gdb.PARAM_ENUM, which ARGS should be the
+ # enum value list.
+ class style_parameter(Parameter):
+ def __init__(
+ self,
+ style_name,
+ parent_obj,
+ param_name,
+ desc_text,
+ default_value,
+ param_type,
+ *args
+ ):
+ # Setup documentation must be done before calling
+ # parent's __init__ method, as the __init__ reads (and
+ # copies) these values.
+ self.show_doc = "Show the " + desc_text + " for this property."
+ self.set_doc = "Set the " + desc_text + " for this property."
+ self.__doc__ = ""
+
+ # Call the parent's __init__ method to actually create
+ # the parameter.
+ super().__init__(
+ "style " + style_name + " " + param_name,
+ COMMAND_NONE,
+ param_type,
+ *args
+ )
+
+ # Store information we need in other methods.
+ self._style_name = style_name
+ self._desc_text = desc_text
+ self._parent = parent_obj
+
+ # Finally, setup the default value.
+ self.value = default_value
+
+ # Return the 'show style <style-name> <attribute>' string,
+ # which has styling applied.
+ def get_show_string(self, value):
+ s = self._parent.style
+ return (
+ "The "
+ + s.apply('"' + self._style_name + '" style')
+ + " "
+ + self._desc_text
+ + " is: "
+ + value
+ )
+
+ class style_foreground_parameter(style_parameter):
+ def __init__(self, name, parent):
+ super().__init__(
+ name,
+ parent,
+ "foreground",
+ "foreground color",
+ Color(),
+ PARAM_COLOR,
+ )
+
+ class style_background_parameter(style_parameter):
+ def __init__(self, name, parent):
+ super().__init__(
+ name,
+ parent,
+ "background",
+ "background color",
+ Color(),
+ PARAM_COLOR,
+ )
+
+ class style_intensity_parameter(style_parameter):
+ def __init__(self, name, parent):
+ super().__init__(
+ name,
+ parent,
+ "intensity",
+ "display intensity",
+ "normal",
+ PARAM_ENUM,
+ ["normal", "bold", "dim"],
+ )
+
+ if doc is None:
+ doc = (
+ "The "
+ + name
+ + " display styling.\nConfigure "
+ + name
+ + " colors and display intensity."
+ )
+
+ ParameterPrefix("style " + name, COMMAND_NONE, doc)
+ self._foreground = style_foreground_parameter(name, self)
+ self._background = style_background_parameter(name, self)
+ if add_intensity:
+ self._intensity = style_intensity_parameter(name, self)
+ self._name = name
+ self._style = None
+
+ @property
+ def value(self):
+ """Return the gdb.Style object for this parameter set."""
+ if self._style is None:
+ self._style = Style(self._name)
+ return self._style
+
+ @property
+ def style(self):
+ """Return the gdb.Style object for this parameter set.
+
+ This is an alias for self.value."""
+ return self.value
+
+ @value.setter
+ def value(self, new_value):
+ """Set this parameter set to NEW_VALUE, a gdb.Style object.
+
+ The attributes of NEW_VALUE are used to update the current settings
+ of this parameter set. If this parameter set was created without
+ an intensity setting, then the intensity of NEW_VALUE is ignored."""
+ if not isinstance(new_value, Style):
+ raise TypeError("value must be gdb.Style, not %s" % type(new_value))
+ self._foreground.value = new_value.foreground
+ self._background.value = new_value.background
+ if hasattr(self, "_intensity"):
+ intensity_value = new_value.intensity
+ if intensity_value == INTENSITY_BOLD:
+ intensity_string = "bold"
+ elif intensity_value == INTENSITY_DIM:
+ intensity_string = "dim"
+ elif intensity_value == INTENSITY_NORMAL:
+ intensity_string = "normal"
+ else:
+ raise ValueError(
+ "unknown intensity value %d from Style" % intensity_value
+ )
+
+ self._intensity.value = intensity_string
+
+ @style.setter
+ def style(self, new_value):
+ """Set this parameter set to NEW_VALUE, a gdb.Style object.
+
+ This is an alias for self.value."""
+ self.value = new_value
+
+ def apply(self, *args, **kwargs):
+ """Apply this style to the arguments.
+
+ Forwards all arguments to self.style.apply(). The arguments should be a string,
+ to which this style is applied. This function returns the same string with
+ escape sequences added to apply this style.
+
+ If styling is globally disabled ('set style enabled off') then no escape sequences
+ will be added, the input string is returned."""
+ return self.style.apply(*args, **kwargs)
+
+ def __repr__(self):
+ """A string representation of SELF."""
+
+ def full_typename(obj):
+ module = type(obj).__module__
+ qualname = type(obj).__qualname__
+
+ if module is None or module == "builtins":
+ return qualname
+ else:
+ return module + "." + qualname
+
+ return "<" + full_typename(self) + " name='" + self._name + "'>"
diff --git a/gdb/python/lib/gdb/command/frame_filters.py b/gdb/python/lib/gdb/command/frame_filters.py
index be7be9a..3b7cd03 100644
--- a/gdb/python/lib/gdb/command/frame_filters.py
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -16,8 +16,6 @@
"""GDB commands for working with frame-filters."""
-import sys
-
import gdb
import gdb.frames
@@ -68,7 +66,11 @@ class InfoFrameFilter(gdb.Command):
return 0
print(title)
- print(" Priority Enabled Name")
+ style = gdb.Style("title")
+ print(
+ " %s %s %s"
+ % (style.apply("Priority"), style.apply("Enabled"), style.apply("Name"))
+ )
for frame_filter in sorted_frame_filters:
name = frame_filter[0]
try:
@@ -77,9 +79,8 @@ class InfoFrameFilter(gdb.Command):
self.enabled_string(gdb.frames.get_enabled(frame_filter[1]))
)
print(" %s %s %s" % (priority, enabled, name))
- except Exception:
- e = sys.exc_info()[1]
- print(" Error printing filter '" + name + "': " + str(e))
+ except Exception as e:
+ gdb.warning("Error printing filter '" + name + "': " + str(e))
if blank_line:
print("")
return 1
@@ -87,14 +88,20 @@ class InfoFrameFilter(gdb.Command):
def invoke(self, arg, from_tty):
any_printed = self.print_list("global frame-filters:", gdb.frame_filters, True)
+ file_style = gdb.Style("filename")
cp = gdb.current_progspace()
+ cp_filename = cp.filename
+ if cp_filename is None:
+ cp_filename = "<no-file>"
+ else:
+ cp_filename = file_style.apply(cp_filename)
any_printed += self.print_list(
- "progspace %s frame-filters:" % cp.filename, cp.frame_filters, True
+ "progspace %s frame-filters:" % cp_filename, cp.frame_filters, True
)
for objfile in gdb.objfiles():
any_printed += self.print_list(
- "objfile %s frame-filters:" % objfile.filename,
+ "objfile %s frame-filters:" % file_style.apply(objfile.filename),
objfile.frame_filters,
False,
)
diff --git a/gdb/python/lib/gdb/command/missing_files.py b/gdb/python/lib/gdb/command/missing_files.py
index 09d9684..b2477ce 100644
--- a/gdb/python/lib/gdb/command/missing_files.py
+++ b/gdb/python/lib/gdb/command/missing_files.py
@@ -118,10 +118,16 @@ class InfoMissingFileHandlers(gdb.Command):
def invoke(self, arg, from_tty):
locus_re, name_re = parse_missing_file_command_args(arg)
+ file_style = gdb.Style("filename")
if locus_re.match("progspace") and locus_re.pattern != "":
cp = gdb.current_progspace()
+ cp_filename = cp.filename
+ if cp.filename is None:
+ cp_filename = "<no-file>"
+ else:
+ cp_filename = file_style.apply(cp_filename)
self.list_handlers(
- "Progspace %s:" % cp.filename, cp.missing_file_handlers, name_re
+ "Progspace %s:" % cp_filename, cp.missing_file_handlers, name_re
)
for progspace in gdb.progspaces():
@@ -133,7 +139,7 @@ class InfoMissingFileHandlers(gdb.Command):
else:
msg = "Progspace <no-file>:"
else:
- msg = "Progspace %s:" % filename
+ msg = "Progspace %s:" % file_style.apply(filename)
self.list_handlers(
msg,
progspace.missing_file_handlers,
diff --git a/gdb/python/lib/gdb/command/pretty_printers.py b/gdb/python/lib/gdb/command/pretty_printers.py
index f62d329..cdf9054 100644
--- a/gdb/python/lib/gdb/command/pretty_printers.py
+++ b/gdb/python/lib/gdb/command/pretty_printers.py
@@ -161,9 +161,15 @@ class InfoPrettyPrinter(gdb.Command):
name_re,
subname_re,
)
+ file_style = gdb.Style("filename")
cp = gdb.current_progspace()
+ cp_filename = cp.filename
+ if cp_filename is None:
+ cp_filename = "<no-file>"
+ else:
+ cp_filename = file_style.apply(cp_filename)
self.invoke1(
- "progspace %s pretty-printers:" % cp.filename,
+ "progspace %s pretty-printers:" % cp_filename,
cp.pretty_printers,
"progspace",
object_re,
@@ -172,7 +178,7 @@ class InfoPrettyPrinter(gdb.Command):
)
for objfile in gdb.objfiles():
self.invoke1(
- "objfile %s pretty-printers:" % objfile.filename,
+ "objfile %s pretty-printers:" % file_style.apply(objfile.filename),
objfile.pretty_printers,
objfile.filename,
object_re,
diff --git a/gdb/python/lib/gdb/command/xmethods.py b/gdb/python/lib/gdb/command/xmethods.py
index 719c146..2f87074 100644
--- a/gdb/python/lib/gdb/command/xmethods.py
+++ b/gdb/python/lib/gdb/command/xmethods.py
@@ -101,6 +101,7 @@ def get_method_matchers_in_loci(loci, locus_re, matcher_re):
A dict of matching xmethod matchers. The keys of the dict are the
filenames of the loci the xmethod matchers belong to.
"""
+ file_style = gdb.Style("filename")
xm_dict = {}
for locus in loci:
if isinstance(locus, gdb.Progspace):
@@ -111,7 +112,12 @@ def get_method_matchers_in_loci(loci, locus_re, matcher_re):
if not locus_re.match(locus.filename):
continue
locus_type = "objfile"
- locus_str = "%s %s" % (locus_type, locus.filename)
+ filename = locus.filename
+ if filename is None:
+ filename = "<no-file>"
+ else:
+ filename = file_style.apply(filename)
+ locus_str = "%s %s" % (locus_type, filename)
xm_dict[locus_str] = [m for m in locus.xmethods if matcher_re.match(m.name)]
return xm_dict
diff --git a/gdb/python/lib/gdb/dap/__init__.py b/gdb/python/lib/gdb/dap/__init__.py
index f908c91..1c3cf8e 100644
--- a/gdb/python/lib/gdb/dap/__init__.py
+++ b/gdb/python/lib/gdb/dap/__init__.py
@@ -96,5 +96,4 @@ def pre_command_loop():
# These are handy for bug reports.
startup.exec_and_log("show version")
startup.exec_and_log("show configuration")
- global server
startup.start_dap(server.main_loop)
diff --git a/gdb/python/lib/gdb/dap/breakpoint.py b/gdb/python/lib/gdb/dap/breakpoint.py
index bfb7bd3..3d1cfef 100644
--- a/gdb/python/lib/gdb/dap/breakpoint.py
+++ b/gdb/python/lib/gdb/dap/breakpoint.py
@@ -51,7 +51,6 @@ def suppress_new_breakpoint_event():
@in_gdb_thread
def _bp_modified(event):
- global _suppress_bp
if not _suppress_bp:
send_event(
"breakpoint",
@@ -64,7 +63,6 @@ def _bp_modified(event):
@in_gdb_thread
def _bp_created(event):
- global _suppress_bp
if not _suppress_bp:
send_event(
"breakpoint",
@@ -77,7 +75,6 @@ def _bp_created(event):
@in_gdb_thread
def _bp_deleted(event):
- global _suppress_bp
if not _suppress_bp:
send_event(
"breakpoint",
@@ -150,8 +147,7 @@ def _remove_entries(table, *names):
# specifications and a callback function to do the work of creating
# the breakpoint.
@in_gdb_thread
-def _set_breakpoints_callback(kind, specs, creator):
- global breakpoint_map
+def _set_breakpoints(kind, specs, creator):
# Try to reuse existing breakpoints if possible.
if kind in breakpoint_map:
saved_map = breakpoint_map[kind]
@@ -258,13 +254,6 @@ def _set_one_breakpoint(*, logMessage=None, **args):
return gdb.Breakpoint(**args)
-# Helper function to set ordinary breakpoints according to a list of
-# specifications.
-@in_gdb_thread
-def _set_breakpoints(kind, specs):
- return _set_breakpoints_callback(kind, specs, _set_one_breakpoint)
-
-
# A helper function that rewrites a SourceBreakpoint into the internal
# form passed to the creator. This function also allows for
# type-checking of each SourceBreakpoint.
@@ -306,7 +295,7 @@ def set_breakpoint(*, source, breakpoints: Sequence = (), **args):
# Be sure to include the path in the key, so that we only
# clear out breakpoints coming from this same source.
key = "source:" + source["path"]
- result = _set_breakpoints(key, specs)
+ result = _set_breakpoints(key, specs, _set_one_breakpoint)
return {
"breakpoints": result,
}
@@ -330,12 +319,12 @@ def _rewrite_fn_breakpoint(
}
-@request("setFunctionBreakpoints")
+@request("setFunctionBreakpoints", expect_stopped=False)
@capability("supportsFunctionBreakpoints")
def set_fn_breakpoint(*, breakpoints: Sequence, **args):
specs = [_rewrite_fn_breakpoint(**bp) for bp in breakpoints]
return {
- "breakpoints": _set_breakpoints("function", specs),
+ "breakpoints": _set_breakpoints("function", specs, _set_one_breakpoint),
}
@@ -363,24 +352,26 @@ def _rewrite_insn_breakpoint(
}
-@request("setInstructionBreakpoints")
+@request("setInstructionBreakpoints", expect_stopped=False)
@capability("supportsInstructionBreakpoints")
def set_insn_breakpoints(
*, breakpoints: Sequence, offset: Optional[int] = None, **args
):
specs = [_rewrite_insn_breakpoint(**bp) for bp in breakpoints]
return {
- "breakpoints": _set_breakpoints("instruction", specs),
+ "breakpoints": _set_breakpoints("instruction", specs, _set_one_breakpoint),
}
@in_gdb_thread
def _catch_exception(filterId, **args):
if filterId in ("assert", "exception", "throw", "rethrow", "catch"):
- cmd = "-catch-" + filterId
+ cmd = ["-catch-" + filterId]
else:
raise DAPException("Invalid exception filterID: " + str(filterId))
- result = exec_mi_and_log(cmd)
+ if "exception" in args and args["exception"] is not None:
+ cmd += ["-e", args["exception"]]
+ result = exec_mi_and_log(*cmd)
# While the Ada catchpoints emit a "bkptno" field here, the C++
# ones do not. So, instead we look at the "number" field.
num = result["bkpt"]["number"]
@@ -392,11 +383,6 @@ def _catch_exception(filterId, **args):
raise Exception("Could not find catchpoint after creating")
-@in_gdb_thread
-def _set_exception_catchpoints(filter_options):
- return _set_breakpoints_callback("exception", filter_options, _catch_exception)
-
-
# A helper function that rewrites an ExceptionFilterOptions into the
# internal form passed to the creator. This function also allows for
# type-checking of each ExceptionFilterOptions.
@@ -408,13 +394,20 @@ def _rewrite_exception_breakpoint(
# Note that exception breakpoints do not support a hit count.
**args,
):
+ if filterId == "exception":
+ # Treat Ada exceptions specially -- in particular the
+ # condition is just an exception name, not an expression.
+ return {
+ "filterId": filterId,
+ "exception": condition,
+ }
return {
"filterId": filterId,
"condition": condition,
}
-@request("setExceptionBreakpoints")
+@request("setExceptionBreakpoints", expect_stopped=False)
@capability("supportsExceptionFilterOptions")
@capability(
"exceptionBreakpointFilters",
@@ -453,6 +446,4 @@ def set_exception_breakpoints(
options = [{"filterId": filter} for filter in filters]
options.extend(filterOptions)
options = [_rewrite_exception_breakpoint(**bp) for bp in options]
- return {
- "breakpoints": _set_exception_catchpoints(options),
- }
+ return {"breakpoints": _set_breakpoints("exception", options, _catch_exception)}
diff --git a/gdb/python/lib/gdb/dap/completions.py b/gdb/python/lib/gdb/dap/completions.py
index 85acc43..e5003ff 100644
--- a/gdb/python/lib/gdb/dap/completions.py
+++ b/gdb/python/lib/gdb/dap/completions.py
@@ -39,8 +39,11 @@ def completions(
line = 1
else:
line = import_line(line)
- text = text.splitlines()[line - 1]
- text = text[: column - 1]
+ if text:
+ text = text.splitlines()[line - 1]
+ text = text[: column - 1]
+ else:
+ text = ""
mi_result = exec_mi_and_log("-complete", text)
result = []
completion = None
diff --git a/gdb/python/lib/gdb/dap/evaluate.py b/gdb/python/lib/gdb/dap/evaluate.py
index 55538e6..fcbcc99 100644
--- a/gdb/python/lib/gdb/dap/evaluate.py
+++ b/gdb/python/lib/gdb/dap/evaluate.py
@@ -69,7 +69,7 @@ def _repl(command, frame_id):
}
-@request("evaluate")
+@request("evaluate", defer_events=False)
@capability("supportsEvaluateForHovers")
@capability("supportsValueFormattingOptions")
def eval_request(
@@ -110,7 +110,7 @@ def variables(
@capability("supportsSetExpression")
-@request("setExpression")
+@request("setExpression", defer_events=False)
def set_expression(
*, expression: str, value: str, frameId: Optional[int] = None, format=None, **args
):
@@ -126,7 +126,7 @@ def set_expression(
@capability("supportsSetVariable")
-@request("setVariable")
+@request("setVariable", defer_events=False)
def set_variable(
*, variablesReference: int, name: str, value: str, format=None, **args
):
diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py
index 54caea0..778acc5 100644
--- a/gdb/python/lib/gdb/dap/events.py
+++ b/gdb/python/lib/gdb/dap/events.py
@@ -17,12 +17,12 @@ import gdb
from .modules import is_module, make_module
from .scopes import set_finish_value
-from .server import send_event, send_event_maybe_later
+from .server import send_event
from .startup import exec_and_log, in_gdb_thread, log
# True when the inferior is thought to be running, False otherwise.
# This may be accessed from any thread, which can be racy. However,
-# this unimportant because this global is only used for the
+# this is unimportant because this global is only used for the
# 'notStopped' response, which itself is inherently racy.
inferior_running = False
@@ -161,7 +161,7 @@ _expected_pause = False
@in_gdb_thread
-def exec_and_expect_stop(cmd, expected_pause=False, propagate_exception=False):
+def exec_and_expect_stop(cmd, expected_pause=False):
"""A wrapper for exec_and_log that sets the continue-suppression flag.
When EXPECTED_PAUSE is True, a stop that looks like a pause (e.g.,
@@ -174,7 +174,7 @@ def exec_and_expect_stop(cmd, expected_pause=False, propagate_exception=False):
# continuing.
_suppress_cont = not expected_pause
# FIXME if the call fails should we clear _suppress_cont?
- exec_and_log(cmd, propagate_exception)
+ exec_and_log(cmd)
# Map from gdb stop reasons to DAP stop reasons. Some of these can't
@@ -238,10 +238,9 @@ def _on_stop(event):
):
obj["reason"] = "pause"
else:
- global stop_reason_map
obj["reason"] = stop_reason_map[event.details["reason"]]
_expected_pause = False
- send_event_maybe_later("stopped", obj)
+ send_event("stopped", obj)
# This keeps a bit of state between the start of an inferior call and
diff --git a/gdb/python/lib/gdb/dap/frames.py b/gdb/python/lib/gdb/dap/frames.py
index 6433dbe..4dacb87 100644
--- a/gdb/python/lib/gdb/dap/frames.py
+++ b/gdb/python/lib/gdb/dap/frames.py
@@ -53,12 +53,11 @@ gdb.events.cont.connect(_clear_frame_ids)
@in_gdb_thread
def frame_for_id(id):
"""Given a frame identifier ID, return the corresponding frame."""
- global thread_ids
if id in thread_ids:
thread_id = thread_ids[id]
if thread_id != gdb.selected_thread().global_num:
set_thread(thread_id)
- global _all_frames
+
return _all_frames[id]
@@ -103,10 +102,8 @@ def _frame_id_generator():
# Helper function to assign an ID to a frame.
def get_id(frame):
- global _all_frames
num = len(_all_frames)
_all_frames.append(frame)
- global thread_ids
thread_ids[num] = gdb.selected_thread().global_num
return num
@@ -128,7 +125,6 @@ def _frame_id_generator():
@in_gdb_thread
def _get_frame_iterator():
thread_id = gdb.selected_thread().global_num
- global _iter_map
if thread_id not in _iter_map:
_iter_map[thread_id] = _MemoizingIterator(_frame_id_generator())
return _iter_map[thread_id]
diff --git a/gdb/python/lib/gdb/dap/globalvars.py b/gdb/python/lib/gdb/dap/globalvars.py
index 2e4b2a6..9d64d28 100644
--- a/gdb/python/lib/gdb/dap/globalvars.py
+++ b/gdb/python/lib/gdb/dap/globalvars.py
@@ -78,7 +78,6 @@ def get_global_scope(frame):
except RuntimeError:
return None
- global _id_to_scope
block = block.static_block
if block in _id_to_scope:
return _id_to_scope[block]
diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py
index e093e60..8ac4c77 100644
--- a/gdb/python/lib/gdb/dap/launch.py
+++ b/gdb/python/lib/gdb/dap/launch.py
@@ -164,7 +164,6 @@ def attach(
@request("configurationDone", on_dap_thread=True)
def config_done(**args):
# Handle the launch or attach.
- global _launch_or_attach_promise
if _launch_or_attach_promise is None:
raise DAPException("launch or attach not specified")
# Resolve the launch or attach, but only after the
diff --git a/gdb/python/lib/gdb/dap/next.py b/gdb/python/lib/gdb/dap/next.py
index ddf4e05..993e9d2 100644
--- a/gdb/python/lib/gdb/dap/next.py
+++ b/gdb/python/lib/gdb/dap/next.py
@@ -16,8 +16,8 @@
import gdb
from .events import exec_and_expect_stop
-from .server import capability, request, send_gdb, send_gdb_with_response
-from .startup import in_gdb_thread
+from .server import capability, request
+from .startup import DAPException, exec_and_log, in_gdb_thread
from .state import set_thread
@@ -36,11 +36,9 @@ def _handle_thread_step(thread_id, single_thread, select=False):
result = False
arg = "off"
try:
- # This can fail, depending on the target, so catch the error
- # and report to our caller. We can't use exec_and_log because
- # that does not propagate exceptions.
- gdb.execute("set scheduler-locking " + arg, from_tty=True, to_string=True)
- except gdb.error:
+ # This can fail, depending on the target, so catch any error.
+ exec_and_log("set scheduler-locking " + arg)
+ except DAPException:
result = False
# Other DAP code may select a frame, and the "finish" command uses
# the selected frame.
@@ -73,19 +71,14 @@ def step_in(
exec_and_expect_stop(cmd)
-@request("stepOut", defer_stop_events=True)
+@request("stepOut")
def step_out(*, threadId: int, singleThread: bool = False, **args):
_handle_thread_step(threadId, singleThread, True)
- exec_and_expect_stop("finish &", propagate_exception=True)
+ exec_and_expect_stop("finish &")
-# This is a server-side request because it is funny: it wants to
-# 'continue' but also return a result, which precludes using
-# response=False. Using 'continue &' would mostly work ok, but this
-# yields races when a stop occurs before the response is sent back to
-# the client.
-@request("continue", on_dap_thread=True)
+@request("continue")
def continue_request(*, threadId: int, singleThread: bool = False, **args):
- locked = send_gdb_with_response(lambda: _handle_thread_step(threadId, singleThread))
- send_gdb(lambda: exec_and_expect_stop("continue"))
+ locked = _handle_thread_step(threadId, singleThread)
+ exec_and_expect_stop("continue &")
return {"allThreadsContinued": not locked}
diff --git a/gdb/python/lib/gdb/dap/scopes.py b/gdb/python/lib/gdb/dap/scopes.py
index f499dcb..7ce3a7f 100644
--- a/gdb/python/lib/gdb/dap/scopes.py
+++ b/gdb/python/lib/gdb/dap/scopes.py
@@ -120,7 +120,6 @@ class _FinishScopeReference(_ScopeReference):
def fetch_one_child(self, idx):
assert idx == 0
- global _last_return_value
return ("(return)", _last_return_value)
@@ -145,8 +144,6 @@ class _RegisterReference(_ScopeReference):
@request("scopes")
def scopes(*, frameId: int, **extra):
- global _last_return_value
- global frame_to_scope
if frameId in frame_to_scope:
scopes = frame_to_scope[frameId]
else:
diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py
index d4314e8..98a8084 100644
--- a/gdb/python/lib/gdb/dap/server.py
+++ b/gdb/python/lib/gdb/dap/server.py
@@ -74,6 +74,13 @@ class DeferredRequest:
self._result = result
@in_dap_thread
+ def defer_events(self):
+ """Return True if events should be deferred during execution.
+
+ This may be overridden by subclasses."""
+ return True
+
+ @in_dap_thread
def invoke(self):
"""Implement the deferred request.
@@ -95,7 +102,10 @@ class DeferredRequest:
"""
with _server.canceller.current_request(self._req):
+ if self.defer_events():
+ _server.set_defer_events()
_server.invoke_request(self._req, self._result, self.invoke)
+ _server.emit_pending_events()
# A subclass of Exception that is used solely for reporting that a
@@ -230,7 +240,7 @@ class Server:
self._out_stream = out_stream
self._child_stream = child_stream
self._delayed_fns_lock = threading.Lock()
- self.defer_stop_events = False
+ self._defer_events = False
self._delayed_fns = []
# This queue accepts JSON objects that are then sent to the
# DAP client. Writing is done in a separate thread to avoid
@@ -307,7 +317,6 @@ class Server:
args = {}
def fn():
- global _commands
return _commands[params["command"]](**args)
self.invoke_request(req, result, fn)
@@ -317,7 +326,7 @@ class Server:
def _read_inferior_output(self):
while True:
line = self._child_stream.readline()
- self.send_event(
+ self.send_event_maybe_later(
"output",
{
"category": "stdout",
@@ -357,6 +366,17 @@ class Server:
self._read_queue.put(None)
@in_dap_thread
+ def emit_pending_events(self):
+ """Emit any pending events."""
+ fns = None
+ with self._delayed_fns_lock:
+ fns = self._delayed_fns
+ self._delayed_fns = []
+ self._defer_events = False
+ for fn in fns:
+ fn()
+
+ @in_dap_thread
def main_loop(self):
"""The main loop of the DAP server."""
# Before looping, start the thread that writes JSON to the
@@ -372,28 +392,22 @@ class Server:
req = cmd["seq"]
with self.canceller.current_request(req):
self._handle_command(cmd)
- fns = None
- with self._delayed_fns_lock:
- fns = self._delayed_fns
- self._delayed_fns = []
- self.defer_stop_events = False
- for fn in fns:
- fn()
+ self.emit_pending_events()
# Got the terminate request. This is handled by the
# JSON-writing thread, so that we can ensure that all
# responses are flushed to the client before exiting.
self._write_queue.put(None)
json_writer.join()
- send_gdb("quit")
+ send_gdb(lambda: exec_and_log("quit"))
@in_dap_thread
- def send_event_later(self, event, body=None):
- """Send a DAP event back to the client, but only after the
- current request has completed."""
+ def set_defer_events(self):
+ """Defer any events until the current request has completed."""
with self._delayed_fns_lock:
- self._delayed_fns.append(lambda: self.send_event(event, body))
+ self._defer_events = True
- @in_gdb_thread
+ # Note that this does not need to be run in any particular thread,
+ # because it uses locks for thread-safety.
def send_event_maybe_later(self, event, body=None):
"""Send a DAP event back to the client, but if a request is in-flight
within the dap thread and that request is configured to delay the event,
@@ -402,10 +416,10 @@ class Server:
with self.canceller.lock:
if self.canceller.in_flight_dap_thread:
with self._delayed_fns_lock:
- if self.defer_stop_events:
- self._delayed_fns.append(lambda: self.send_event(event, body))
+ if self._defer_events:
+ self._delayed_fns.append(lambda: self._send_event(event, body))
return
- self.send_event(event, body)
+ self._send_event(event, body)
@in_dap_thread
def call_function_later(self, fn):
@@ -416,7 +430,7 @@ class Server:
# Note that this does not need to be run in any particular thread,
# because it just creates an object and writes it to a thread-safe
# queue.
- def send_event(self, event, body=None):
+ def _send_event(self, event, body=None):
"""Send an event to the DAP client.
EVENT is the name of the event, a string.
BODY is the body of the event, an arbitrary object."""
@@ -440,22 +454,11 @@ def send_event(event, body=None):
"""Send an event to the DAP client.
EVENT is the name of the event, a string.
BODY is the body of the event, an arbitrary object."""
- global _server
- _server.send_event(event, body)
-
-
-def send_event_maybe_later(event, body=None):
- """Send a DAP event back to the client, but if a request is in-flight
- within the dap thread and that request is configured to delay the event,
- wait until the response has been sent until the event is sent back to
- the client."""
- global _server
_server.send_event_maybe_later(event, body)
def call_function_later(fn):
"""Call FN later -- after the current request's response has been sent."""
- global _server
_server.call_function_later(fn)
@@ -480,7 +483,7 @@ def request(
response: bool = True,
on_dap_thread: bool = False,
expect_stopped: bool = True,
- defer_stop_events: bool = False
+ defer_events: bool = True
):
"""A decorator for DAP requests.
@@ -502,9 +505,9 @@ def request(
inferior is running. When EXPECT_STOPPED is False, the request
will proceed regardless of the inferior's state.
- If DEFER_STOP_EVENTS is True, then make sure any stop events sent
- during the request processing are not sent to the client until the
- response has been sent.
+ If DEFER_EVENTS is True, then make sure any events sent during the
+ request processing are not sent to the client until the response
+ has been sent.
"""
# Validate the parameters.
@@ -527,27 +530,33 @@ def request(
# Verify that the function is run on the correct thread.
if on_dap_thread:
- cmd = in_dap_thread(func)
+ check_cmd = in_dap_thread(func)
else:
func = in_gdb_thread(func)
if response:
- if defer_stop_events:
- global _server
- if _server is not None:
- with _server.delayed_events_lock:
- _server.defer_stop_events = True
def sync_call(**args):
return send_gdb_with_response(lambda: func(**args))
- cmd = sync_call
+ check_cmd = sync_call
else:
def non_sync_call(**args):
return send_gdb(lambda: func(**args))
- cmd = non_sync_call
+ check_cmd = non_sync_call
+
+ if defer_events:
+
+ def deferring(**args):
+ _server.set_defer_events()
+ return check_cmd(**args)
+
+ cmd = deferring
+
+ else:
+ cmd = check_cmd
# If needed, check that the inferior is not running. This
# wrapping is done last, so the check is done first, before
@@ -555,7 +564,6 @@ def request(
if expect_stopped:
cmd = _check_not_running(cmd)
- global _commands
assert name not in _commands
_commands[name] = cmd
return cmd
@@ -568,7 +576,6 @@ def capability(name, value=True):
the DAP capability NAME."""
def wrap(func):
- global _capabilities
assert name not in _capabilities
_capabilities[name] = value
return func
@@ -581,7 +588,6 @@ def client_bool_capability(name, default=False):
If the capability was not specified, or did not have boolean type,
DEFAULT is returned. DEFAULT defaults to False."""
- global _server
if name in _server.config and isinstance(_server.config[name], bool):
return _server.config[name]
return default
@@ -589,9 +595,8 @@ def client_bool_capability(name, default=False):
@request("initialize", on_dap_thread=True)
def initialize(**args):
- global _server, _capabilities
_server.config = args
- _server.send_event_later("initialized")
+ _server.send_event_maybe_later("initialized")
global _lines_start_at_1
_lines_start_at_1 = client_bool_capability("linesStartAt1", True)
global _columns_start_at_1
@@ -609,7 +614,7 @@ def terminate(**args):
@capability("supportTerminateDebuggee")
def disconnect(*, terminateDebuggee: bool = False, **args):
if terminateDebuggee:
- send_gdb_with_response("kill")
+ send_gdb_with_response(lambda: exec_and_log("kill"))
_server.shutdown()
@@ -628,18 +633,6 @@ def cancel(**args):
return None
-class Invoker(object):
- """A simple class that can invoke a gdb command."""
-
- def __init__(self, cmd):
- self._cmd = cmd
-
- # This is invoked in the gdb thread to run the command.
- @in_gdb_thread
- def __call__(self):
- exec_and_log(self._cmd)
-
-
class Cancellable(object):
def __init__(self, fn, result_q=None):
@@ -672,25 +665,16 @@ class Cancellable(object):
def send_gdb(cmd):
"""Send CMD to the gdb thread.
- CMD can be either a function or a string.
- If it is a string, it is passed to gdb.execute."""
- if isinstance(cmd, str):
- cmd = Invoker(cmd)
-
+ CMD is a function."""
# Post the event and don't wait for the result.
gdb.post_event(Cancellable(cmd))
def send_gdb_with_response(fn):
"""Send FN to the gdb thread and return its result.
- If FN is a string, it is passed to gdb.execute and None is
- returned as the result.
If FN throws an exception, this function will throw the
same exception in the calling thread.
"""
- if isinstance(fn, str):
- fn = Invoker(fn)
-
# Post the event and wait for the result in result_q.
result_q = DAPQueue()
gdb.post_event(Cancellable(fn, result_q))
@@ -705,7 +689,6 @@ def export_line(line: int) -> int:
"""Rewrite LINE according to client capability.
This applies the linesStartAt1 capability as needed,
when sending a line number from gdb to the client."""
- global _lines_start_at_1
if not _lines_start_at_1:
# In gdb, lines start at 1, so we only need to change this if
# the client starts at 0.
@@ -717,7 +700,6 @@ def import_line(line: int) -> int:
"""Rewrite LINE according to client capability.
This applies the linesStartAt1 capability as needed,
when the client sends a line number to gdb."""
- global _lines_start_at_1
if not _lines_start_at_1:
# In gdb, lines start at 1, so we only need to change this if
# the client starts at 0.
diff --git a/gdb/python/lib/gdb/dap/sources.py b/gdb/python/lib/gdb/dap/sources.py
index 938c93a..efcd799 100644
--- a/gdb/python/lib/gdb/dap/sources.py
+++ b/gdb/python/lib/gdb/dap/sources.py
@@ -37,7 +37,6 @@ def make_source(fullname, filename=None):
FILENAME is the base name; if None (the default), then it is
computed from FULLNAME.
"""
- global _source_map
if fullname in _source_map:
result = _source_map[fullname]
else:
@@ -53,7 +52,6 @@ def make_source(fullname, filename=None):
global _next_source
result["sourceReference"] = _next_source
- global _id_map
_id_map[_next_source] = result
_next_source += 1
@@ -66,12 +64,11 @@ def decode_source(source):
"""Decode a Source object.
Finds and returns the filename of a given Source object."""
- if "path" in source:
- return source["path"]
- if "sourceReference" not in source:
+ if "sourceReference" not in source or source["sourceReference"] <= 0:
+ if "path" in source:
+ return source["path"]
raise DAPException("either 'path' or 'sourceReference' must appear in Source")
ref = source["sourceReference"]
- global _id_map
if ref not in _id_map:
raise DAPException("no sourceReference " + str(ref))
return _id_map[ref]["path"]
diff --git a/gdb/python/lib/gdb/dap/startup.py b/gdb/python/lib/gdb/dap/startup.py
index ab3e8fd..0c95ada 100644
--- a/gdb/python/lib/gdb/dap/startup.py
+++ b/gdb/python/lib/gdb/dap/startup.py
@@ -204,7 +204,7 @@ def log_stack(level=LogLevel.DEFAULT):
@in_gdb_thread
-def exec_and_log(cmd, propagate_exception=False):
+def exec_and_log(cmd):
"""Execute the gdb command CMD.
If logging is enabled, log the command and its output."""
log("+++ " + cmd)
@@ -213,10 +213,10 @@ def exec_and_log(cmd, propagate_exception=False):
if output != "":
log(">>> " + output)
except gdb.error as e:
- if propagate_exception:
- raise DAPException(str(e)) from e
- else:
- log_stack()
+ # Don't normally want to see this, as it interferes with the
+ # test suite.
+ log_stack(LogLevel.FULL)
+ raise DAPException(str(e)) from e
@in_gdb_thread
diff --git a/gdb/python/lib/gdb/dap/threads.py b/gdb/python/lib/gdb/dap/threads.py
index c271961..89046a8 100644
--- a/gdb/python/lib/gdb/dap/threads.py
+++ b/gdb/python/lib/gdb/dap/threads.py
@@ -16,27 +16,32 @@
import gdb
from .server import request
+from .startup import in_gdb_thread
+@in_gdb_thread
def _thread_name(thr):
if thr.name is not None:
return thr.name
if thr.details is not None:
return thr.details
- return None
+ # Always return a name, as the protocol doesn't allow for nameless
+ # threads. Use the local thread number here... it doesn't matter
+ # without multi-inferior but in that case it might make more
+ # sense.
+ return f"Thread #{thr.num}"
-@request("threads")
+@request("threads", expect_stopped=False)
def threads(**args):
result = []
for thr in gdb.selected_inferior().threads():
- one_result = {
- "id": thr.global_num,
- }
- name = _thread_name(thr)
- if name is not None:
- one_result["name"] = name
- result.append(one_result)
+ result.append(
+ {
+ "id": thr.global_num,
+ "name": _thread_name(thr),
+ }
+ )
return {
"threads": result,
}
diff --git a/gdb/python/lib/gdb/dap/varref.py b/gdb/python/lib/gdb/dap/varref.py
index 42b5302..d18197b 100644
--- a/gdb/python/lib/gdb/dap/varref.py
+++ b/gdb/python/lib/gdb/dap/varref.py
@@ -69,7 +69,6 @@ class BaseReference(ABC):
NAME is a string or None. None means this does not have a
name, e.g., the result of expression evaluation."""
- global all_variables
all_variables.append(self)
self._ref = len(all_variables)
self._name = name
@@ -147,6 +146,10 @@ class BaseReference(ABC):
if self._children is None:
self._children = [None] * self.child_count()
for idx in range(start, start + count):
+ if idx >= len(self._children):
+ raise DAPException(
+ f"requested child {idx} outside range of variable {self._ref}"
+ )
if self._children[idx] is None:
(name, value) = self.fetch_one_child(idx)
name = self._compute_name(name)
@@ -243,7 +246,11 @@ class VariableReference(BaseReference):
# changed DAP to allow memory references for any of the
# variable response requests, and to lift the restriction
# to pointer-to-function from Variable.
- if self._value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR:
+ if (
+ self._value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR
+ and not self._value.is_optimized_out
+ and not self._value.is_unavailable
+ ):
result["memoryReference"] = hex(int(self._value))
if client_bool_capability("supportsVariableType"):
result["type"] = str(self._value.type)
@@ -267,7 +274,7 @@ class VariableReference(BaseReference):
@in_gdb_thread
def find_variable(ref):
"""Given a variable reference, return the corresponding variable object."""
- global all_variables
+
# Variable references are offset by 1.
ref = ref - 1
if ref < 0 or ref > len(all_variables):
diff --git a/gdb/python/lib/gdb/disassembler.py b/gdb/python/lib/gdb/disassembler.py
index 8f8e768..5956678 100644
--- a/gdb/python/lib/gdb/disassembler.py
+++ b/gdb/python/lib/gdb/disassembler.py
@@ -155,9 +155,21 @@ class maint_info_py_disassemblers_cmd(gdb.Command):
# Now print the dictionary of registered disassemblers out to
# the user.
match_tag = "\t(Matches current architecture)"
- fmt_len = max(longest_arch_name, len("Architecture"))
- format_string = "{:" + str(fmt_len) + "s} {:s}"
- print(format_string.format("Architecture", "Disassember Name"))
+ arch_title = "Architecture"
+ fmt_len = max(longest_arch_name, len(arch_title))
+ format_string = "{:" + str(fmt_len) + "s} {:s}"
+ padding_string = " " * (fmt_len - len(arch_title))
+ title_style = gdb.Style("title")
+ # We cannot use FORMAT_STRING to layout the title line, as
+ # Python is unable to calculate the length of a styled string.
+ # Instead use PADDING_STRING to manually layout the columns.
+ print(
+ "{:s}{:s} {:s}".format(
+ title_style.apply(arch_title),
+ padding_string,
+ title_style.apply("Disassember Name"),
+ )
+ )
for architecture in _disassemblers_dict:
if architecture is not None:
name = _disassemblers_dict[architecture].name
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index cba27d2..f1ac19d 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -415,10 +415,21 @@ def make_visualizer(value):
result = NoOpArrayPrinter(ty, value)
elif ty.code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION):
result = NoOpStructPrinter(ty, value)
- elif ty.code in (
- gdb.TYPE_CODE_PTR,
- gdb.TYPE_CODE_REF,
- gdb.TYPE_CODE_RVALUE_REF,
+ elif (
+ ty.code
+ in (
+ gdb.TYPE_CODE_PTR,
+ gdb.TYPE_CODE_REF,
+ gdb.TYPE_CODE_RVALUE_REF,
+ )
+ # Avoid "void *" here because those pointers can't be
+ # dereferenced without a cast.
+ and ty.target().code != gdb.TYPE_CODE_VOID
+ # An optimized-out or unavailable pointer should just be
+ # treated as a scalar, since there's no way to dereference
+ # it.
+ and not value.is_optimized_out
+ and not value.is_unavailable
):
result = NoOpPointerReferencePrinter(value)
else:
diff --git a/gdb/python/lib/gdb/styling.py b/gdb/python/lib/gdb/styling.py
index e9387e3..60c470f 100644
--- a/gdb/python/lib/gdb/styling.py
+++ b/gdb/python/lib/gdb/styling.py
@@ -80,7 +80,6 @@ try:
# ignore.
pass
- global _asm_lexers
if lexer_type not in _asm_lexers:
_asm_lexers[lexer_type] = lexers.get_lexer_by_name(lexer_type)
_asm_lexers[lexer_type].add_filter(HandleNasmComments())