aboutsummaryrefslogtreecommitdiff
path: root/gdb/python
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python')
-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
-rw-r--r--gdb/python/py-block.c3
-rw-r--r--gdb/python/py-breakpoint.c6
-rw-r--r--gdb/python/py-cmd.c108
-rw-r--r--gdb/python/py-color.c77
-rw-r--r--gdb/python/py-connection.c4
-rw-r--r--gdb/python/py-corefile.c684
-rw-r--r--gdb/python/py-dap.c4
-rw-r--r--gdb/python/py-disasm.c96
-rw-r--r--gdb/python/py-event-types.def2
-rw-r--r--gdb/python/py-finishbreakpoint.c6
-rw-r--r--gdb/python/py-framefilter.c2
-rw-r--r--gdb/python/py-gdb-readline.c4
-rw-r--r--gdb/python/py-inferior.c18
-rw-r--r--gdb/python/py-infevents.c2
-rw-r--r--gdb/python/py-infthread.c33
-rw-r--r--gdb/python/py-mi.c6
-rw-r--r--gdb/python/py-micmd.c8
-rw-r--r--gdb/python/py-objfile.c12
-rw-r--r--gdb/python/py-param.c18
-rw-r--r--gdb/python/py-prettyprint.c4
-rw-r--r--gdb/python/py-progspace.c4
-rw-r--r--gdb/python/py-record.c3
-rw-r--r--gdb/python/py-registers.c3
-rw-r--r--gdb/python/py-style.c818
-rw-r--r--gdb/python/py-symbol.c46
-rw-r--r--gdb/python/py-type.c23
-rw-r--r--gdb/python/py-unwind.c12
-rw-r--r--gdb/python/py-value.c85
-rw-r--r--gdb/python/py-xmethods.c4
-rw-r--r--gdb/python/python-internal.h45
-rw-r--r--gdb/python/python.c115
54 files changed, 2545 insertions, 463 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())
diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c
index fa7dd19..66ccad7 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -356,6 +356,9 @@ block_to_block_object (const struct block *block, struct objfile *objfile)
}
result = PyObject_New (block_object, &block_object_type);
+ if (result == nullptr)
+ return nullptr;
+
result->block = block;
result->objfile = objfile;
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 861e9a3..9ce8671 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -948,7 +948,7 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
else
{
PyErr_SetString (PyExc_RuntimeError,
- _("Line keyword should be an integer or a string. "));
+ _("Line keyword should be an integer or a string."));
return -1;
}
}
@@ -1537,9 +1537,7 @@ PyTypeObject breakpoint_object_type =
0, /* tp_alloc */
};
-void _initialize_py_breakpoint ();
-void
-_initialize_py_breakpoint ()
+INIT_GDB_FILE (py_breakpoint)
{
add_setshow_boolean_cmd
("py-breakpoint", class_maintenance, &pybp_debug,
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index 5d98d03..dc5e270 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -105,19 +105,17 @@ cmdpy_function (const char *args, int from_tty, cmd_list_element *command)
gdbpy_enter enter_py;
- if (! obj)
+ if (obj == nullptr)
error (_("Invalid invocation of Python command object."));
- if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
- {
- if (obj->command->is_prefix ())
- {
- /* A prefix command does not need an invoke method. */
- return;
- }
- error (_("Python command object missing 'invoke' method."));
- }
- if (! args)
+ /* If we get here for a prefix command then the prefix command had an
+ 'invoke' method when it was created. If the 'invoke' method is now
+ missing, then the user has done something weird (like deleting the
+ invoke method, yuck!). */
+ if (!PyObject_HasAttr ((PyObject *) obj, invoke_cst))
+ error (_("Python command object missing 'invoke' method."));
+
+ if (args == nullptr)
args = "";
gdbpy_ref<> argobj (PyUnicode_Decode (args, strlen (args), host_charset (),
NULL));
@@ -336,24 +334,30 @@ cmdpy_completer (struct cmd_list_element *command,
name of the new command. All earlier words must be existing prefix
commands.
- *BASE_LIST is set to the final prefix command's list of
- *sub-commands.
+ *BASE_LIST is set to the final prefix command's list of sub-commands.
START_LIST is the list in which the search starts.
+ When PREFIX_CMD is not NULL then *PREFIX_CMD is set to the prefix
+ command itself, or NULL, if there is no prefix command.
+
This function returns the name of the new command. On error sets the Python
error and returns NULL. */
gdb::unique_xmalloc_ptr<char>
gdbpy_parse_command_name (const char *name,
struct cmd_list_element ***base_list,
- struct cmd_list_element **start_list)
+ struct cmd_list_element **start_list,
+ struct cmd_list_element **prefix_cmd)
{
struct cmd_list_element *elt;
int len = strlen (name);
int i, lastchar;
const char *prefix_text2;
+ if (prefix_cmd != nullptr)
+ *prefix_cmd = nullptr;
+
/* Skip trailing whitespace. */
for (i = len - 1; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i)
;
@@ -368,9 +372,8 @@ gdbpy_parse_command_name (const char *name,
for (; i > 0 && valid_cmd_char_p (name[i - 1]); --i)
;
- gdb::unique_xmalloc_ptr<char> result ((char *) xmalloc (lastchar - i + 2));
- memcpy (result.get (), &name[i], lastchar - i + 1);
- result.get ()[lastchar - i + 1] = '\0';
+ gdb::unique_xmalloc_ptr<char> result
+ = make_unique_xstrndup (&name[i], lastchar - i + 1);
/* Skip whitespace again. */
for (--i; i >= 0 && (name[i] == ' ' || name[i] == '\t'); --i)
@@ -385,7 +388,7 @@ gdbpy_parse_command_name (const char *name,
prefix_text2 = prefix_text.c_str ();
elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1);
- if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
+ if (elt == nullptr || elt == CMD_LIST_AMBIGUOUS || *prefix_text2 != '\0')
{
PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."),
prefix_text.c_str ());
@@ -395,6 +398,8 @@ gdbpy_parse_command_name (const char *name,
if (elt->is_prefix ())
{
*base_list = elt->subcommands;
+ if (prefix_cmd != nullptr)
+ *prefix_cmd = elt;
return result;
}
@@ -469,8 +474,9 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
return -1;
}
+ cmd_list_element *prefix_cmd = nullptr;
gdb::unique_xmalloc_ptr<char> cmd_name
- = gdbpy_parse_command_name (name, &cmd_list, &cmdlist);
+ = gdbpy_parse_command_name (name, &cmd_list, &cmdlist, &prefix_cmd);
if (cmd_name == nullptr)
return -1;
@@ -507,26 +513,64 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
if (is_prefix)
{
- int allow_unknown;
-
- /* If we have our own "invoke" method, then allow unknown
- sub-commands. */
- allow_unknown = PyObject_HasAttr (self, invoke_cst);
- cmd = add_prefix_cmd (cmd_name.get (),
- (enum command_class) cmdtype,
- NULL, docstring.release (), &obj->sub_list,
- allow_unknown, cmd_list);
+ bool has_invoke = PyObject_HasAttr (self, invoke_cst) == 1;
+ if (has_invoke)
+ {
+ /* If there's an 'invoke' method, then create the prefix
+ command, but call cmdpy_function to dispatch to the invoke
+ method when the user runs the prefix with no sub-command. */
+ cmd = add_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ nullptr,
+ docstring.release (), &obj->sub_list,
+ 1 /* allow_unknown */, cmd_list);
+ cmd->func = cmdpy_function;
+ }
+ else
+ {
+ /* If there is no 'invoke' method, then create the prefix
+ using the standard prefix callbacks. This means that for
+ 'set prefix' the user will get the help text listing all
+ of the sub-commands, and for 'show prefix', the user will
+ see all of the sub-command values. */
+ if (prefix_cmd != nullptr)
+ {
+ while (prefix_cmd->prefix != nullptr)
+ prefix_cmd = prefix_cmd->prefix;
+ }
+
+ bool is_show = (prefix_cmd != nullptr
+ && prefix_cmd->subcommands == &showlist);
+
+ if (is_show)
+ cmd = add_show_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ docstring.release (),
+ &obj->sub_list,
+ 0 /* allow_unknown */, cmd_list);
+ else
+ cmd = add_basic_prefix_cmd (cmd_name.get (),
+ (enum command_class) cmdtype,
+ docstring.release (),
+ &obj->sub_list,
+ 0 /* allow_unknown */, cmd_list);
+ }
}
else
- cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
- docstring.release (), cmd_list);
+ {
+ /* For non-prefix commands, arrange to call cmdpy_function, which
+ invokes the Python 'invoke' method, or raises an exception if
+ the 'invoke' method is missing. */
+ cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
+ docstring.release (), cmd_list);
+ cmd->func = cmdpy_function;
+ }
/* If successful, the above takes ownership of the name, since we set
name_allocated, so release it. */
cmd_name.release ();
- /* There appears to be no API to set this. */
- cmd->func = cmdpy_function;
+ /* There appears to be no API to set these member variables. */
cmd->destroyer = cmdpy_destroyer;
cmd->doc_allocated = 1;
cmd->name_allocated = 1;
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
index 91ad7ae..3bbd22d 100644
--- a/gdb/python/py-color.c
+++ b/gdb/python/py-color.c
@@ -21,6 +21,7 @@
#include "python-internal.h"
#include "py-color.h"
#include "cli/cli-decode.h"
+#include "cli/cli-style.h"
/* Colorspace constants and their values. */
static struct {
@@ -64,7 +65,8 @@ create_color_object (const ui_file_style::color &color)
bool
gdbpy_is_color (PyObject *obj)
{
- return PyObject_IsInstance (obj, (PyObject *) &colorpy_object_type);
+ gdb_assert (obj != nullptr);
+ return PyObject_TypeCheck (obj, &colorpy_object_type) != 0;
}
/* See py-color.h. */
@@ -80,33 +82,33 @@ gdbpy_get_color (PyObject *obj)
static PyObject *
get_attr (PyObject *obj, PyObject *attr_name)
{
- if (! PyUnicode_Check (attr_name))
+ if (!PyUnicode_Check (attr_name))
return PyObject_GenericGetAttr (obj, attr_name);
colorpy_object *self = (colorpy_object *) obj;
const ui_file_style::color &color = self->color;
- if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
{
int value = static_cast<int> (color.colorspace ());
return gdb_py_object_from_longest (value).release ();
}
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
return PyBool_FromLong (color.is_none ());
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
return PyBool_FromLong (color.is_indexed ());
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
return PyBool_FromLong (color.is_direct ());
if (color.is_indexed ()
- && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+ && !PyUnicode_CompareWithASCIIString (attr_name, "index"))
return gdb_py_object_from_longest (color.get_value ()).release ();
if (color.is_direct ()
- && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+ && !PyUnicode_CompareWithASCIIString (attr_name, "components"))
{
uint8_t rgb[3];
color.get_rgb (rgb);
@@ -135,25 +137,29 @@ get_attr (PyObject *obj, PyObject *attr_name)
/* Implementation of Color.escape_sequence (self, is_fg) -> str. */
static PyObject *
-colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
+colorpy_escape_sequence (PyObject *self, PyObject *args, PyObject *kwargs)
{
- if (!gdbpy_is_color (self))
- {
- PyErr_SetString (PyExc_RuntimeError,
- _("Object is not gdb.Color."));
- return nullptr;
- }
+ static const char *keywords[] = { "is_foreground", nullptr };
+ PyObject *is_fg_obj;
+
+ /* Parse method arguments. */
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "O!", keywords,
+ &PyBool_Type, &is_fg_obj))
+ return nullptr;
- if (! PyBool_Check (is_fg_obj))
+ /* Python ensures the type of SELF. */
+ gdb_assert (gdbpy_is_color (self));
+
+ /* The argument parsing ensures we have a bool. */
+ gdb_assert (PyBool_Check (is_fg_obj));
+
+ std::string s;
+ if (term_cli_styling ())
{
- PyErr_SetString (PyExc_RuntimeError,
- _("A boolean argument is required."));
- return nullptr;
+ bool is_fg = is_fg_obj == Py_True;
+ s = gdbpy_get_color (self).to_ansi (is_fg);
}
- bool is_fg = is_fg_obj == Py_True;
- std::string s = gdbpy_get_color (self).to_ansi (is_fg);
-
return host_string_to_python_string (s.c_str ()).release ();
}
@@ -175,17 +181,20 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
PyObject *colorspace_obj = nullptr;
color_space colorspace = color_space::MONOCHROME;
- if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+ static const char *keywords[] = { "value", "color_space", nullptr };
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kwds, "|OO", keywords,
+ &value_obj, &colorspace_obj))
return -1;
try
{
- if (colorspace_obj)
+ if (colorspace_obj != nullptr)
{
if (PyLong_Check (colorspace_obj))
{
long colorspace_id = -1;
- if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+ if (!gdb_py_int_as_long (colorspace_obj, &colorspace_id))
return -1;
if (!color_space_safe_cast (&colorspace, colorspace_id))
error (_("colorspace %ld is out of range."), colorspace_id);
@@ -201,11 +210,11 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
else if (PyLong_Check (value_obj))
{
long value = -1;
- if (! gdb_py_int_as_long (value_obj, &value))
+ if (!gdb_py_int_as_long (value_obj, &value))
return -1;
if (value < 0 || value > INT_MAX)
error (_("value %ld is out of range."), value);
- if (colorspace_obj)
+ if (colorspace_obj != nullptr)
obj->color = ui_file_style::color (colorspace, value);
else
obj->color = ui_file_style::color (value);
@@ -256,7 +265,6 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
return gdbpy_handle_gdb_exception (-1, except);
}
- Py_INCREF (self);
return 0;
}
@@ -272,10 +280,10 @@ colorpy_str (PyObject *self)
static int
gdbpy_initialize_color (void)
{
- for (auto & pair : colorspace_constants)
- if (PyModule_AddIntConstant (gdb_module, pair.name,
- static_cast<long> (pair.value)) < 0)
- return -1;
+ for (auto &pair : colorspace_constants)
+ if (PyModule_AddIntConstant (gdb_module, pair.name,
+ static_cast<long> (pair.value)) < 0)
+ return -1;
colorpy_object_type.tp_new = PyType_GenericNew;
return gdbpy_type_ready (&colorpy_object_type, gdb_module);
@@ -285,7 +293,8 @@ gdbpy_initialize_color (void)
static PyMethodDef color_methods[] =
{
- { "escape_sequence", colorpy_escape_sequence, METH_O,
+ { "escape_sequence", (PyCFunction) colorpy_escape_sequence,
+ METH_VARARGS | METH_KEYWORDS,
"escape_sequence (is_foreground) -> str.\n\
Return the ANSI escape sequence for this color.\n\
IS_FOREGROUND indicates whether this is a foreground or background color."},
@@ -313,7 +322,7 @@ PyTypeObject colorpy_object_type =
get_attr, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB color object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c
index 93757ac..a6d9ad0 100644
--- a/gdb/python/py-connection.c
+++ b/gdb/python/py-connection.c
@@ -428,9 +428,7 @@ connpy_send_packet (PyObject *self, PyObject *args, PyObject *kw)
/* Global initialization for this file. */
-void _initialize_py_connection ();
-void
-_initialize_py_connection ()
+INIT_GDB_FILE (py_connection)
{
gdb::observers::connection_removed.attach (connpy_connection_removed,
"py-connection");
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
new file mode 100644
index 0000000..97f4242
--- /dev/null
+++ b/gdb/python/py-corefile.c
@@ -0,0 +1,684 @@
+/* Python interface to core files.
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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 "python-internal.h"
+#include "progspace.h"
+#include "observable.h"
+#include "inferior.h"
+#include "gdbcore.h"
+#include "gdbsupport/rsp-low.h"
+
+/* A gdb.Corefile object. */
+
+struct corefile_object
+{
+ PyObject_HEAD
+
+ /* The inferior this core file is attached to. This will be set to NULL
+ when the inferior is deleted, or if a different core file is loaded
+ for the inferior. When this is NULL the gdb.Corefile object is
+ considered invalid.*/
+ struct inferior *inferior;
+
+ /* Dictionary holding user-added attributes. This is the __dict__
+ attribute of the object. This is an owning reference. */
+ PyObject *dict;
+
+ /* A Tuple of gdb.CorefileMappedFile objects. This tuple is only created
+ the first time the user calls gdb.Corefile.mapped_files(), the result
+ is cached here. If this pointer is not NULL then this is an owning
+ pointer (i.e. this owns a reference to the Tuple). */
+ PyObject *mapped_files;
+};
+
+extern PyTypeObject corefile_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_object");
+
+/* A gdb.CorefileMapped object. */
+
+struct corefile_mapped_file_object
+{
+ PyObject_HEAD
+
+ /* The name of a file that was mapped when the core file was created.
+ This is a 'str' object. */
+ PyObject *filename;
+
+ /* The build-id of a file that was mapped when the core file was
+ created. This is either a 'str' if the file had a build-id, or
+ 'None' if there was no build-id for this file. */
+ PyObject *build_id;
+
+ /* A List of gdb.CorefileMappedFileRegion objects. */
+ PyObject *regions;
+
+ /* True if this represents the main executable from which the core file
+ was created. */
+ bool is_main_exec_p;
+};
+
+extern PyTypeObject corefile_mapped_file_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_object");
+
+/* A gdb.CorefileMappedFileRegion object. */
+
+struct corefile_mapped_file_region_object
+{
+ PyObject_HEAD
+
+ /* The start and end addresses for this mapping, these are addresses
+ within the inferior's address space. */
+ CORE_ADDR start;
+ CORE_ADDR end;
+
+ /* The offset within the mapped file for this mapping. */
+ ULONGEST file_offset;
+};
+
+extern PyTypeObject corefile_mapped_file_region_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_region_object");
+
+/* Clear the inferior pointer in a Corefile object OBJ when an inferior is
+ deleted. */
+
+struct inferior_corefile_deleter
+{
+ void operator() (corefile_object *obj)
+ {
+ if (!gdb_python_initialized)
+ return;
+
+ gdbpy_enter enter_py;
+
+ /* When OBJECT goes out of scope this will Py_DECREF on OBJ. */
+ gdbpy_ref<corefile_object> object (obj);
+
+ /* Clearing the inferior pointer marks the gdb.Corefile as invalid. */
+ object->inferior = nullptr;
+ }
+};
+
+/* Store a gdb.Corefile object in an inferior's registry. */
+
+static const registry<inferior>::key<corefile_object,
+ inferior_corefile_deleter>
+ cfpy_inferior_corefile_data_key;
+
+/* See python-internal.h. */
+
+gdbpy_ref<>
+gdbpy_core_file_from_inferior (inferior *inf)
+{
+ gdb_assert (inf != nullptr);
+ gdb_assert (inf->pspace != nullptr);
+
+ if (get_inferior_core_bfd (inf) == nullptr)
+ return gdbpy_ref<>::new_reference (Py_None);
+
+ PyObject *result = (PyObject *) cfpy_inferior_corefile_data_key.get (inf);
+ if (result == nullptr)
+ {
+ gdbpy_ref<corefile_object> object
+ (PyObject_New (corefile_object, &corefile_object_type));
+ if (object == nullptr)
+ return nullptr;
+
+ /* Ensure the 'inferior' field is set to NULL. If the PyDict_New
+ call fails then the gdb.Corefile will be discarded and
+ cfpy_dealloc will be called, which requires that the 'inferior' be
+ set to NULL. */
+ object->inferior = nullptr;
+ object->mapped_files = nullptr;
+ object->dict = PyDict_New ();
+ if (object->dict == nullptr)
+ return nullptr;
+
+ /* Now that the gdb.Corefile has been successfully initialised and we
+ know that it is going to be passed back to the user, move it out
+ of the invalid state by setting the 'inferior' field to a non NULL
+ value. */
+ object->inferior = inf;
+ cfpy_inferior_corefile_data_key.set (inf, object.get ());
+ result = (PyObject *) object.release ();
+ }
+
+ return gdbpy_ref<>::new_reference (result);
+}
+
+/* Return true if OBJ is valid. */
+
+static bool
+cfpy_corefile_object_is_valid (const corefile_object *obj)
+{
+ if (obj->inferior == nullptr)
+ return false;
+
+ return get_inferior_core_bfd (obj->inferior) != nullptr;
+}
+
+/* Require that COREFILE_OBJ be a valid core file. A valid core file
+ object has a valid program space, and the program space has a core file
+ loaded into it. */
+#define CFPY_REQUIRE_VALID(corefile_obj) \
+ do { \
+ if (!cfpy_corefile_object_is_valid (corefile_obj)) \
+ { \
+ PyErr_SetString (PyExc_RuntimeError, \
+ _("Corefile no longer exists.")); \
+ return nullptr; \
+ } \
+ } while (0)
+
+/* Read the gdb.Corefile.filename attribute. */
+
+static PyObject *
+cfpy_get_filename (PyObject *self, void *closure)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ CFPY_REQUIRE_VALID (obj);
+
+ /* If the program space's core file had been cleared, then this Corefile
+ object would have been invalidated. */
+ bfd *abfd = get_inferior_core_bfd (obj->inferior);
+ gdb_assert (abfd != nullptr);
+
+ return host_string_to_python_string (bfd_get_filename (abfd)).release ();
+}
+
+/* Implementation of gdb.Corefile.is_valid (self) -> Boolean.
+ Returns True if this core file object is associated with a program space
+ that still exists, an the program space still has a core file loaded. */
+
+static PyObject *
+cfpy_is_valid (PyObject *self, PyObject *args)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ Py_RETURN_FALSE;
+
+ Py_RETURN_TRUE;
+}
+
+/* Implement gdb.Corefile.mapped_files (). Return a List of
+ gdb.CorefileMappedFile objects. The list is created the first time
+ this method is called, and then cached within the gdb.Corefile object,
+ future calls just return a reference to the same list. */
+
+static PyObject *
+cfpy_mapped_files (PyObject *self, PyObject *args)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ CFPY_REQUIRE_VALID (obj);
+
+ /* If we have already created the List then just return another reference
+ to the existing list. */
+ if (obj->mapped_files != nullptr)
+ {
+ Py_INCREF (obj->mapped_files);
+ return obj->mapped_files;
+ }
+
+ /* Get all the mapping data from GDB. */
+ std::vector<core_mapped_file> mapped_files;
+ try
+ {
+ mapped_files
+ = gdb_read_core_file_mappings (obj->inferior->arch (),
+ get_inferior_core_bfd (obj->inferior));
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+
+ /* Create a new list to hold the results. */
+ gdbpy_ref<> tuple (PyTuple_New (mapped_files.size ()));
+ if (tuple == nullptr)
+ return nullptr;
+
+ /* Create each gdb.CorefileMappedFile object. */
+ Py_ssize_t tuple_idx = 0;
+ for (const core_mapped_file &file : mapped_files)
+ {
+ /* The filename 'str' object. */
+ gdbpy_ref<> filename
+ = host_string_to_python_string (file.filename.c_str ());
+ if (filename == nullptr)
+ return nullptr;
+
+ /* The build-id object. Either a 'str' or 'None'. */
+ gdbpy_ref<> build_id;
+ if (file.build_id != nullptr)
+ {
+ std::string hex_form = bin2hex (file.build_id->data,
+ file.build_id->size);
+
+ build_id
+ = host_string_to_python_string (hex_form.c_str ());
+ if (build_id == nullptr)
+ return nullptr;
+ }
+ else
+ build_id = gdbpy_ref<>::new_reference (Py_None);
+
+ /* List to hold all the gdb.CorefileMappedFileRegion objects. */
+ gdbpy_ref<> regions (PyTuple_New (file.regions.size ()));
+ if (regions == nullptr)
+ return nullptr;
+
+ /* Create all the gdb.CorefileMappedFileRegion objects. */
+ Py_ssize_t regions_idx = 0;
+ for (const core_mapped_file::region &r : file.regions)
+ {
+ /* Actually create the object. */
+ gdbpy_ref<corefile_mapped_file_region_object> region_obj
+ (PyObject_New (corefile_mapped_file_region_object,
+ &corefile_mapped_file_region_object_type));
+ if (region_obj == nullptr)
+ return nullptr;
+
+ /* Initialise the object. */
+ region_obj->start = r.start;
+ region_obj->end = r.end;
+ region_obj->file_offset = r.file_ofs;
+
+ /* Add to the gdb.CorefileMappedFileRegion list. */
+ if (PyTuple_SetItem (regions.get (), regions_idx++,
+ (PyObject *) region_obj.release ()) < 0)
+ return nullptr;
+ }
+
+ /* Actually create the gdb.CorefileMappedFile object. */
+ gdbpy_ref<corefile_mapped_file_object> entry
+ (PyObject_New (corefile_mapped_file_object,
+ &corefile_mapped_file_object_type));
+ if (entry == nullptr)
+ return nullptr;
+
+ /* Initialise the object. */
+ entry->filename = filename.release ();
+ entry->build_id = build_id.release ();
+ entry->regions = regions.release ();
+ entry->is_main_exec_p = file.is_main_exec;
+
+ /* Add to the gdb.CorefileMappedFile list. */
+ if (PyTuple_SetItem (tuple.get (), tuple_idx++,
+ (PyObject *) entry.release ()) < 0)
+ return nullptr;
+ }
+
+ /* No errors. Move the reference currently in LIST into the Corefile
+ object itself. Then create a new reference and hand this back to the
+ user. */
+ obj->mapped_files = tuple.release ();
+ Py_INCREF (obj->mapped_files);
+ return obj->mapped_files;
+}
+
+/* Callback from gdb::observers::core_file_changed. The core file in
+ PSPACE has been changed. */
+
+static void
+cfpy_corefile_changed (inferior *inf)
+{
+ cfpy_inferior_corefile_data_key.clear (inf);
+}
+
+/* Called when a gdb.Corefile is destroyed. */
+
+static void
+cfpy_dealloc (PyObject *obj)
+{
+ corefile_object *corefile = (corefile_object *) obj;
+
+ /* Every gdb.Corefile is cached in an inferior's registry. The only way
+ for a gdb.Corefile to be deallocated is to remove the object reference
+ from the registry (and dec its ref count), but before we do that, we
+ set the object's inferior pointer to NULL. */
+ gdb_assert (corefile->inferior == nullptr);
+
+ Py_XDECREF (corefile->dict);
+ Py_XDECREF (corefile->mapped_files);
+
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* __repr__ implementation for gdb.Corefile. */
+
+static PyObject *
+cfpy_repr (PyObject *self)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ return gdb_py_invalid_object_repr (self);
+
+ bfd *core_bfd = get_inferior_core_bfd (obj->inferior);
+ gdb_assert (core_bfd != nullptr);
+ return PyUnicode_FromFormat ("<%s inferior=%d filename='%s'>",
+ Py_TYPE (self)->tp_name,
+ obj->inferior->num,
+ bfd_get_filename (core_bfd));
+}
+
+
+
+/* Called when a gdb.CorefileMappedFile is destroyed. */
+
+static void
+cfmfpy_dealloc (PyObject *obj)
+{
+ corefile_mapped_file_object *mapped_file
+ = (corefile_mapped_file_object *) obj;
+
+ Py_XDECREF (mapped_file->filename);
+ Py_XDECREF (mapped_file->build_id);
+ Py_XDECREF (mapped_file->regions);
+
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* Read the gdb.CorefileMappedFile.filename attribute. */
+
+static PyObject *
+cfmfpy_get_filename (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ gdb_assert (obj->filename != nullptr);
+
+ Py_INCREF (obj->filename);
+ return obj->filename;
+}
+
+/* Read the gdb.CorefileMappedFile.build_id attribute. */
+
+static PyObject *
+cfmfpy_get_build_id (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ gdb_assert (obj->build_id != nullptr);
+
+ Py_INCREF (obj->build_id);
+ return obj->build_id;
+}
+
+/* Read the gdb.CorefileMappedFile.regions attribute. */
+
+static PyObject *
+cfmfpy_get_regions (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ gdb_assert (obj->regions != nullptr);
+
+ Py_INCREF (obj->regions);
+ return obj->regions;
+}
+
+/* Read the gdb.CorefileMappedFile.is_main_executable attribute. */
+
+static PyObject *
+cfmf_is_main_exec (PyObject *self, void *closure)
+{
+ corefile_mapped_file_object *obj
+ = (corefile_mapped_file_object *) self;
+
+ if (obj->is_main_exec_p)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+}
+
+
+
+/* Read the gdb.CorefileMappedFileRegion.start attribute. */
+
+static PyObject *
+cfmfrpy_get_start (PyObject *self, void *closure)
+{
+ corefile_mapped_file_region_object *obj
+ = (corefile_mapped_file_region_object *) self;
+
+ return gdb_py_object_from_ulongest (obj->start).release ();
+}
+
+/* Read the gdb.CorefileMappedFileRegion.end attribute. */
+
+static PyObject *
+cfmfrpy_get_end (PyObject *self, void *closure)
+{
+ corefile_mapped_file_region_object *obj
+ = (corefile_mapped_file_region_object *) self;
+
+ return gdb_py_object_from_ulongest (obj->end).release ();
+}
+
+/* Read the gdb.CorefileMappedFileRegion.file_offset attribute. */
+
+static PyObject *
+cfmfrpy_get_file_offset (PyObject *self, void *closure)
+{
+ corefile_mapped_file_region_object *obj
+ = (corefile_mapped_file_region_object *) self;
+
+ return gdb_py_object_from_ulongest (obj->file_offset).release ();
+}
+
+
+
+static int
+gdbpy_initialize_corefile ()
+{
+ gdb::observers::core_file_changed.attach (cfpy_corefile_changed,
+ "py-corefile");
+
+ if (gdbpy_type_ready (&corefile_object_type) < 0)
+ return -1;
+
+ if (gdbpy_type_ready (&corefile_mapped_file_object_type) < 0)
+ return -1;
+
+ if (gdbpy_type_ready (&corefile_mapped_file_region_object_type) < 0)
+ return -1;
+
+ return 0;
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile);
+
+
+
+static gdb_PyGetSetDef corefile_getset[] =
+{
+ { "__dict__", gdb_py_generic_dict, nullptr,
+ "The __dict__ for the gdb.Corefile.", &corefile_object_type },
+ { "filename", cfpy_get_filename, nullptr,
+ "The filename of a valid Corefile object.", nullptr },
+ { nullptr }
+};
+
+static PyMethodDef corefile_object_methods[] =
+{
+ { "is_valid", cfpy_is_valid, METH_NOARGS,
+ "is_valid () -> Boolean.\n\
+Return true if this Corefile is valid, false if not." },
+ { "mapped_files", cfpy_mapped_files, METH_NOARGS,
+ "mapped_files () -> List of mapping tuples.\n\
+Return a list of tuples. Each tuple represents a mapping from the\
+core file." },
+ { nullptr }
+};
+
+PyTypeObject corefile_object_type =
+{
+ PyVarObject_HEAD_INIT (nullptr, 0)
+ "gdb.Corefile", /*tp_name*/
+ sizeof (corefile_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ cfpy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ cfpy_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB corefile object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ corefile_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ corefile_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ offsetof (corefile_object, dict), /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+static gdb_PyGetSetDef corefile_mapped_file_object_getset[] =
+{
+ { "filename", cfmfpy_get_filename, nullptr,
+ "The filename of a CorefileMappedFile object.", nullptr },
+ { "build_id", cfmfpy_get_build_id, nullptr,
+ "The build-id of a CorefileMappedFile object or None.", nullptr },
+ { "regions", cfmfpy_get_regions, nullptr,
+ "The list of regions from a CorefileMappedFile object.", nullptr },
+ { "is_main_executable", cfmf_is_main_exec, nullptr,
+ "True for the main executable mapping, otherwise False.", nullptr },
+ { nullptr }
+};
+
+PyTypeObject corefile_mapped_file_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.CorefileMappedFile", /*tp_name*/
+ sizeof (corefile_mapped_file_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ cfmfpy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB corefile mapped file object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ corefile_mapped_file_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+static gdb_PyGetSetDef corefile_mapped_file_region_object_getset[] =
+{
+ { "start", cfmfrpy_get_start, nullptr,
+ "The start address of a CorefileMappedFileRegion object.", nullptr },
+ { "end", cfmfrpy_get_end, nullptr,
+ "The end address of a CorefileMappedFileRegion object.", nullptr },
+ { "file_offset", cfmfrpy_get_file_offset, nullptr,
+ "The file offset of a CorefileMappedFileRegion object.", nullptr },
+ { nullptr }
+};
+
+PyTypeObject corefile_mapped_file_region_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.CorefileMappedFileRegion", /*tp_name*/
+ sizeof (corefile_mapped_file_region_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB corefile mapped file region object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ corefile_mapped_file_region_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/gdb/python/py-dap.c b/gdb/python/py-dap.c
index 7196d6c..9a9875a 100644
--- a/gdb/python/py-dap.c
+++ b/gdb/python/py-dap.c
@@ -110,9 +110,7 @@ dap_interp::pre_command_loop ()
call_dap_fn ("pre_command_loop");
}
-void _initialize_py_interp ();
-void
-_initialize_py_interp ()
+INIT_GDB_FILE (py_interp)
{
/* The dap code uses module typing, available starting python 3.5. */
#if PY_VERSION_HEX >= 0x03050000
diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c
index 9ca8d22..47ae99c 100644
--- a/gdb/python/py-disasm.c
+++ b/gdb/python/py-disasm.c
@@ -254,15 +254,15 @@ disasm_info_object_is_valid (disasm_info_object *obj)
/* Fill in OBJ with all the other arguments. */
static void
-disasm_info_fill (disasm_info_object *obj, struct gdbarch *gdbarch,
+disasm_info_fill (disasm_info_object &obj, struct gdbarch *gdbarch,
program_space *progspace, bfd_vma address,
disassemble_info *di, disasm_info_object *next)
{
- obj->gdbarch = gdbarch;
- obj->program_space = progspace;
- obj->address = address;
- obj->gdb_info = di;
- obj->next = next;
+ obj.gdbarch = gdbarch;
+ obj.program_space = progspace;
+ obj.address = address;
+ obj.gdb_info = di;
+ obj.next = next;
}
/* Implement DisassembleInfo.__init__. Takes a single argument that must
@@ -281,7 +281,7 @@ disasm_info_init (PyObject *self, PyObject *args, PyObject *kwargs)
disasm_info_object *other = (disasm_info_object *) info_obj;
disasm_info_object *info = (disasm_info_object *) self;
- disasm_info_fill (info, other->gdbarch, other->program_space,
+ disasm_info_fill (*info, other->gdbarch, other->program_space,
other->address, other->gdb_info, other->next);
other->next = info;
@@ -1156,19 +1156,18 @@ gdbpy_disassembler::gdbpy_disassembler (disasm_info_object *obj)
happens when gdbpy_print_insn returns. This class is responsible for
marking the DisassembleInfo as invalid in its destructor. */
-struct scoped_disasm_info_object
+struct scoped_invalidate_disasm_info
{
- /* Constructor. */
- scoped_disasm_info_object (struct gdbarch *gdbarch, CORE_ADDR memaddr,
- disassemble_info *info)
- : m_disasm_info (allocate_disasm_info_object ())
+ /* Constructor. Just cache DISASM_INFO for use in the destructor. */
+ scoped_invalidate_disasm_info
+ (gdbpy_ref<disasm_info_object> disasm_info)
+ : m_disasm_info (std::move (disasm_info))
{
- disasm_info_fill (m_disasm_info.get (), gdbarch, current_program_space,
- memaddr, info, nullptr);
+ /* Nothing. */
}
/* Upon destruction mark m_disasm_info as invalid. */
- ~scoped_disasm_info_object ()
+ ~scoped_invalidate_disasm_info ()
{
/* Invalidate the original DisassembleInfo object as well as any copies
that the user might have made. */
@@ -1178,30 +1177,15 @@ struct scoped_disasm_info_object
obj->gdb_info = nullptr;
}
- /* Return a pointer to the underlying disasm_info_object instance. */
- disasm_info_object *
- get () const
- {
- return m_disasm_info.get ();
- }
-
private:
- /* Wrapper around the call to PyObject_New, this wrapper function can be
- called from the constructor initialization list, while PyObject_New, a
- macro, can't. */
- static disasm_info_object *
- allocate_disasm_info_object ()
- {
- return (disasm_info_object *) PyObject_New (disasm_info_object,
- &disasm_info_object_type);
- }
-
/* A reference to a gdb.disassembler.DisassembleInfo object. When this
- containing instance goes out of scope this reference is released,
- however, the user might be holding other references to the
- DisassembleInfo object in Python code, so the underlying object might
- not be deleted. */
+ object goes out of scope this reference is released, however, the user
+ might be holding other references to the DisassembleInfo (either
+ directly, or via copies of this object), in which case the underlying
+ object will not be deleted. The destructor of this class ensures
+ that this DisassembleInfo object, and any copies, are all marked
+ invalid. */
gdbpy_ref<disasm_info_object> m_disasm_info;
};
@@ -1242,17 +1226,30 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr,
return {};
}
- /* Create the new DisassembleInfo object we will pass into Python. This
- object will be marked as invalid when we leave this scope. */
- scoped_disasm_info_object scoped_disasm_info (gdbarch, memaddr, info);
- disasm_info_object *disasm_info = scoped_disasm_info.get ();
+ /* Create the new DisassembleInfo object we will pass into Python. */
+ gdbpy_ref<disasm_info_object> disasm_info
+ ((disasm_info_object *) PyObject_New (disasm_info_object,
+ &disasm_info_object_type));
+ if (disasm_info == nullptr)
+ {
+ gdbpy_print_stack ();
+ return {};
+ }
+
+ /* Initialise the DisassembleInfo object. */
+ disasm_info_fill (*disasm_info.get (), gdbarch, current_program_space,
+ memaddr, info, nullptr);
+
+ /* Ensure the DisassembleInfo, along with any copies the user makes, are
+ marked as invalid when we leave this scope. */
+ scoped_invalidate_disasm_info invalidate_disasm (disasm_info);
/* Call into the registered disassembler to (possibly) perform the
disassembly. */
- PyObject *insn_disas_obj = (PyObject *) disasm_info;
- gdbpy_ref<> result (PyObject_CallFunctionObjArgs (hook.get (),
- insn_disas_obj,
- nullptr));
+ gdbpy_ref<> result
+ (PyObject_CallFunctionObjArgs (hook.get (),
+ (PyObject *) disasm_info.get (),
+ nullptr));
if (result == nullptr)
{
@@ -1311,12 +1308,13 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr,
return {};
}
- /* Check the result is a DisassemblerResult (or a sub-class). */
- if (!PyObject_IsInstance (result.get (),
- (PyObject *) &disasm_result_object_type))
+ /* Check the result is a DisassemblerResult. */
+ if (!PyObject_TypeCheck (result.get (), &disasm_result_object_type))
{
- PyErr_SetString (PyExc_TypeError,
- _("Result is not a DisassemblerResult."));
+ PyErr_Format
+ (PyExc_TypeError,
+ _("Result from Disassembler must be gdb.DisassemblerResult, not %s."),
+ Py_TYPE (result.get ())->tp_name);
gdbpy_print_stack ();
return std::optional<int> (-1);
}
diff --git a/gdb/python/py-event-types.def b/gdb/python/py-event-types.def
index 15cd9fa..83167f3 100644
--- a/gdb/python/py-event-types.def
+++ b/gdb/python/py-event-types.def
@@ -54,7 +54,7 @@ GDB_PY_DEFINE_EVENT_TYPE (new_thread,
GDB_PY_DEFINE_EVENT_TYPE (thread_exited,
"ThreadExitedEvent",
"GDB thread exited event object",
- event_object_type);
+ thread_event_object_type);
GDB_PY_DEFINE_EVENT_TYPE (new_inferior,
"NewInferiorEvent",
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index 0ea629f..70e1684 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -175,7 +175,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
struct frame_id frame_id;
PyObject *internal = NULL;
int internal_bp = 0;
- CORE_ADDR pc;
+ std::optional<CORE_ADDR> pc;
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
&frame_obj, &internal))
@@ -249,9 +249,9 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
try
{
- if (get_frame_pc_if_available (frame, &pc))
+ if ((pc = get_frame_pc_if_available (frame)))
{
- struct symbol *function = find_pc_function (pc);
+ struct symbol *function = find_pc_function (*pc);
if (function != nullptr)
{
struct type *ret_type =
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
index adf4233..db8c274 100644
--- a/gdb/python/py-framefilter.c
+++ b/gdb/python/py-framefilter.c
@@ -168,7 +168,7 @@ mi_should_print (struct symbol *sym, enum mi_print_types type)
{
int print_me = 0;
- switch (sym->aclass ())
+ switch (sym->loc_class ())
{
default:
case LOC_UNDEF: /* catches errors */
diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c
index ea3a385..70ceebb 100644
--- a/gdb/python/py-gdb-readline.c
+++ b/gdb/python/py-gdb-readline.c
@@ -29,11 +29,7 @@
static char *
gdbpy_readline_wrapper (FILE *sys_stdin, FILE *sys_stdout,
-#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 4
const char *prompt)
-#else
- char *prompt)
-#endif
{
int n;
const char *p = NULL;
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 2aa11d3..d926923 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -973,6 +973,22 @@ infpy_get_main_name (PyObject *self, void *closure)
return host_string_to_python_string (name).release ();
}
+/* Implement the Inferior.corefile getter. Returns a gdb.Corefile
+ object, or None. */
+
+static PyObject *
+infpy_get_core_file (PyObject *self, void *closure)
+{
+ inferior_object *inf = (inferior_object *) self;
+
+ INFPY_REQUIRE_VALID (inf);
+
+ inferior *inferior = inf->inferior;
+ gdb_assert (inferior != nullptr);
+
+ return gdbpy_core_file_from_inferior (inferior).release ();
+}
+
static void
infpy_dealloc (PyObject *obj)
{
@@ -1062,6 +1078,8 @@ static gdb_PyGetSetDef inferior_object_getset[] =
{ "progspace", infpy_get_progspace, NULL, "Program space of this inferior" },
{ "main_name", infpy_get_main_name, nullptr,
"Name of 'main' function, if known.", nullptr },
+ { "corefile", infpy_get_core_file, nullptr,
+ "The corefile loaded in to this inferior, or None.", nullptr },
{ NULL }
};
diff --git a/gdb/python/py-infevents.c b/gdb/python/py-infevents.c
index e63ba52..f74fb01 100644
--- a/gdb/python/py-infevents.c
+++ b/gdb/python/py-infevents.c
@@ -40,7 +40,7 @@ create_inferior_call_event_object (inferior_call_kind flag, ptid_t ptid,
gdb_assert_not_reached ("invalid inferior_call_kind");
}
- gdbpy_ref<> ptid_obj (gdbpy_create_ptid_object (ptid));
+ gdbpy_ref<> ptid_obj = gdbpy_create_ptid_object (ptid);
if (ptid_obj == NULL)
return NULL;
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 4f1f8d4..08533fe 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -190,7 +190,7 @@ thpy_get_ptid (PyObject *self, void *closure)
THPY_REQUIRE_VALID (thread_obj);
- return gdbpy_create_ptid_object (thread_obj->thread->ptid);
+ return gdbpy_create_ptid_object (thread_obj->thread->ptid).release ();
}
/* Implement gdb.InferiorThread.ptid_string attribute. */
@@ -361,23 +361,14 @@ thpy_repr (PyObject *self)
target_pid_to_str (thr->ptid).c_str ());
}
-/* Return a reference to a new Python object representing a ptid_t.
- The object is a tuple containing (pid, lwp, tid). */
-PyObject *
+/* See python-internal.h. */
+
+gdbpy_ref<>
gdbpy_create_ptid_object (ptid_t ptid)
{
- int pid;
- long lwp;
- ULONGEST tid;
- PyObject *ret;
-
- ret = PyTuple_New (3);
- if (!ret)
- return NULL;
-
- pid = ptid.pid ();
- lwp = ptid.lwp ();
- tid = ptid.tid ();
+ int pid = ptid.pid ();
+ long lwp = ptid.lwp ();
+ ULONGEST tid = ptid.tid ();
gdbpy_ref<> pid_obj = gdb_py_object_from_longest (pid);
if (pid_obj == nullptr)
@@ -389,10 +380,14 @@ gdbpy_create_ptid_object (ptid_t ptid)
if (tid_obj == nullptr)
return nullptr;
+ gdbpy_ref<> ret (PyTuple_New (3));
+ if (ret == nullptr)
+ return nullptr;
+
/* Note that these steal references, hence the use of 'release'. */
- PyTuple_SET_ITEM (ret, 0, pid_obj.release ());
- PyTuple_SET_ITEM (ret, 1, lwp_obj.release ());
- PyTuple_SET_ITEM (ret, 2, tid_obj.release ());
+ PyTuple_SET_ITEM (ret.get (), 0, pid_obj.release ());
+ PyTuple_SET_ITEM (ret.get (), 1, lwp_obj.release ());
+ PyTuple_SET_ITEM (ret.get (), 2, tid_obj.release ());
return ret;
}
diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
index 9b871d4..b2ab4e2 100644
--- a/gdb/python/py-mi.c
+++ b/gdb/python/py-mi.c
@@ -218,11 +218,11 @@ py_object_to_mi_key (PyObject *key_obj)
{
gdb_assert (name != nullptr);
- if (*name == '\0' || !isalpha (*name))
+ if (*name == '\0' || !c_isalpha (*name))
return false;
for (; *name != '\0'; ++name)
- if (!isalnum (*name) && *name != '_' && *name != '-')
+ if (!c_isalnum (*name) && *name != '_' && *name != '-')
return false;
return true;
@@ -363,7 +363,7 @@ gdbpy_notify_mi (PyObject *self, PyObject *args, PyObject *kwargs)
}
for (int i = 0; i < name_len; i++)
{
- if (!isalnum (name[i]) && name[i] != '-')
+ if (!c_isalnum (name[i]) && name[i] != '-')
{
PyErr_Format
(PyExc_ValueError,
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
index e4f07c9..07db0cc 100644
--- a/gdb/python/py-micmd.c
+++ b/gdb/python/py-micmd.c
@@ -350,7 +350,7 @@ micmdpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
PyErr_SetString (PyExc_ValueError, _("MI command name is empty."));
return -1;
}
- else if ((name_len < 2) || (name[0] != '-') || !isalnum (name[1]))
+ else if ((name_len < 2) || (name[0] != '-') || !c_isalnum (name[1]))
{
PyErr_SetString (PyExc_ValueError,
_("MI command name does not start with '-'"
@@ -361,7 +361,7 @@ micmdpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
{
for (int i = 2; i < name_len; i++)
{
- if (!isalnum (name[i]) && name[i] != '-')
+ if (!c_isalnum (name[i]) && name[i] != '-')
{
PyErr_Format
(PyExc_ValueError,
@@ -578,9 +578,7 @@ PyTypeObject micmdpy_object_type = {
0, /* tp_alloc */
};
-void _initialize_py_micmd ();
-void
-_initialize_py_micmd ()
+INIT_GDB_FILE (py_micmd)
{
add_setshow_boolean_cmd
("py-micmd", class_maintenance, &pymicmd_debug,
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index 1c6f569..a9f5754 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -556,7 +556,7 @@ objfpy_build_id_ok (const char *string)
return 0;
for (i = 0; i < n; ++i)
{
- if (!isxdigit (string[i]))
+ if (!c_isxdigit (string[i]))
return 0;
}
return 1;
@@ -619,9 +619,8 @@ gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw)
struct objfile *objfile = nullptr;
if (by_build_id)
- gdbarch_iterate_over_objfiles_in_search_order
- (current_inferior ()->arch (),
- [&objfile, name] (struct objfile *obj)
+ current_program_space->iterate_over_objfiles_in_search_order
+ ([&objfile, name] (struct objfile *obj)
{
/* Don't return separate debug files. */
if (obj->separate_debug_objfile_backlink != nullptr)
@@ -642,9 +641,8 @@ gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw)
return 1;
}, gdbpy_current_objfile);
else
- gdbarch_iterate_over_objfiles_in_search_order
- (current_inferior ()->arch (),
- [&objfile, name] (struct objfile *obj)
+ current_program_space->iterate_over_objfiles_in_search_order
+ ([&objfile, name] (struct objfile *obj)
{
/* Don't return separate debug files. */
if (obj->separate_debug_objfile_backlink != nullptr)
diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c
index 763680e..06237b6 100644
--- a/gdb/python/py-param.c
+++ b/gdb/python/py-param.c
@@ -495,7 +495,11 @@ get_doc_string (PyObject *object, enum doc_string_type doc_type,
}
}
- if (result == nullptr)
+ /* For the set/show docs, if these strings are empty then we set then to
+ a non-empty string. This ensures that the command has some sane
+ documentation for its 'help' text. */
+ if (result == nullptr
+ || (doc_type != doc_string_description && *result == '\0'))
{
if (doc_type == doc_string_description)
result.reset (xstrdup (_("This command is not documented.")));
@@ -904,6 +908,18 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds)
show_doc = get_doc_string (self, doc_string_show, name);
doc = get_doc_string (self, doc_string_description, cmd_name.get ());
+ /* The set/show docs should always be a non-empty string. */
+ gdb_assert (set_doc != nullptr && *set_doc != '\0');
+ gdb_assert (show_doc != nullptr && *show_doc != '\0');
+
+ /* For the DOC string only, if it is the empty string, then we convert it
+ to NULL. This means GDB will not even display a blank line for this
+ part of the help text, instead the set/show line is all the user will
+ get. */
+ gdb_assert (doc != nullptr);
+ if (*doc == '\0')
+ doc = nullptr;
+
Py_INCREF (self);
try
diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
index 1e72e90..e2bfd6b 100644
--- a/gdb/python/py-prettyprint.c
+++ b/gdb/python/py-prettyprint.c
@@ -98,9 +98,9 @@ search_pp_list (PyObject *list, PyObject *value)
static PyObject *
find_pretty_printer_from_objfiles (PyObject *value)
{
- for (objfile *obj : current_program_space->objfiles ())
+ for (objfile &obj : current_program_space->objfiles ())
{
- gdbpy_ref<> objf = objfile_to_objfile_object (obj);
+ gdbpy_ref<> objf = objfile_to_objfile_object (&obj);
if (objf == NULL)
{
/* Ignore the error and continue. */
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 61f9b4b..9f3e45c 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -431,9 +431,9 @@ pspy_get_objfiles (PyObject *self_, PyObject *args)
if (self->pspace != NULL)
{
- for (objfile *objf : self->pspace->objfiles ())
+ for (objfile &objf : self->pspace->objfiles ())
{
- gdbpy_ref<> item = objfile_to_objfile_object (objf);
+ gdbpy_ref<> item = objfile_to_objfile_object (&objf);
if (item == nullptr
|| PyList_Append (list.get (), item.get ()) == -1)
diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c
index 7e7904b..89c2e77 100644
--- a/gdb/python/py-record.c
+++ b/gdb/python/py-record.c
@@ -696,6 +696,9 @@ gdbpy_current_recording (PyObject *self, PyObject *args)
Py_RETURN_NONE;
ret = PyObject_New (recpy_record_object, &recpy_record_type);
+ if (ret == nullptr)
+ return nullptr;
+
ret->thread = inferior_thread ();
ret->method = target_record_method (ret->thread->ptid);
diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c
index 78a806c..9be2e3c 100644
--- a/gdb/python/py-registers.c
+++ b/gdb/python/py-registers.c
@@ -403,8 +403,7 @@ gdbpy_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
PyErr_SetString (PyExc_ValueError, "Bad register");
}
/* The register could be a gdb.RegisterDescriptor object. */
- else if (PyObject_IsInstance (pyo_reg_id,
- (PyObject *) &register_descriptor_object_type))
+ else if (PyObject_TypeCheck (pyo_reg_id, &register_descriptor_object_type))
{
register_descriptor_object *reg
= (register_descriptor_object *) pyo_reg_id;
diff --git a/gdb/python/py-style.c b/gdb/python/py-style.c
new file mode 100644
index 0000000..b10a45f
--- /dev/null
+++ b/gdb/python/py-style.c
@@ -0,0 +1,818 @@
+/* Python interface to ui_file_style objects.
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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 "python-internal.h"
+#include "ui-style.h"
+#include "py-color.h"
+#include "cli/cli-decode.h"
+#include "cli/cli-style.h"
+#include "top.h"
+
+/* Intensity constants and their values. */
+static struct {
+ const char *name;
+ ui_file_style::intensity value;
+} intensity_constants[] =
+{
+ { "INTENSITY_NORMAL", ui_file_style::NORMAL },
+ { "INTENSITY_DIM", ui_file_style::DIM },
+ { "INTENSITY_BOLD", ui_file_style::BOLD }
+};
+
+/* A style. */
+struct style_object
+{
+ PyObject_HEAD
+
+ /* Underlying style, only valid when STYLE_NAME is NULL. */
+ ui_file_style style;
+
+ /* The name of the style. Can be NULL, in which case STYLE holds the
+ style value. */
+ char *style_name;
+};
+
+extern PyTypeObject style_object_type;
+
+/* Initialize the 'style' module. */
+
+static int
+gdbpy_initialize_style ()
+{
+ for (auto &pair : intensity_constants)
+ if (PyModule_AddIntConstant (gdb_module, pair.name,
+ static_cast<long> (pair.value)) < 0)
+ return -1;
+
+ return gdbpy_type_ready (&style_object_type, gdb_module);
+}
+
+/* Free any resources help by SELF, and reset points to NULL. */
+
+static void
+stylepy_free_resources (PyObject *self)
+{
+ style_object *style = (style_object *) self;
+
+ xfree (style->style_name);
+ style->style_name = nullptr;
+}
+
+/* gdb.Style deallocation method. */
+
+static void
+stylepy_dealloc (PyObject *self)
+{
+ stylepy_free_resources (self);
+ Py_TYPE (self)->tp_free (self);
+}
+
+/* Find style NAME and return it. If NAME cannot be found then an empty
+ optional is returned, and a Python error will be set.
+
+ If HAS_INTENSITY_PTR is not NULL, then, if NAME is found,
+ *HAS_INTENSITY_PTR will be set true if NAME has an "intensity"
+ sub-command, and set false otherwise. If NAME is not found then
+ *HAS_INTENSITY_PTR is left unchanged.
+
+ If FOUND_CMD_PTR is not NULL, then, if NAME is found, *FOUND_CMD_PTR is
+ set to point to the prefix command matching NAME. If NAME is a
+ multi-word style name (e.g. 'disassembler comment') then *FOUND_CMD_PTR
+ will point to the prefix command for the last word (e.g. 'comment'). */
+
+static std::optional<ui_file_style>
+stylepy_style_from_name (const char *name, bool *has_intensity_ptr = nullptr,
+ const cmd_list_element **found_cmd_ptr = nullptr)
+{
+ std::string cmd_str = std::string ("show style ") + name;
+
+ struct cmd_list_element *cmd = nullptr;
+ struct cmd_list_element *alias = nullptr;
+ int found;
+ try
+ {
+ struct cmd_list_element *prefix;
+ found = lookup_cmd_composition (cmd_str.c_str (), &alias, &prefix, &cmd);
+ }
+ catch (const gdb_exception &ex)
+ {
+ PyErr_Format (PyExc_RuntimeError,
+ _("style '%s' cannot be found."), name);
+ return {};
+ }
+
+ gdb_assert (!found || cmd != nullptr);
+
+ if (!found || cmd == CMD_LIST_AMBIGUOUS || !cmd->is_prefix ())
+ {
+ PyErr_Format (PyExc_RuntimeError,
+ _("style '%s' cannot be found."), name);
+ return {};
+ }
+
+ ui_file_style style;
+ bool has_fg = false;
+ bool has_bg = false;
+ bool has_intensity = false;
+ for (cmd_list_element *sub = *cmd->subcommands;
+ sub != nullptr;
+ sub = sub->next)
+ {
+ if (!sub->var.has_value ())
+ continue;
+
+ if (strcmp (sub->name, "foreground") == 0)
+ {
+ const ui_file_style::color &color
+ = sub->var->get<ui_file_style::color> ();
+ style.set_fg (color);
+ has_fg = true;
+ }
+ else if (strcmp (sub->name, "background") == 0)
+ {
+ const ui_file_style::color &color
+ = sub->var->get<ui_file_style::color> ();
+ style.set_bg (color);
+ has_bg = true;
+ }
+ else if (strcmp (sub->name, "intensity") == 0
+ && sub->var->type () == var_enum)
+ {
+ const char *intensity_str = sub->var->get<const char *> ();
+ ui_file_style::intensity intensity = ui_file_style::NORMAL;
+ if (strcmp (intensity_str, "bold") == 0)
+ intensity = ui_file_style::BOLD;
+ else if (strcmp (intensity_str, "dim") == 0)
+ intensity = ui_file_style::DIM;
+ style.set_intensity (intensity);
+ has_intensity = true;
+ }
+ }
+
+ /* All styles should have a foreground and background, but the intensity
+ is optional. */
+ if (!has_fg || !has_bg)
+ {
+ PyErr_Format (PyExc_RuntimeError,
+ _("style '%s' missing '%s' component."), name,
+ (has_fg ? "background" : "foreground"));
+ return {};
+ }
+
+ if (has_intensity_ptr != nullptr)
+ *has_intensity_ptr = has_intensity;
+
+ /* If NAME identified an alias then use that instead of CMD, this means
+ the style's cached name will better match the string the user used to
+ initialise the style. */
+ if (found_cmd_ptr != nullptr)
+ *found_cmd_ptr = alias != nullptr ? alias : cmd;
+
+ return style;
+}
+
+/* Initialise a gdb.Style object from a named style. We only store the
+ style name within SELF, each time the style is used, we look up its
+ current value, this allows the style to change if the user adjusts the
+ settings. */
+
+static int
+stylepy_init_from_style_name (PyObject *self, const char *style_name)
+{
+ style_object *style = (style_object *) self;
+ gdb_assert (style->style_name == nullptr);
+
+ gdb_assert (style_name != nullptr);
+
+ const cmd_list_element *cmd = nullptr;
+ std::optional<ui_file_style> maybe_style
+ = stylepy_style_from_name (style_name, nullptr, &cmd);
+ if (!maybe_style.has_value ())
+ return -1;
+
+ /* If we found a style then we must have found a prefix command. */
+ gdb_assert (cmd != nullptr);
+ gdb_assert (cmd->is_prefix ());
+
+ /* Get the components of this command. */
+ std::vector<std::string> components = cmd->command_components ();
+ gdb_assert (components.size () > 2);
+ gdb_assert (components[0] == "show");
+ gdb_assert (components[1] == "style");
+
+ /* And build the components into a string, but without the 'show style'
+ part at the start. */
+ std::string expanded_style_name (components[2]);
+ for (int i = 3; i < components.size (); ++i)
+ expanded_style_name += " " + components[i];
+
+ style->style_name = xstrdup (expanded_style_name.c_str ());
+
+ return 0;
+}
+
+/* Convert INTENSITY_VALUE to a valid intensity enum value, if possible,
+ and return the enum value. If INTENSITY_VALUE is not a valid intensity
+ value then set a Python error, and return an empty optional. */
+
+static std::optional<ui_file_style::intensity>
+stylepy_long_to_intensity (long intensity_value)
+{
+ switch (intensity_value)
+ {
+ case ui_file_style::NORMAL:
+ case ui_file_style::DIM:
+ case ui_file_style::BOLD:
+ return static_cast<ui_file_style::intensity> (intensity_value);
+
+ default:
+ PyErr_Format
+ (PyExc_ValueError, _("invalid 'intensity' value %d."),
+ intensity_value);
+ return {};
+ }
+}
+
+/* Initialise a gdb.Style object from a foreground and background
+ gdb.Color object, and an intensity. */
+
+static int
+stylepy_init_from_parts (PyObject *self, PyObject *fg, PyObject *bg,
+ int intensity_value)
+{
+ style_object *style = (style_object *) self;
+ gdb_assert (style->style_name == nullptr);
+
+ if (fg == Py_None)
+ fg = nullptr;
+
+ if (bg == Py_None)
+ bg = nullptr;
+
+ if (fg != nullptr && !gdbpy_is_color (fg))
+ {
+ PyErr_Format
+ (PyExc_TypeError,
+ _("'foreground' argument must be gdb.Color or None, not %s."),
+ Py_TYPE (fg)->tp_name);
+ return -1;
+ }
+
+ if (bg != nullptr && !gdbpy_is_color (bg))
+ {
+ PyErr_Format
+ (PyExc_TypeError,
+ _("'background' argument must be gdb.Color or None, not %s."),
+ Py_TYPE (bg)->tp_name);
+ return -1;
+ }
+
+ if (fg != nullptr)
+ style->style.set_fg (gdbpy_get_color (fg));
+ else
+ style->style.set_fg (ui_file_style::color (ui_file_style::NONE));
+
+ if (bg != nullptr)
+ style->style.set_bg (gdbpy_get_color (bg));
+ else
+ style->style.set_bg (ui_file_style::color (ui_file_style::NONE));
+
+ /* Convert INTENSITY_VALUE into the enum. */
+ std::optional<ui_file_style::intensity> intensity
+ = stylepy_long_to_intensity (intensity_value);
+ if (!intensity.has_value ())
+ return -1;
+ style->style.set_intensity (intensity.value ());
+
+ return 0;
+}
+
+/* gdb.Style object initializer. Either:
+ gdb.Style.__init__("style name")
+ gdb.Style.__init__(fg, bg, intensity)
+
+ This init function supports two possible sets of arguments. Both
+ options are different enough that we can distinguish the two by the
+ argument types. Dispatch to one of the two stylepy_init_from_*
+ functions above. */
+
+static int
+stylepy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ /* If this object was previously initialised, then clear it out now. */
+ stylepy_free_resources (self);
+
+ /* Try to parse the incoming arguments as a string, this is a style
+ name. */
+ const char *style_name = nullptr;
+ static const char *keywords_style[] = { "style", nullptr };
+ if (gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "s", keywords_style,
+ &style_name))
+ return stylepy_init_from_style_name (self, style_name);
+
+ /* That didn't work, discard any errors. */
+ PyErr_Clear ();
+
+ /* Try to parse the incoming arguments as a list of parts, this is an
+ unnamed style. */
+ PyObject *foreground_color = nullptr;
+ PyObject *background_color = nullptr;
+ int intensity_value = static_cast<int> (ui_file_style::NORMAL);
+ static const char *keywords_parts[]
+ = { "foreground", "background", "intensity", nullptr };
+ if (gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "|OOi", keywords_parts,
+ &foreground_color,
+ &background_color,
+ &intensity_value))
+ return stylepy_init_from_parts (self, foreground_color,
+ background_color, intensity_value);
+
+ /* Return the error gdb_PyArg_ParseTupleAndKeywords set. */
+ return -1;
+}
+
+
+
+/* See python-internal.h. */
+
+bool
+gdbpy_is_style (PyObject *obj)
+{
+ gdb_assert (obj != nullptr);
+ return PyObject_TypeCheck (obj, &style_object_type);
+}
+
+/* Return the ui_file_style for STYLEPY. If the style cannot be found,
+ then return an empty optional, and set a Python error. */
+
+static std::optional<ui_file_style>
+stylepy_to_style (style_object *stylepy)
+{
+ std::optional<ui_file_style> style;
+
+ if (stylepy->style_name != nullptr)
+ style = stylepy_style_from_name (stylepy->style_name);
+ else
+ style.emplace (stylepy->style);
+
+ return style;
+}
+
+/* See python-internal.h. */
+
+std::optional<ui_file_style>
+gdbpy_style_object_to_ui_file_style (PyObject *obj)
+{
+ gdb_assert (obj != nullptr);
+ gdb_assert (gdbpy_is_style (obj));
+
+ style_object *style_obj = (style_object *) obj;
+ return stylepy_to_style (style_obj);
+}
+
+/* Implementation of gdb.Style.escape_sequence(). Return the escape
+ sequence to apply Style. If styling is turned off, then this returns
+ the empty string. Can raise an exception if a named style can no longer
+ be read. */
+
+static PyObject *
+stylepy_escape_sequence (PyObject *self, PyObject *args)
+{
+ style_object *style_obj = (style_object *) self;
+
+ std::optional<ui_file_style> style = stylepy_to_style (style_obj);
+ if (!style.has_value ())
+ return nullptr;
+
+ std::string style_str;
+ if (term_cli_styling ())
+ style_str = style->to_ansi ();
+
+ return host_string_to_python_string (style_str.c_str ()).release ();
+}
+
+/* Implement gdb.Style.apply(STR). Return a new string which is STR with
+ escape sequences added so that STR is formatted in this style. A
+ trailing escape sequence is added to restore the default style.
+
+ If styling is currently disabled ('set style enabled off'), then no
+ escape sequences are added, but all the checks for the validity of the
+ current style are still performed, and a new string, a copy of the
+ input string is returned.
+
+ Can raise a Python exception and return NULL if the argument types are
+ wrong, or if a named style can no longer be read. */
+
+static PyObject *
+stylepy_apply (PyObject *self, PyObject *args, PyObject *kw)
+{
+ style_object *style_obj = (style_object *) self;
+
+ static const char *keywords[] = { "string", nullptr };
+ PyObject *input_obj;
+
+ /* Grab the incoming string as a Python object. In the case where
+ styling is not being applied we can just return this object with the
+ reference count incremented. */
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "O!", keywords,
+ &PyUnicode_Type, &input_obj))
+ return nullptr;
+
+ std::optional<ui_file_style> style = stylepy_to_style (style_obj);
+ if (!style.has_value ())
+ return nullptr;
+
+ if (gdb_stdout->can_emit_style_escape ())
+ {
+ gdb_assert (gdbpy_is_string (input_obj));
+ gdb::unique_xmalloc_ptr<char>
+ input (python_string_to_host_string (input_obj));
+
+ std::string output
+ = style->to_ansi () + input.get () + ui_file_style ().to_ansi ();
+
+ return host_string_to_python_string (output.c_str ()).release ();
+ }
+ else
+ {
+ /* Return an unmodified "copy" of INPUT_OBJ by just incrementing the
+ reference count. */
+ Py_INCREF (input_obj);
+ return input_obj;
+ }
+}
+
+
+
+/* Implement reading the gdb.Style.foreground attribute. */
+
+static PyObject *
+stylepy_get_foreground (PyObject *self, void *closure)
+{
+ style_object *style_obj = (style_object *) self;
+
+ std::optional<ui_file_style> style = stylepy_to_style (style_obj);
+ if (!style.has_value ())
+ return nullptr;
+
+ gdbpy_ref<> color = create_color_object (style->get_foreground ());
+ if (color == nullptr)
+ return nullptr;
+
+ return color.release ();
+}
+
+/* Implement writing the gdb.Style.foreground attribute. */
+
+static int
+stylepy_set_foreground (PyObject *self, PyObject *newvalue, void *closure)
+{
+ if (!gdbpy_is_color (newvalue))
+ {
+ PyErr_Format (PyExc_TypeError, _("value must be gdb.Color, not %s"),
+ Py_TYPE (newvalue)->tp_name);
+ return -1;
+ }
+
+ style_object *style_obj = (style_object *) self;
+
+ /* Handle unnamed styles. This is easy, just update the embedded style
+ object. */
+ if (style_obj->style_name == nullptr)
+ {
+ style_obj->style.set_fg (gdbpy_get_color (newvalue));
+ return 0;
+ }
+
+ /* Handle named styles. This is harder, we need to write to the actual
+ parameter. */
+ std::string cmd_string
+ = string_printf ("set style %s foreground %s",
+ style_obj->style_name,
+ gdbpy_get_color (newvalue).to_string ().c_str ());
+ try
+ {
+ execute_command (cmd_string.c_str (), 0);
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (-1, except);
+ }
+
+ return 0;
+}
+
+/* Implement reading the gdb.Style.background attribute. */
+
+static PyObject *
+stylepy_get_background (PyObject *self, void *closure)
+{
+ style_object *style_obj = (style_object *) self;
+
+ std::optional<ui_file_style> style = stylepy_to_style (style_obj);
+ if (!style.has_value ())
+ return nullptr;
+
+ gdbpy_ref<> color = create_color_object (style->get_background ());
+ if (color == nullptr)
+ return nullptr;
+
+ return color.release ();
+}
+
+/* Implement writing the gdb.Style.background attribute. */
+
+static int
+stylepy_set_background (PyObject *self, PyObject *newvalue, void *closure)
+{
+ if (!gdbpy_is_color (newvalue))
+ {
+ PyErr_Format (PyExc_TypeError, _("value must be gdb.Color, not %s"),
+ Py_TYPE (newvalue)->tp_name);
+ return -1;
+ }
+
+ style_object *style_obj = (style_object *) self;
+
+ /* Handle unnamed styles. This is easy, just update the embedded style
+ object. */
+ if (style_obj->style_name == nullptr)
+ {
+ style_obj->style.set_bg (gdbpy_get_color (newvalue));
+ return 0;
+ }
+
+ /* Handle named styles. This is harder, we need to write to the actual
+ parameter. */
+ std::string cmd_string
+ = string_printf ("set style %s background %s",
+ style_obj->style_name,
+ gdbpy_get_color (newvalue).to_string ().c_str ());
+ try
+ {
+ execute_command (cmd_string.c_str (), 0);
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (-1, except);
+ }
+
+ return 0;
+}
+
+/* Implement reading the gdb.Style.intensity attribute. */
+
+static PyObject *
+stylepy_get_intensity (PyObject *self, void *closure)
+{
+ style_object *style_obj = (style_object *) self;
+
+ std::optional<ui_file_style> style = stylepy_to_style (style_obj);
+ if (!style.has_value ())
+ return nullptr;
+
+ ui_file_style::intensity intensity = style->get_intensity ();
+ return PyLong_FromLong (static_cast<long> (intensity));
+}
+
+/* Return a string representing INTENSITY. */
+
+static const char *
+stylepy_intensity_to_string (ui_file_style::intensity intensity)
+{
+ const char *intensity_str = nullptr;
+ switch (intensity)
+ {
+ case ui_file_style::NORMAL:
+ intensity_str = "normal";
+ break;
+ case ui_file_style::BOLD:
+ intensity_str = "bold";
+ break;
+ case ui_file_style::DIM:
+ intensity_str = "dim";
+ break;
+ }
+
+ gdb_assert (intensity_str != nullptr);
+ return intensity_str;
+}
+
+/* Implement writing the gdb.Style.intensity attribute. */
+
+static int
+stylepy_set_intensity (PyObject *self, PyObject *newvalue, void *closure)
+{
+ style_object *style_obj = (style_object *) self;
+
+ if (!PyLong_Check (newvalue))
+ {
+ PyErr_Format
+ (PyExc_TypeError,
+ _("value must be a Long (a gdb.INTENSITY constant), not %s"),
+ Py_TYPE (newvalue)->tp_name);
+ return -1;
+ }
+
+ /* Convert the Python object to a value we can use. */
+ long intensity_value;
+ if (!gdb_py_int_as_long (newvalue, &intensity_value))
+ return -1;
+
+ std::optional<ui_file_style::intensity> intensity
+ = stylepy_long_to_intensity (intensity_value);
+ if (!intensity.has_value ())
+ return -1;
+
+ /* Handle unnamed styles. This is easy, just update the embedded style
+ object. */
+ if (style_obj->style_name == nullptr)
+ {
+ style_obj->style.set_intensity (intensity.value ());
+ return 0;
+ }
+
+ /* Handle named styles. This is harder, we need to write to the actual
+ parameter. First though, look up the named style to see if it has an
+ intensity. HAS_INTENSITY will be set true only if the style exists,
+ and has an 'intensity' setting. */
+ bool has_intensity = false;
+ std::optional<ui_file_style> style
+ = stylepy_style_from_name (style_obj->style_name, &has_intensity);
+ if (!style.has_value ())
+ return -1;
+ if (!has_intensity)
+ {
+ PyErr_Format
+ (PyExc_ValueError, "the intensity of style '%s' is not writable.",
+ style_obj->style_name);
+ return -1;
+ }
+
+ const char *intensity_str = stylepy_intensity_to_string (intensity.value ());
+ gdb_assert (intensity_str != nullptr);
+
+ std::string cmd_string
+ = string_printf ("set style %s intensity %s",
+ style_obj->style_name, intensity_str);
+ try
+ {
+ execute_command (cmd_string.c_str (), 0);
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (-1, except);
+ }
+
+ return 0;
+}
+
+
+
+/* Call FETCH_ATTR, passing in SELF, to get a PyObject*, then convert it to
+ a Python string, and finally into a C++ managed string. Will return
+ nullptr and set a Python error if something goes wrong.
+
+ The FETCH_ATTR function will be a function that returns an attribute
+ from SELF. */
+
+static gdb::unique_xmalloc_ptr<char>
+stylepy_attribute_to_string
+ (PyObject *self,
+ gdb::function_view<PyObject * (PyObject *, void *)> fetch_attr)
+{
+ PyObject *attr_obj = fetch_attr (self, nullptr);
+ if (attr_obj == nullptr)
+ return nullptr;
+
+ PyObject *str_obj = PyObject_Str (attr_obj);
+ if (str_obj == nullptr)
+ return nullptr;
+
+ return python_string_to_host_string (str_obj);
+}
+
+/* __repr__ implementation for gdb.Style. */
+
+static PyObject *
+stylepy_repr (PyObject *self)
+{
+ style_object *style_obj = (style_object *) self;
+
+ gdb::unique_xmalloc_ptr<char> fg_str
+ (stylepy_attribute_to_string (self, stylepy_get_foreground));
+ gdb::unique_xmalloc_ptr<char> bg_str
+ (stylepy_attribute_to_string (self, stylepy_get_background));
+
+ PyObject *intensity_obj = stylepy_get_intensity (self, nullptr);
+ if (intensity_obj == nullptr)
+ return nullptr;
+ gdb_assert (PyLong_Check (intensity_obj));
+ long intensity_value;
+ if (!gdb_py_int_as_long (intensity_obj, &intensity_value))
+ return nullptr;
+ std::optional<ui_file_style::intensity> intensity
+ = stylepy_long_to_intensity (intensity_value);
+ if (!intensity.has_value ())
+ return nullptr;
+ const char *intensity_str = stylepy_intensity_to_string (intensity.value ());
+ gdb_assert (intensity_str != nullptr);
+
+ if (style_obj->style_name == nullptr)
+ return PyUnicode_FromFormat ("<%s fg=%s, bg=%s, intensity=%s>",
+ Py_TYPE (self)->tp_name,
+ fg_str.get (), bg_str.get (),
+ intensity_str);
+ else
+ return PyUnicode_FromFormat ("<%s name='%s', fg=%s, bg=%s, intensity=%s>",
+ Py_TYPE (self)->tp_name,
+ style_obj->style_name, fg_str.get (),
+ bg_str.get (), intensity_str);
+}
+
+
+
+/* Style methods. */
+
+static PyMethodDef stylepy_methods[] =
+{
+ { "escape_sequence", stylepy_escape_sequence, METH_NOARGS,
+ "escape_sequence () -> str.\n\
+Return the ANSI escape sequence for this style."},
+ { "apply", (PyCFunction) stylepy_apply, METH_VARARGS | METH_KEYWORDS,
+ "apply(String) -> String.\n\
+Apply this style to the input string. Return an updated string."},
+ {nullptr}
+};
+
+/* Attribute get/set Python definitions. */
+
+static gdb_PyGetSetDef style_object_getset[] = {
+ { "foreground", stylepy_get_foreground, stylepy_set_foreground,
+ "The gdb.Color for the foreground of this style.", NULL },
+ { "background", stylepy_get_background, stylepy_set_background,
+ "The gdb.Color for the background of this style.", NULL },
+ { "intensity", stylepy_get_intensity, stylepy_set_intensity,
+ "The Str for the intensity of this style.", NULL },
+ { nullptr } /* Sentinel. */
+};
+
+PyTypeObject style_object_type =
+{
+ PyVarObject_HEAD_INIT (nullptr, 0)
+ "gdb.Style", /*tp_name*/
+ sizeof (style_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ stylepy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ stylepy_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "GDB style object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ stylepy_methods, /* tp_methods */
+ 0, /* tp_members */
+ style_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ stylepy_init, /* tp_init */
+ 0, /* tp_alloc */
+ PyType_GenericNew /* tp_new */
+};
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_style);
diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index 3028a30..b95071f 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -126,7 +126,7 @@ sympy_get_addr_class (PyObject *self, void *closure)
SYMPY_REQUIRE_VALID (self, symbol);
- return gdb_py_object_from_longest (symbol->aclass ()).release ();
+ return gdb_py_object_from_longest (symbol->loc_class ()).release ();
}
/* Implement gdb.Symbol.domain attribute. Return the domain as an
@@ -156,42 +156,39 @@ static PyObject *
sympy_is_constant (PyObject *self, void *closure)
{
struct symbol *symbol = NULL;
- enum address_class theclass;
SYMPY_REQUIRE_VALID (self, symbol);
- theclass = symbol->aclass ();
+ location_class loc_class = symbol->loc_class ();
- return PyBool_FromLong (theclass == LOC_CONST || theclass == LOC_CONST_BYTES);
+ return PyBool_FromLong (loc_class == LOC_CONST || loc_class == LOC_CONST_BYTES);
}
static PyObject *
sympy_is_function (PyObject *self, void *closure)
{
struct symbol *symbol = NULL;
- enum address_class theclass;
SYMPY_REQUIRE_VALID (self, symbol);
- theclass = symbol->aclass ();
+ location_class loc_class = symbol->loc_class ();
- return PyBool_FromLong (theclass == LOC_BLOCK);
+ return PyBool_FromLong (loc_class == LOC_BLOCK);
}
static PyObject *
sympy_is_variable (PyObject *self, void *closure)
{
struct symbol *symbol = NULL;
- enum address_class theclass;
SYMPY_REQUIRE_VALID (self, symbol);
- theclass = symbol->aclass ();
+ location_class loc_class = symbol->loc_class ();
return PyBool_FromLong (!symbol->is_argument ()
- && (theclass == LOC_LOCAL || theclass == LOC_REGISTER
- || theclass == LOC_STATIC || theclass == LOC_COMPUTED
- || theclass == LOC_OPTIMIZED_OUT));
+ && (loc_class == LOC_LOCAL || loc_class == LOC_REGISTER
+ || loc_class == LOC_STATIC || loc_class == LOC_COMPUTED
+ || loc_class == LOC_OPTIMIZED_OUT));
}
/* Implementation of Symbol.is_artificial. */
@@ -279,7 +276,7 @@ sympy_value (PyObject *self, PyObject *args)
}
SYMPY_REQUIRE_VALID (self, symbol);
- if (symbol->aclass () == LOC_TYPEDEF)
+ if (symbol->loc_class () == LOC_TYPEDEF)
{
PyErr_SetString (PyExc_TypeError, "cannot get the value of a typedef");
return NULL;
@@ -605,17 +602,15 @@ gdbpy_lookup_static_symbols (PyObject *self, PyObject *args, PyObject *kw)
/* Expand any symtabs that contain potentially matching symbols. */
lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
- expand_symtabs_matching (NULL, lookup_name, NULL, NULL,
- SEARCH_STATIC_BLOCK, flags);
- for (objfile *objfile : current_program_space->objfiles ())
+ for (objfile &objfile : current_program_space->objfiles ())
{
- for (compunit_symtab *cust : objfile->compunits ())
+ auto callback = [&] (compunit_symtab *cust)
{
/* Skip included compunits to prevent including compunits from
being searched twice. */
if (cust->user != nullptr)
- continue;
+ return true;
const struct blockvector *bv = cust->blockvector ();
const struct block *block = bv->static_block ();
@@ -628,13 +623,18 @@ gdbpy_lookup_static_symbols (PyObject *self, PyObject *args, PyObject *kw)
if (symbol != nullptr)
{
PyObject *sym_obj = symbol_to_symbol_object (symbol);
- if (sym_obj == nullptr)
- return nullptr;
- if (PyList_Append (return_list.get (), sym_obj) == -1)
- return nullptr;
+ if (sym_obj == nullptr
+ || PyList_Append (return_list.get (), sym_obj) == -1)
+ return false;
}
}
- }
+
+ return true;
+ };
+
+ if (!objfile.search (nullptr, &lookup_name, nullptr, callback,
+ SEARCH_STATIC_BLOCK, flags))
+ return nullptr;
}
}
catch (const gdb_exception &except)
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index f37afde..65753fd 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -158,7 +158,7 @@ convert_field (struct type *type, int field)
}
else
{
- if (type->field (field).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK)
+ if (type->field (field).loc_is_dwarf_block ())
arg = gdbpy_ref<>::new_reference (Py_None);
else
arg = gdb_py_object_from_longest (type->field (field).loc_bitpos ());
@@ -842,11 +842,11 @@ typy_lookup_typename (const char *type_name, const struct block *block)
try
{
if (startswith (type_name, "struct "))
- type = lookup_struct (type_name + 7, NULL);
+ type = lookup_struct (type_name + 7, block);
else if (startswith (type_name, "union "))
- type = lookup_union (type_name + 6, NULL);
+ type = lookup_union (type_name + 6, block);
else if (startswith (type_name, "enum "))
- type = lookup_enum (type_name + 5, NULL);
+ type = lookup_enum (type_name + 5, block);
else
type = lookup_typename (current_language,
type_name, block, 0);
@@ -1045,9 +1045,9 @@ typy_template_argument (PyObject *self, PyObject *args)
}
sym = TYPE_TEMPLATE_ARGUMENT (type, argno);
- if (sym->aclass () == LOC_TYPEDEF)
+ if (sym->loc_class () == LOC_TYPEDEF)
return type_to_type_object (sym->type ());
- else if (sym->aclass () == LOC_OPTIMIZED_OUT)
+ else if (sym->loc_class () == LOC_OPTIMIZED_OUT)
{
PyErr_Format (PyExc_RuntimeError,
_("Template argument is optimized out"));
@@ -1312,10 +1312,9 @@ static PyObject *
typy_has_key (PyObject *self, PyObject *args)
{
struct type *type = ((type_object *) self)->type;
- const char *field;
- int i;
+ const char *field_name;
- if (!PyArg_ParseTuple (args, "s", &field))
+ if (!PyArg_ParseTuple (args, "s", &field_name))
return NULL;
/* We want just fields of this type, not of base types, so instead of
@@ -1326,11 +1325,11 @@ typy_has_key (PyObject *self, PyObject *args)
if (type == NULL)
return NULL;
- for (i = 0; i < type->num_fields (); i++)
+ for (const auto &field : type->fields ())
{
- const char *t_field_name = type->field (i).name ();
+ const char *t_field_name = field.name ();
- if (t_field_name && (strcmp_iw (t_field_name, field) == 0))
+ if (t_field_name && (strcmp_iw (t_field_name, field_name) == 0))
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index ab34971..43125bb 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -287,6 +287,8 @@ pyuw_create_unwind_info (PyObject *pyo_pending_frame,
unwind_info_object *unwind_info
= PyObject_New (unwind_info_object, &unwind_info_object_type);
+ if (unwind_info == nullptr)
+ return nullptr;
unwind_info->frame_id = frame_id;
Py_INCREF (pyo_pending_frame);
@@ -929,9 +931,9 @@ frame_unwind_python::sniff (const frame_info_ptr &this_frame,
/* Received UnwindInfo, cache data. */
PyObject *pyo_unwind_info = PyTuple_GET_ITEM (pyo_execute_ret.get (), 0);
- if (PyObject_IsInstance (pyo_unwind_info,
- (PyObject *) &unwind_info_object_type) <= 0)
- error (_("A Unwinder should return gdb.UnwindInfo instance."));
+ if (!PyObject_TypeCheck (pyo_unwind_info, &unwind_info_object_type))
+ error (_("an Unwinder should return gdb.UnwindInfo, not %s."),
+ Py_TYPE (pyo_unwind_info)->tp_name);
{
unwind_info_object *unwind_info =
@@ -1029,9 +1031,7 @@ gdbpy_initialize_unwind (void)
return 0;
}
-void _initialize_py_unwind ();
-void
-_initialize_py_unwind ()
+INIT_GDB_FILE (py_unwind)
{
add_setshow_boolean_cmd
("py-unwind", class_maintenance, &pyuw_debug,
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index 3855bde..5d8fab9 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -478,6 +478,9 @@ valpy_get_dynamic_type (PyObject *self, void *closure)
type = value_rtti_type (val, NULL, NULL, NULL);
else
type = val->type ();
+
+ if (type == nullptr)
+ type = val->type ();
}
catch (const gdb_exception &except)
{
@@ -1096,8 +1099,7 @@ valpy_getitem (PyObject *self, PyObject *key)
res_val = value_struct_elt (&tmp, {}, field.get (), NULL,
"struct/class/union");
else if (bitpos >= 0)
- res_val = value_struct_elt_bitpos (&tmp, bitpos, field_type,
- "struct/class/union");
+ res_val = value_struct_elt_bitpos (tmp, bitpos, field_type);
else if (base_class_type != NULL)
{
struct type *val_type;
@@ -1289,6 +1291,30 @@ valpy_get_is_optimized_out (PyObject *self, void *closure)
Py_RETURN_FALSE;
}
+/* Implements gdb.Value.is_unavailable. Return true if any part of the
+ value is unavailable. */
+
+static PyObject *
+valpy_get_is_unavailable (PyObject *self, void *closure)
+{
+ struct value *value = ((value_object *) self)->value;
+ bool entirely_available = false;
+
+ try
+ {
+ entirely_available = value->entirely_available ();
+ }
+ catch (const gdb_exception &except)
+ {
+ return gdbpy_handle_gdb_exception (nullptr, except);
+ }
+
+ if (!entirely_available)
+ Py_RETURN_TRUE;
+
+ Py_RETURN_FALSE;
+}
+
/* Implements gdb.Value.is_lazy. */
static PyObject *
valpy_get_is_lazy (PyObject *self, void *closure)
@@ -1843,7 +1869,7 @@ valpy_long (PyObject *self)
{
struct value *value = ((value_object *) self)->value;
struct type *type = value->type ();
- LONGEST l = 0;
+ PyObject *result;
try
{
@@ -1859,17 +1885,57 @@ valpy_long (PyObject *self)
&& type->code () != TYPE_CODE_PTR)
error (_("Cannot convert value to long."));
- l = value_as_long (value);
+ gdb::array_view<const gdb_byte> contents = value->contents ();
+#if PY_VERSION_HEX >= 0x030d0000
+ int flags = (type_byte_order (type) == BFD_ENDIAN_BIG
+ ? Py_ASNATIVEBYTES_BIG_ENDIAN
+ : Py_ASNATIVEBYTES_LITTLE_ENDIAN);
+ if (type->is_unsigned ())
+ flags |= Py_ASNATIVEBYTES_UNSIGNED_BUFFER;
+ result = PyLong_FromNativeBytes (contents.data (), contents.size (),
+ flags);
+#else
+ /* Here we construct a call to "int.from_bytes", passing in the
+ appropriate arguments. We need a somewhat roundabout
+ approach because int.from_bytes requires "signed" to be a
+ keyword arg. */
+
+ /* PyObject_Call requires a tuple argument. */
+ gdbpy_ref<> empty_tuple (PyTuple_New (0));
+ if (empty_tuple == nullptr)
+ return nullptr;
+
+ /* Since we need a dictionary anyway, we pass all arguments as
+ keywords, building the dictionary here. */
+ gdbpy_ref<> args
+ (Py_BuildValue ("{sy#sssO}",
+ "bytes", contents.data (),
+ (Py_ssize_t) contents.size (),
+ "byteorder",
+ (type_byte_order (type) == BFD_ENDIAN_BIG
+ ? "big" : "little"),
+ "signed",
+ type->is_unsigned ()
+ ? Py_False : Py_True));
+ if (args == nullptr)
+ return nullptr;
+
+ /* Find the "int.from_bytes" callable. */
+ gdbpy_ref<> callable (PyObject_GetAttrString ((PyObject *) &PyLong_Type,
+ "from_bytes"));
+ if (callable == nullptr)
+ return nullptr;
+
+ result = PyObject_Call (callable.get (), empty_tuple.get (),
+ args.get ());
+#endif
}
catch (const gdb_exception &except)
{
return gdbpy_handle_gdb_exception (nullptr, except);
}
- if (type->is_unsigned ())
- return gdb_py_object_from_ulongest (l).release ();
- else
- return gdb_py_object_from_longest (l).release ();
+ return result;
}
/* Implements conversion to float. */
@@ -2195,6 +2261,9 @@ static gdb_PyGetSetDef value_object_getset[] = {
"Boolean telling whether the value is optimized "
"out (i.e., not available).",
NULL },
+ { "is_unavailable", valpy_get_is_unavailable, nullptr,
+ "Boolean telling whether the value is unavailable.",
+ nullptr },
{ "type", valpy_get_type, NULL, "Type of the value.", NULL },
{ "dynamic_type", valpy_get_dynamic_type, NULL,
"Dynamic type of the value.", NULL },
diff --git a/gdb/python/py-xmethods.c b/gdb/python/py-xmethods.c
index deac98f..c10ac5d 100644
--- a/gdb/python/py-xmethods.c
+++ b/gdb/python/py-xmethods.c
@@ -141,9 +141,9 @@ gdbpy_get_matching_xmethod_workers
/* Gather debug method matchers registered with the object files.
This could be done differently by iterating over each objfile's matcher
list individually, but there's no data yet to show it's needed. */
- for (objfile *objfile : current_program_space->objfiles ())
+ for (objfile &objfile : current_program_space->objfiles ())
{
- gdbpy_ref<> py_objfile = objfile_to_objfile_object (objfile);
+ gdbpy_ref<> py_objfile = objfile_to_objfile_object (&objfile);
if (py_objfile == NULL)
{
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 5262d76..51ace13 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -88,6 +88,8 @@
#include <frameobject.h>
#include "py-ref.h"
+static_assert (PY_VERSION_HEX >= 0x03040000);
+
#define Py_TPFLAGS_CHECKTYPES 0
/* If Python.h does not define WITH_THREAD, then the various
@@ -135,17 +137,6 @@ typedef unsigned long gdb_py_ulongest;
#endif /* HAVE_LONG_LONG */
-#if PY_VERSION_HEX < 0x03020000
-typedef long Py_hash_t;
-#endif
-
-/* PyMem_RawMalloc appeared in Python 3.4. For earlier versions, we can just
- fall back to PyMem_Malloc. */
-
-#if PY_VERSION_HEX < 0x03040000
-#define PyMem_RawMalloc PyMem_Malloc
-#endif
-
/* A template variable holding the format character (as for
Py_BuildValue) for a given type. */
template<typename T>
@@ -512,14 +503,20 @@ PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
const char *encoding,
struct type *type);
PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
-PyObject *gdbpy_create_ptid_object (ptid_t ptid);
+
+/* Return a reference to a new Python Tuple object representing a ptid_t.
+ The object is a tuple containing (pid, lwp, tid). */
+
+extern gdbpy_ref<> gdbpy_create_ptid_object (ptid_t ptid);
+
PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
PyObject *gdbpy_parameter_value (const setting &var);
gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name
(const char *name, struct cmd_list_element ***base_list,
- struct cmd_list_element **start_list);
+ struct cmd_list_element **start_list,
+ struct cmd_list_element **prefix_cmd = nullptr);
PyObject *gdbpy_register_tui_window (PyObject *self, PyObject *args,
PyObject *kw);
@@ -573,6 +570,20 @@ struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
+/* Return true if OBJ is a gdb.Style object. OBJ must not be NULL. */
+
+extern bool gdbpy_is_style (PyObject *obj);
+
+/* Return the ui_file_style from OBJ, a gdb.Style object. OBJ must not be
+ NULL.
+
+ It is possible that OBJ is a gdb.Style object, but the underlying style
+ cannot be fetched for some reason. If this happens then a Python error
+ is set and an empty optional is returned. */
+
+extern std::optional<ui_file_style>
+ gdbpy_style_object_to_ui_file_style (PyObject *obj);
+
extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
PyObject *kw);
@@ -1116,6 +1127,14 @@ extern std::optional<int> gdbpy_print_insn (struct gdbarch *gdbarch,
CORE_ADDR address,
disassemble_info *info);
+/* Return the gdb.Corefile object representing the core file loaded into
+ the program space of INF, or None if there is no core file loaded. INF
+ must not be NULL. If an error occurs then NULL is returned, and a
+ suitable Python error will be set. */
+
+extern gdbpy_ref<> gdbpy_core_file_from_inferior (inferior *inf);
+
+
/* A wrapper for PyType_Ready that also automatically registers the
type in the appropriate module. Returns 0 on success, -1 on error.
If MOD is supplied, then the type is added to that module. If MOD
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 7e28eb6..51e7a0a 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -31,7 +31,6 @@
#include "python.h"
#include "extension-priv.h"
#include "cli/cli-utils.h"
-#include <ctype.h>
#include "location.h"
#include "run-on-main-thread.h"
#include "observable.h"
@@ -1202,15 +1201,22 @@ gdbpy_post_event (PyObject *self, PyObject *args)
static PyObject *
gdbpy_interrupt (PyObject *self, PyObject *args)
{
+#ifdef __MINGW32__
{
- /* Make sure the interrupt isn't delivered immediately somehow.
- This probably is not truly needed, but at the same time it
- seems more clear to be explicit about the intent. */
gdbpy_allow_threads temporarily_exit_python;
scoped_disable_cooperative_sigint_handling no_python_sigint;
set_quit_flag ();
}
+#else
+ {
+ /* For targets with support kill() just send SIGINT. This will be
+ handled as if the user hit Ctrl+C. This isn't exactly the same as
+ the above, which directly sets the quit flag. Consider, for
+ example, every place that install_sigint_handler is called. */
+ kill (getpid (), SIGINT);
+ }
+#endif
Py_RETURN_NONE;
}
@@ -1561,29 +1567,49 @@ static PyObject *
gdbpy_write (PyObject *self, PyObject *args, PyObject *kw)
{
const char *arg;
- static const char *keywords[] = { "text", "stream", NULL };
+ static const char *keywords[] = { "text", "stream", "style", nullptr };
int stream_type = 0;
+ PyObject *style_obj = Py_None;
- if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|i", keywords, &arg,
- &stream_type))
- return NULL;
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|iO", keywords, &arg,
+ &stream_type, &style_obj))
+ return nullptr;
+
+ if (style_obj != Py_None && !gdbpy_is_style (style_obj))
+ {
+ PyErr_Format
+ (PyExc_TypeError,
+ _("'style' argument must be gdb.Style or None, not %s."),
+ Py_TYPE (style_obj)->tp_name);
+ return nullptr;
+ }
try
{
+ ui_file *stream;
switch (stream_type)
{
case 1:
- {
- gdb_printf (gdb_stderr, "%s", arg);
- break;
- }
+ stream = gdb_stderr;
+ break;
case 2:
- {
- gdb_printf (gdb_stdlog, "%s", arg);
- break;
- }
+ stream = gdb_stdlog;
+ break;
default:
- gdb_printf (gdb_stdout, "%s", arg);
+ stream = gdb_stdout;
+ break;
+ }
+
+ if (style_obj == Py_None)
+ gdb_puts (arg, stream);
+ else
+ {
+ std::optional<ui_file_style> style
+ = gdbpy_style_object_to_ui_file_style (style_obj);
+ if (!style.has_value ())
+ return nullptr;
+
+ fputs_styled (arg, style.value (), stream);
}
}
catch (const gdb_exception &except)
@@ -1612,16 +1638,53 @@ gdbpy_flush (PyObject *self, PyObject *args, PyObject *kw)
{
case 1:
{
- gdb_flush (gdb_stderr);
+ if (gdb_stderr != nullptr)
+ gdb_flush (gdb_stderr);
break;
}
case 2:
{
- gdb_flush (gdb_stdlog);
+ if (gdb_stdlog != nullptr)
+ gdb_flush (gdb_stdlog);
break;
}
default:
- gdb_flush (gdb_stdout);
+ if (gdb_stdout != nullptr)
+ gdb_flush (gdb_stdout);
+ }
+
+ Py_RETURN_NONE;
+}
+
+/* Implement gdb.warning(). Takes a single text string argument and emit a
+ warning using GDB's 'warning' function. The input text string must not
+ be empty. */
+
+static PyObject *
+gdbpy_warning (PyObject *self, PyObject *args, PyObject *kw)
+{
+ const char *text;
+ static const char *keywords[] = { "text", nullptr };
+
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s", keywords, &text))
+ return nullptr;
+
+ if (strlen (text) == 0)
+ {
+ PyErr_SetString (PyExc_ValueError,
+ _("Empty text string passed to gdb.warning"));
+ return nullptr;
+ }
+
+ try
+ {
+ warning ("%s", text);
+ }
+ catch (const gdb_exception &ex)
+ {
+ /* The warning() call probably cannot throw an exception. But just
+ in case it ever does. */
+ return gdbpy_handle_gdb_exception (nullptr, ex);
}
Py_RETURN_NONE;
@@ -2443,7 +2506,7 @@ py_initialize ()
/foo/lib/pythonX.Y/...
This must be done before calling Py_Initialize. */
gdb::unique_xmalloc_ptr<char> progname
- (concat (ldirname (python_libdir.c_str ()).c_str (), SLASH_STRING, "bin",
+ (concat (gdb_ldirname (python_libdir.c_str ()).c_str (), SLASH_STRING, "bin",
SLASH_STRING, "python", (char *) NULL));
{
@@ -2680,9 +2743,7 @@ test_python ()
/* See python.h. */
cmd_list_element *python_cmd_element = nullptr;
-void _initialize_python ();
-void
-_initialize_python ()
+INIT_GDB_FILE (python)
{
cmd_list_element *python_interactive_cmd
= add_com ("python-interactive", class_obscure,
@@ -3121,6 +3182,12 @@ Return the current print options." },
METH_VARARGS | METH_KEYWORDS,
"notify_mi (name, data) -> None\n\
Output async record to MI channels if any." },
+
+ { "warning", (PyCFunction) gdbpy_warning,
+ METH_VARARGS | METH_KEYWORDS,
+ "warning (text) -> None\n\
+Print a warning." },
+
{NULL, NULL, 0, NULL}
};