aboutsummaryrefslogtreecommitdiff
path: root/gdb/python/lib
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/python/lib')
-rw-r--r--gdb/python/lib/gdb/FrameDecorator.py4
-rw-r--r--gdb/python/lib/gdb/FrameIterator.py2
-rw-r--r--gdb/python/lib/gdb/__init__.py155
-rw-r--r--gdb/python/lib/gdb/command/__init__.py2
-rw-r--r--gdb/python/lib/gdb/command/explore.py2
-rw-r--r--gdb/python/lib/gdb/command/frame_filters.py2
-rw-r--r--gdb/python/lib/gdb/command/missing_files.py2
-rw-r--r--gdb/python/lib/gdb/command/pretty_printers.py2
-rw-r--r--gdb/python/lib/gdb/command/prompt.py2
-rw-r--r--gdb/python/lib/gdb/command/type_printers.py2
-rw-r--r--gdb/python/lib/gdb/command/unwinders.py2
-rw-r--r--gdb/python/lib/gdb/command/xmethods.py4
-rw-r--r--gdb/python/lib/gdb/dap/__init__.py4
-rw-r--r--gdb/python/lib/gdb/dap/breakpoint.py16
-rw-r--r--gdb/python/lib/gdb/dap/bt.py2
-rw-r--r--gdb/python/lib/gdb/dap/completions.py63
-rw-r--r--gdb/python/lib/gdb/dap/disassemble.py18
-rw-r--r--gdb/python/lib/gdb/dap/evaluate.py8
-rw-r--r--gdb/python/lib/gdb/dap/events.py9
-rw-r--r--gdb/python/lib/gdb/dap/frames.py18
-rw-r--r--gdb/python/lib/gdb/dap/globalvars.py15
-rw-r--r--gdb/python/lib/gdb/dap/io.py2
-rw-r--r--gdb/python/lib/gdb/dap/launch.py3
-rw-r--r--gdb/python/lib/gdb/dap/locations.py2
-rw-r--r--gdb/python/lib/gdb/dap/memory.py2
-rw-r--r--gdb/python/lib/gdb/dap/modules.py2
-rw-r--r--gdb/python/lib/gdb/dap/next.py17
-rw-r--r--gdb/python/lib/gdb/dap/pause.py2
-rw-r--r--gdb/python/lib/gdb/dap/scopes.py27
-rw-r--r--gdb/python/lib/gdb/dap/server.py204
-rw-r--r--gdb/python/lib/gdb/dap/sources.py11
-rw-r--r--gdb/python/lib/gdb/dap/startup.py2
-rw-r--r--gdb/python/lib/gdb/dap/state.py2
-rw-r--r--gdb/python/lib/gdb/dap/threads.py25
-rw-r--r--gdb/python/lib/gdb/dap/typecheck.py2
-rw-r--r--gdb/python/lib/gdb/dap/varref.py87
-rw-r--r--gdb/python/lib/gdb/disassembler.py4
-rw-r--r--gdb/python/lib/gdb/frames.py2
-rw-r--r--gdb/python/lib/gdb/function/__init__.py2
-rw-r--r--gdb/python/lib/gdb/function/as_string.py2
-rw-r--r--gdb/python/lib/gdb/function/caller_is.py2
-rw-r--r--gdb/python/lib/gdb/function/strfns.py2
-rw-r--r--gdb/python/lib/gdb/missing_debug.py2
-rw-r--r--gdb/python/lib/gdb/missing_files.py2
-rw-r--r--gdb/python/lib/gdb/missing_objfile.py2
-rw-r--r--gdb/python/lib/gdb/printer/__init__.py2
-rw-r--r--gdb/python/lib/gdb/printing.py2
-rw-r--r--gdb/python/lib/gdb/prompt.py2
-rw-r--r--gdb/python/lib/gdb/ptwrite.py2
-rw-r--r--gdb/python/lib/gdb/styling.py13
-rw-r--r--gdb/python/lib/gdb/types.py2
-rw-r--r--gdb/python/lib/gdb/unwinder.py2
-rw-r--r--gdb/python/lib/gdb/xmethod.py2
53 files changed, 480 insertions, 291 deletions
diff --git a/gdb/python/lib/gdb/FrameDecorator.py b/gdb/python/lib/gdb/FrameDecorator.py
index 5cdfbe1..fa6effa 100644
--- a/gdb/python/lib/gdb/FrameDecorator.py
+++ b/gdb/python/lib/gdb/FrameDecorator.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -174,7 +174,7 @@ class FrameDecorator(_FrameDecoratorBase):
sub-classed from FrameDecorator. If Decorator1 just overrides the
'function' method, then all of the other methods are carried out
by the super-class FrameDecorator. But Decorator2 may have
- overriden other methods, so FrameDecorator will look at the
+ overridden other methods, so FrameDecorator will look at the
'base' parameter and defer to that class's methods. And so on,
down the chain."""
diff --git a/gdb/python/lib/gdb/FrameIterator.py b/gdb/python/lib/gdb/FrameIterator.py
index 75176c3..54534fe 100644
--- a/gdb/python/lib/gdb/FrameIterator.py
+++ b/gdb/python/lib/gdb/FrameIterator.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 146a963..cedd897 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,17 +19,22 @@ 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 (
+ STDERR,
+ STDOUT,
+ Command,
+ execute,
+ flush,
+ parameter,
+ selected_inferior,
+ write,
+)
# isort: split
@@ -60,14 +65,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 +194,7 @@ def GdbSetPythonDirectory(dir):
def current_progspace():
"Return the current Progspace."
- return _gdb.selected_inferior().progspace
+ return selected_inferior().progspace
def objfiles():
@@ -226,14 +231,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 +397,121 @@ 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()
diff --git a/gdb/python/lib/gdb/command/__init__.py b/gdb/python/lib/gdb/command/__init__.py
index f1b13bd..3688152 100644
--- a/gdb/python/lib/gdb/command/__init__.py
+++ b/gdb/python/lib/gdb/command/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/explore.py b/gdb/python/lib/gdb/command/explore.py
index e359fa5..6107338 100644
--- a/gdb/python/lib/gdb/command/explore.py
+++ b/gdb/python/lib/gdb/command/explore.py
@@ -1,5 +1,5 @@
# GDB 'explore' command.
-# Copyright (C) 2012-2024 Free Software Foundation, Inc.
+# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/frame_filters.py b/gdb/python/lib/gdb/command/frame_filters.py
index 4e1b320..be7be9a 100644
--- a/gdb/python/lib/gdb/command/frame_filters.py
+++ b/gdb/python/lib/gdb/command/frame_filters.py
@@ -1,5 +1,5 @@
# Frame-filter commands.
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/missing_files.py b/gdb/python/lib/gdb/command/missing_files.py
index 463853b..09d9684 100644
--- a/gdb/python/lib/gdb/command/missing_files.py
+++ b/gdb/python/lib/gdb/command/missing_files.py
@@ -1,6 +1,6 @@
# Missing debug and objfile related commands.
#
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/pretty_printers.py b/gdb/python/lib/gdb/command/pretty_printers.py
index cb9b9f3..f62d329 100644
--- a/gdb/python/lib/gdb/command/pretty_printers.py
+++ b/gdb/python/lib/gdb/command/pretty_printers.py
@@ -1,5 +1,5 @@
# Pretty-printer commands.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/prompt.py b/gdb/python/lib/gdb/command/prompt.py
index 2cfb25d..6574c83 100644
--- a/gdb/python/lib/gdb/command/prompt.py
+++ b/gdb/python/lib/gdb/command/prompt.py
@@ -1,5 +1,5 @@
# Extended prompt.
-# Copyright (C) 2011-2024 Free Software Foundation, Inc.
+# Copyright (C) 2011-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/type_printers.py b/gdb/python/lib/gdb/command/type_printers.py
index a2be226..9fc654c 100644
--- a/gdb/python/lib/gdb/command/type_printers.py
+++ b/gdb/python/lib/gdb/command/type_printers.py
@@ -1,5 +1,5 @@
# Type printer commands.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/unwinders.py b/gdb/python/lib/gdb/command/unwinders.py
index b863b33..ffedab8 100644
--- a/gdb/python/lib/gdb/command/unwinders.py
+++ b/gdb/python/lib/gdb/command/unwinders.py
@@ -1,5 +1,5 @@
# Unwinder commands.
-# Copyright 2015-2024 Free Software Foundation, Inc.
+# Copyright 2015-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/command/xmethods.py b/gdb/python/lib/gdb/command/xmethods.py
index f786227..719c146 100644
--- a/gdb/python/lib/gdb/command/xmethods.py
+++ b/gdb/python/lib/gdb/command/xmethods.py
@@ -1,5 +1,5 @@
# Xmethod commands.
-# Copyright 2013-2024 Free Software Foundation, Inc.
+# Copyright 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@ def parse_xm_command_args(arg):
Returns:
A 3-tuple: (<locus matching regular expression>,
<matcher matching regular expression>,
- <name matching regular experession>)
+ <name matching regular expression>)
"""
argv = gdb.string_to_argv(arg)
argc = len(argv)
diff --git a/gdb/python/lib/gdb/dap/__init__.py b/gdb/python/lib/gdb/dap/__init__.py
index 145aeb6..1c3cf8e 100644
--- a/gdb/python/lib/gdb/dap/__init__.py
+++ b/gdb/python/lib/gdb/dap/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@ from . import startup
# server object. "F401" is the flake8 "imported but unused" code.
from . import breakpoint # noqa: F401
from . import bt # noqa: F401
+from . import completions # noqa: F401
from . import disassemble # noqa: F401
from . import evaluate # noqa: F401
from . import launch # noqa: F401
@@ -95,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 f0fe073..4d4ca18 100644
--- a/gdb/python/lib/gdb/dap/breakpoint.py
+++ b/gdb/python/lib/gdb/dap/breakpoint.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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",
@@ -151,7 +148,6 @@ def _remove_entries(table, *names):
# the breakpoint.
@in_gdb_thread
def _set_breakpoints_callback(kind, specs, creator):
- global breakpoint_map
# Try to reuse existing breakpoints if possible.
if kind in breakpoint_map:
saved_map = breakpoint_map[kind]
@@ -218,11 +214,11 @@ class _PrintBreakpoint(gdb.Breakpoint):
def __init__(self, logMessage, **args):
super().__init__(**args)
# Split the message up for easier processing.
- self.message = re.split("{(.*?)}", logMessage)
+ self._message = re.split("{(.*?)}", logMessage)
def stop(self):
output = ""
- for idx, item in enumerate(self.message):
+ for idx, item in enumerate(self._message):
if idx % 2 == 0:
# Even indices are plain text.
output += item
@@ -330,7 +326,7 @@ 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]
@@ -363,7 +359,7 @@ def _rewrite_insn_breakpoint(
}
-@request("setInstructionBreakpoints")
+@request("setInstructionBreakpoints", expect_stopped=False)
@capability("supportsInstructionBreakpoints")
def set_insn_breakpoints(
*, breakpoints: Sequence, offset: Optional[int] = None, **args
@@ -414,7 +410,7 @@ def _rewrite_exception_breakpoint(
}
-@request("setExceptionBreakpoints")
+@request("setExceptionBreakpoints", expect_stopped=False)
@capability("supportsExceptionFilterOptions")
@capability(
"exceptionBreakpointFilters",
diff --git a/gdb/python/lib/gdb/dap/bt.py b/gdb/python/lib/gdb/dap/bt.py
index 0fefa69..41c7d00 100644
--- a/gdb/python/lib/gdb/dap/bt.py
+++ b/gdb/python/lib/gdb/dap/bt.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/completions.py b/gdb/python/lib/gdb/dap/completions.py
new file mode 100644
index 0000000..e5003ff
--- /dev/null
+++ b/gdb/python/lib/gdb/dap/completions.py
@@ -0,0 +1,63 @@
+# Copyright 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from typing import Optional
+
+from .frames import select_frame
+from .server import capability, import_column, import_line, request
+from .startup import exec_mi_and_log
+
+
+@request("completions")
+@capability("supportsCompletionsRequest")
+@capability("completionTriggerCharacters", [" ", "."])
+def completions(
+ *,
+ frameId: Optional[int] = None,
+ text: str,
+ column: int,
+ line: Optional[int] = None,
+ **extra,
+):
+ if frameId is not None:
+ select_frame(frameId)
+
+ column = import_column(column)
+ if line is None:
+ line = 1
+ else:
+ line = import_line(line)
+ if text:
+ text = text.splitlines()[line - 1]
+ text = text[: column - 1]
+ else:
+ text = ""
+ mi_result = exec_mi_and_log("-complete", text)
+ result = []
+ completion = None
+ if "completion" in mi_result:
+ completion = mi_result["completion"]
+ result.append({"label": completion, "length": len(completion)})
+ # If `-complete' finds one match then `completion' and `matches'
+ # will contain the same one match.
+ if (
+ completion is not None
+ and len(mi_result["matches"]) == 1
+ and completion == mi_result["matches"][0]
+ ):
+ return {"targets": result}
+ for match in mi_result["matches"]:
+ result.append({"label": match, "length": len(match)})
+ return {"targets": result}
diff --git a/gdb/python/lib/gdb/dap/disassemble.py b/gdb/python/lib/gdb/dap/disassemble.py
index 5389803..42cad3e 100644
--- a/gdb/python/lib/gdb/dap/disassemble.py
+++ b/gdb/python/lib/gdb/dap/disassemble.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -26,30 +26,30 @@ class _BlockTracker:
# Map from PC to symbol names. A given PC is assumed to have
# just one label -- DAP wouldn't let us return multiple labels
# anyway.
- self.labels = {}
+ self._labels = {}
# Blocks that have already been handled.
- self.blocks = set()
+ self._blocks = set()
# Add a gdb.Block and its superblocks, ignoring the static and
# global block. BLOCK can also be None, which is ignored.
def add_block(self, block):
while block is not None:
- if block.is_static or block.is_global or block in self.blocks:
+ if block.is_static or block.is_global or block in self._blocks:
return
- self.blocks.add(block)
+ self._blocks.add(block)
if block.function is not None:
- self.labels[block.start] = block.function.name
+ self._labels[block.start] = block.function.name
for sym in block:
if sym.addr_class == gdb.SYMBOL_LOC_LABEL:
- self.labels[int(sym.value())] = sym.name
+ self._labels[int(sym.value())] = sym.name
block = block.superblock
# Add PC to this tracker. Update RESULT as appropriate with
# information about the source and any label.
def add_pc(self, pc, result):
self.add_block(gdb.block_for_pc(pc))
- if pc in self.labels:
- result["symbol"] = self.labels[pc]
+ if pc in self._labels:
+ result["symbol"] = self._labels[pc]
sal = gdb.find_pc_line(pc)
if sal.symtab is not None:
if sal.line != 0:
diff --git a/gdb/python/lib/gdb/dap/evaluate.py b/gdb/python/lib/gdb/dap/evaluate.py
index 34843f4..fcbcc99 100644
--- a/gdb/python/lib/gdb/dap/evaluate.py
+++ b/gdb/python/lib/gdb/dap/evaluate.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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 2e6fe98..e8f2655 100644
--- a/gdb/python/lib/gdb/dap/events.py
+++ b/gdb/python/lib/gdb/dap/events.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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
@@ -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 f4e6565..4dacb87 100644
--- a/gdb/python/lib/gdb/dap/frames.py
+++ b/gdb/python/lib/gdb/dap/frames.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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]
@@ -75,16 +74,16 @@ def select_frame(id):
# what is needed for the current callers.
class _MemoizingIterator:
def __init__(self, iterator):
- self.iterator = iterator
- self.seen = []
+ self._iterator = iterator
+ self._seen = []
def __iter__(self):
# First the memoized items.
- for item in self.seen:
+ for item in self._seen:
yield item
# Now memoize new items.
- for item in self.iterator:
- self.seen.append(item)
+ for item in self._iterator:
+ self._seen.append(item)
yield item
@@ -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 104b242..9d64d28 100644
--- a/gdb/python/lib/gdb/dap/globalvars.py
+++ b/gdb/python/lib/gdb/dap/globalvars.py
@@ -1,4 +1,4 @@
-# Copyright 2024 Free Software Foundation, Inc.
+# Copyright 2024-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -37,8 +37,8 @@ gdb.events.cont.connect(clear)
class _Globals(BaseReference):
def __init__(self, filename, var_list):
super().__init__("Globals")
- self.filename = filename
- self.var_list = var_list
+ self._filename = filename
+ self._var_list = var_list
def to_object(self):
result = super().to_object()
@@ -46,8 +46,8 @@ class _Globals(BaseReference):
# How would we know?
result["expensive"] = False
result["namedVariables"] = self.child_count()
- if self.filename is not None:
- result["source"] = make_source(self.filename)
+ if self._filename is not None:
+ result["source"] = make_source(self._filename)
return result
def has_children(self):
@@ -56,11 +56,11 @@ class _Globals(BaseReference):
return True
def child_count(self):
- return len(self.var_list)
+ return len(self._var_list)
@in_gdb_thread
def fetch_one_child(self, idx):
- sym = self.var_list[idx]
+ sym = self._var_list[idx]
return (sym.name, sym.value())
@@ -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/io.py b/gdb/python/lib/gdb/dap/io.py
index 03031a7..45890da 100644
--- a/gdb/python/lib/gdb/dap/io.py
+++ b/gdb/python/lib/gdb/dap/io.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py
index fc1890c..8ac4c77 100644
--- a/gdb/python/lib/gdb/dap/launch.py
+++ b/gdb/python/lib/gdb/dap/launch.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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/locations.py b/gdb/python/lib/gdb/dap/locations.py
index 1ef5a34..fffc038 100644
--- a/gdb/python/lib/gdb/dap/locations.py
+++ b/gdb/python/lib/gdb/dap/locations.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/memory.py b/gdb/python/lib/gdb/dap/memory.py
index 4aa4996..d0f8825 100644
--- a/gdb/python/lib/gdb/dap/memory.py
+++ b/gdb/python/lib/gdb/dap/memory.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/modules.py b/gdb/python/lib/gdb/dap/modules.py
index 69e5a40..b06f771 100644
--- a/gdb/python/lib/gdb/dap/modules.py
+++ b/gdb/python/lib/gdb/dap/modules.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/next.py b/gdb/python/lib/gdb/dap/next.py
index 7e06b1b..898fff1 100644
--- a/gdb/python/lib/gdb/dap/next.py
+++ b/gdb/python/lib/gdb/dap/next.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
import gdb
from .events import exec_and_expect_stop
-from .server import capability, request, send_gdb, send_gdb_with_response
+from .server import capability, request
from .startup import in_gdb_thread
from .state import set_thread
@@ -73,19 +73,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)
-# 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/pause.py b/gdb/python/lib/gdb/dap/pause.py
index d874a60..c254e45 100644
--- a/gdb/python/lib/gdb/dap/pause.py
+++ b/gdb/python/lib/gdb/dap/pause.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/scopes.py b/gdb/python/lib/gdb/dap/scopes.py
index 221ae35..7ce3a7f 100644
--- a/gdb/python/lib/gdb/dap/scopes.py
+++ b/gdb/python/lib/gdb/dap/scopes.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@ from .sources import make_source
from .startup import in_gdb_thread
from .varref import BaseReference
-# Map DAP frame IDs to scopes. This ensures that scopes are re-used.
+# Map DAP frame IDs to scopes. This ensures that scopes are reused.
frame_to_scope = {}
@@ -78,19 +78,19 @@ def symbol_value(sym, frame):
class _ScopeReference(BaseReference):
def __init__(self, name, hint, frameId: int, var_list):
super().__init__(name)
- self.hint = hint
- self.frameId = frameId
+ self._hint = hint
+ self._frameId = frameId
# VAR_LIST might be any kind of iterator, but it's convenient
# here if it is just a collection.
- self.var_list = tuple(var_list)
+ self._var_list = tuple(var_list)
def to_object(self):
result = super().to_object()
- result["presentationHint"] = self.hint
+ result["presentationHint"] = self._hint
# How would we know?
result["expensive"] = False
result["namedVariables"] = self.child_count()
- frame = frame_for_id(self.frameId)
+ frame = frame_for_id(self._frameId)
if frame.line() is not None:
result["line"] = export_line(frame.line())
filename = frame.filename()
@@ -102,11 +102,11 @@ class _ScopeReference(BaseReference):
return True
def child_count(self):
- return len(self.var_list)
+ return len(self._var_list)
@in_gdb_thread
def fetch_one_child(self, idx):
- return symbol_value(self.var_list[idx], frame_for_id(self.frameId))
+ return symbol_value(self._var_list[idx], frame_for_id(self._frameId))
# A _ScopeReference that wraps the 'finish' value. Note that this
@@ -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)
@@ -136,17 +135,15 @@ class _RegisterReference(_ScopeReference):
@in_gdb_thread
def fetch_one_child(self, idx):
return (
- self.var_list[idx].name,
- frame_for_id(self.frameId)
+ self._var_list[idx].name,
+ frame_for_id(self._frameId)
.inferior_frame()
- .read_register(self.var_list[idx]),
+ .read_register(self._var_list[idx]),
)
@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 6f3af73..7dab582 100644
--- a/gdb/python/lib/gdb/dap/server.py
+++ b/gdb/python/lib/gdb/dap/server.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -49,6 +49,7 @@ _server = None
# This is set by the initialize request and is used when rewriting
# line numbers.
_lines_start_at_1 = False
+_columns_start_at_1 = False
class DeferredRequest:
@@ -73,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.
@@ -94,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
@@ -115,10 +126,10 @@ class CancellationHandler:
# The request currently being handled, or None.
self.in_flight_dap_thread = None
self.in_flight_gdb_thread = None
- self.reqs = []
+ self._reqs = []
# A set holding the request IDs of all deferred requests that
# are still unresolved.
- self.deferred_ids = set()
+ self._deferred_ids = set()
@contextmanager
def current_request(self, req):
@@ -138,7 +149,7 @@ class CancellationHandler:
def defer_request(self, req):
"""Indicate that the request REQ has been deferred."""
with self.lock:
- self.deferred_ids.add(req)
+ self._deferred_ids.add(req)
def request_finished(self, req):
"""Indicate that the request REQ is finished.
@@ -150,7 +161,7 @@ class CancellationHandler:
with self.lock:
# Use discard here, not remove, because this is called
# regardless of whether REQ was deferred.
- self.deferred_ids.discard(req)
+ self._deferred_ids.discard(req)
def check_cancel(self, req):
"""Check whether request REQ is cancelled.
@@ -163,15 +174,15 @@ class CancellationHandler:
deferred = []
try:
# If the request is cancelled, don't execute the region.
- while len(self.reqs) > 0 and self.reqs[0] <= req:
+ while len(self._reqs) > 0 and self._reqs[0] <= req:
# In most cases, if we see a cancellation request
# on the heap that is before REQ, we can just
# ignore it -- we missed our chance to cancel that
# request.
- next_id = heapq.heappop(self.reqs)
+ next_id = heapq.heappop(self._reqs)
if next_id == req:
raise KeyboardInterrupt()
- elif next_id in self.deferred_ids:
+ elif next_id in self._deferred_ids:
# We could be in a situation where we're
# processing request 23, but request 18 is
# still deferred. In this case, popping
@@ -180,7 +191,7 @@ class CancellationHandler:
deferred.append(next_id)
finally:
for x in deferred:
- heapq.heappush(self.reqs, x)
+ heapq.heappush(self._reqs, x)
def cancel(self, req):
"""Call to cancel a request.
@@ -198,7 +209,7 @@ class CancellationHandler:
# the weird property that a request can be cancelled
# before it is even sent. It didn't seem worthwhile
# to try to check for this.
- heapq.heappush(self.reqs, req)
+ heapq.heappush(self._reqs, req)
@contextmanager
def interruptable_region(self, req):
@@ -225,20 +236,20 @@ class Server:
"""The DAP server class."""
def __init__(self, in_stream, out_stream, child_stream):
- self.in_stream = in_stream
- self.out_stream = out_stream
- self.child_stream = child_stream
- self.delayed_fns_lock = threading.Lock()
- self.defer_stop_events = False
- self.delayed_fns = []
+ self._in_stream = in_stream
+ self._out_stream = out_stream
+ self._child_stream = child_stream
+ self._delayed_fns_lock = threading.Lock()
+ 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
# blocking the read loop.
- self.write_queue = DAPQueue()
+ self._write_queue = DAPQueue()
# Reading is also done in a separate thread, and a queue of
# requests is kept.
- self.read_queue = DAPQueue()
- self.done = False
+ self._read_queue = DAPQueue()
+ self._done = False
self.canceller = CancellationHandler()
global _server
_server = self
@@ -306,7 +317,6 @@ class Server:
args = {}
def fn():
- global _commands
return _commands[params["command"]](**args)
self.invoke_request(req, result, fn)
@@ -315,8 +325,8 @@ class Server:
# is run in its own thread.
def _read_inferior_output(self):
while True:
- line = self.child_stream.readline()
- self.send_event(
+ line = self._child_stream.readline()
+ self.send_event_maybe_later(
"output",
{
"category": "stdout",
@@ -327,7 +337,7 @@ class Server:
# Send OBJ to the client, logging first if needed.
def _send_json(self, obj):
log("WROTE: <<<" + json.dumps(obj) + ">>>")
- self.write_queue.put(obj)
+ self._write_queue.put(obj)
# This is run in a separate thread and simply reads requests from
# the client and puts them into a queue. A separate thread is
@@ -335,7 +345,7 @@ class Server:
# will normally block, waiting for each request to complete.
def _reader_thread(self):
while True:
- cmd = read_json(self.in_stream)
+ cmd = read_json(self._in_stream)
if cmd is None:
break
log("READ: <<<" + json.dumps(cmd) + ">>>")
@@ -351,9 +361,20 @@ class Server:
and "requestId" in cmd["arguments"]
):
self.canceller.cancel(cmd["arguments"]["requestId"])
- self.read_queue.put(cmd)
+ self._read_queue.put(cmd)
# When we hit EOF, signal it with None.
- self.read_queue.put(None)
+ 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):
@@ -361,38 +382,32 @@ class Server:
# Before looping, start the thread that writes JSON to the
# client, and the thread that reads output from the inferior.
start_thread("output reader", self._read_inferior_output)
- json_writer = start_json_writer(self.out_stream, self.write_queue)
+ json_writer = start_json_writer(self._out_stream, self._write_queue)
start_thread("JSON reader", self._reader_thread)
- while not self.done:
- cmd = self.read_queue.get()
+ while not self._done:
+ cmd = self._read_queue.get()
# A None value here means the reader hit EOF.
if cmd is None:
break
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)
+ self._write_queue.put(None)
json_writer.join()
send_gdb("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."""
- with self.delayed_fns_lock:
- self.delayed_fns.append(lambda: self.send_event(event, body))
+ def set_defer_events(self):
+ """Defer any events until the current request has completed."""
+ with self._delayed_fns_lock:
+ 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,
@@ -400,22 +415,22 @@ class Server:
the client."""
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))
+ with self._delayed_fns_lock:
+ 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):
"""Call FN later -- after the current request's response has been sent."""
- with self.delayed_fns_lock:
- self.delayed_fns.append(fn)
+ with self._delayed_fns_lock:
+ self._delayed_fns.append(fn)
# 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."""
@@ -432,29 +447,18 @@ class Server:
# Just set a flag. This operation is complicated because we
# want to write the result of the request before exiting. See
# main_loop.
- self.done = True
+ self._done = True
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)
@@ -479,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.
@@ -501,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.
@@ -526,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
@@ -554,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
@@ -567,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
@@ -580,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
@@ -588,11 +595,12 @@ 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
+ _columns_start_at_1 = client_bool_capability("columnsStartAt1", True)
return _capabilities.copy()
@@ -629,19 +637,19 @@ class Invoker(object):
"""A simple class that can invoke a gdb command."""
def __init__(self, cmd):
- self.cmd = 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)
+ exec_and_log(self._cmd)
class Cancellable(object):
def __init__(self, fn, result_q=None):
- self.fn = fn
- self.result_q = result_q
+ self._fn = fn
+ self._result_q = result_q
with _server.canceller.lock:
self.req = _server.canceller.in_flight_dap_thread
@@ -650,13 +658,13 @@ class Cancellable(object):
def __call__(self):
try:
with _server.canceller.interruptable_region(self.req):
- val = self.fn()
- if self.result_q is not None:
- self.result_q.put(val)
+ val = self._fn()
+ if self._result_q is not None:
+ self._result_q.put(val)
except (Exception, KeyboardInterrupt) as e:
- if self.result_q is not None:
+ if self._result_q is not None:
# Pass result or exception to caller.
- self.result_q.put(e)
+ self._result_q.put(e)
elif isinstance(e, KeyboardInterrupt):
# Fn was cancelled.
pass
@@ -698,11 +706,10 @@ def send_gdb_with_response(fn):
return val
-def export_line(line):
+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.
@@ -710,13 +717,26 @@ def export_line(line):
return line
-def import_line(line):
+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.
line = line + 1
return line
+
+
+def export_column(column: int) -> int:
+ """Rewrite COLUMN according to client capability.
+ This applies the columnsStartAt1 capability as needed,
+ when sending a column number from gdb to the client."""
+ return column if _columns_start_at_1 else column - 1
+
+
+def import_column(column: int) -> int:
+ """Rewrite COLUMN according to client capability.
+ This applies the columnsStartAt1 capability as needed,
+ when the client sends a column number to gdb."""
+ return column if _columns_start_at_1 else column + 1
diff --git a/gdb/python/lib/gdb/dap/sources.py b/gdb/python/lib/gdb/dap/sources.py
index a9f4ea6..efcd799 100644
--- a/gdb/python/lib/gdb/dap/sources.py
+++ b/gdb/python/lib/gdb/dap/sources.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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 a3f048b..ab3e8fd 100644
--- a/gdb/python/lib/gdb/dap/startup.py
+++ b/gdb/python/lib/gdb/dap/startup.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/state.py b/gdb/python/lib/gdb/dap/state.py
index 57ae355..5fdfbb2 100644
--- a/gdb/python/lib/gdb/dap/state.py
+++ b/gdb/python/lib/gdb/dap/state.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/threads.py b/gdb/python/lib/gdb/dap/threads.py
index e65495b..89046a8 100644
--- a/gdb/python/lib/gdb/dap/threads.py
+++ b/gdb/python/lib/gdb/dap/threads.py
@@ -1,4 +1,4 @@
-# Copyright 2022-2024 Free Software Foundation, Inc.
+# Copyright 2022-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -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/typecheck.py b/gdb/python/lib/gdb/dap/typecheck.py
index 55896cc..1496b67 100644
--- a/gdb/python/lib/gdb/dap/typecheck.py
+++ b/gdb/python/lib/gdb/dap/typecheck.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/dap/varref.py b/gdb/python/lib/gdb/dap/varref.py
index 0dd9879..8a13c51 100644
--- a/gdb/python/lib/gdb/dap/varref.py
+++ b/gdb/python/lib/gdb/dap/varref.py
@@ -1,4 +1,4 @@
-# Copyright 2023-2024 Free Software Foundation, Inc.
+# Copyright 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -60,8 +60,6 @@ class BaseReference(ABC):
This class is just a base class, some methods must be implemented in
subclasses.
-
- The 'ref' field can be used as the variablesReference in the protocol.
"""
@in_gdb_thread
@@ -71,10 +69,9 @@ 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
+ self._ref = len(all_variables)
+ self._name = name
self.reset_children()
@in_gdb_thread
@@ -83,9 +80,9 @@ class BaseReference(ABC):
The resulting object is a starting point that can be filled in
further. See the Scope or Variable types in the spec"""
- result = {"variablesReference": self.ref if self.has_children() else 0}
- if self.name is not None:
- result["name"] = str(self.name)
+ result = {"variablesReference": self._ref if self.has_children() else 0}
+ if self._name is not None:
+ result["name"] = str(self._name)
return result
@abstractmethod
@@ -97,13 +94,13 @@ class BaseReference(ABC):
"""Reset any cached information about the children of this object."""
# A list of all the children. Each child is a BaseReference
# of some kind.
- self.children = None
+ self._children = None
# Map from the name of a child to a BaseReference.
- self.by_name = {}
+ self._by_name = {}
# Keep track of how many duplicates there are of a given name,
# so that unique names can be generated. Map from base name
# to a count.
- self.name_counts = defaultdict(lambda: 1)
+ self._name_counts = defaultdict(lambda: 1)
@abstractmethod
def fetch_one_child(self, index):
@@ -128,13 +125,13 @@ class BaseReference(ABC):
# and
# https://github.com/microsoft/debug-adapter-protocol/issues/149
def _compute_name(self, name):
- if name in self.by_name:
- self.name_counts[name] += 1
+ if name in self._by_name:
+ self._name_counts[name] += 1
# In theory there's no safe way to compute a name, because
# a pretty-printer might already be generating names of
# that form. In practice I think we should not worry too
# much.
- name = name + " #" + str(self.name_counts[name])
+ name = name + " #" + str(self._name_counts[name])
return name
@in_gdb_thread
@@ -146,16 +143,16 @@ class BaseReference(ABC):
Returns an iterable of some kind."""
if count == 0:
count = self.child_count()
- if self.children is None:
- self.children = [None] * self.child_count()
+ if self._children is None:
+ self._children = [None] * self.child_count()
for idx in range(start, start + count):
- if self.children[idx] is None:
+ if self._children[idx] is None:
(name, value) = self.fetch_one_child(idx)
name = self._compute_name(name)
var = VariableReference(name, value)
- self.children[idx] = var
- self.by_name[name] = var
- yield self.children[idx]
+ self._children[idx] = var
+ self._by_name[name] = var
+ yield self._children[idx]
@in_gdb_thread
def find_child_by_name(self, name):
@@ -165,8 +162,8 @@ class BaseReference(ABC):
# A lookup by name can only be done using names previously
# provided to the client, so we can simply rely on the by-name
# map here.
- if name in self.by_name:
- return self.by_name[name]
+ if name in self._by_name:
+ return self._by_name[name]
raise DAPException("no variable named '" + name + "'")
@@ -181,15 +178,15 @@ class VariableReference(BaseReference):
RESULT_NAME can be used to change how the simple string result
is emitted in the result dictionary."""
super().__init__(name)
- self.result_name = result_name
- self.value = value
+ self._result_name = result_name
+ self._value = value
self._update_value()
# Internal method to update local data when the value changes.
def _update_value(self):
self.reset_children()
- self.printer = gdb.printing.make_visualizer(self.value)
- self.child_cache = None
+ self._printer = gdb.printing.make_visualizer(self._value)
+ self._child_cache = None
if self.has_children():
self.count = -1
else:
@@ -197,32 +194,32 @@ class VariableReference(BaseReference):
def assign(self, value):
"""Assign VALUE to this object and update."""
- self.value.assign(value)
+ self._value.assign(value)
self._update_value()
def has_children(self):
- return hasattr(self.printer, "children")
+ return hasattr(self._printer, "children")
def cache_children(self):
- if self.child_cache is None:
+ if self._child_cache is None:
# This discards all laziness. This could be improved
# slightly by lazily evaluating children, but because this
# code also generally needs to know the number of
# children, it probably wouldn't help much. Note that
# this is only needed with legacy (non-ValuePrinter)
# printers.
- self.child_cache = list(self.printer.children())
- return self.child_cache
+ self._child_cache = list(self._printer.children())
+ return self._child_cache
def child_count(self):
if self.count is None:
return None
if self.count == -1:
num_children = None
- if isinstance(self.printer, gdb.ValuePrinter) and hasattr(
- self.printer, "num_children"
+ if isinstance(self._printer, gdb.ValuePrinter) and hasattr(
+ self._printer, "num_children"
):
- num_children = self.printer.num_children()
+ num_children = self._printer.num_children()
if num_children is None:
num_children = len(self.cache_children())
self.count = num_children
@@ -230,12 +227,12 @@ class VariableReference(BaseReference):
def to_object(self):
result = super().to_object()
- result[self.result_name] = str(self.printer.to_string())
+ result[self._result_name] = str(self._printer.to_string())
num_children = self.child_count()
if num_children is not None:
if (
- hasattr(self.printer, "display_hint")
- and self.printer.display_hint() == "array"
+ hasattr(self._printer, "display_hint")
+ and self._printer.display_hint() == "array"
):
result["indexedVariables"] = num_children
else:
@@ -245,18 +242,18 @@ 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:
- result["memoryReference"] = hex(int(self.value))
+ if self._value.type.strip_typedefs().code == gdb.TYPE_CODE_PTR:
+ result["memoryReference"] = hex(int(self._value))
if client_bool_capability("supportsVariableType"):
- result["type"] = str(self.value.type)
+ result["type"] = str(self._value.type)
return result
@in_gdb_thread
def fetch_one_child(self, idx):
- if isinstance(self.printer, gdb.ValuePrinter) and hasattr(
- self.printer, "child"
+ if isinstance(self._printer, gdb.ValuePrinter) and hasattr(
+ self._printer, "child"
):
- (name, val) = self.printer.child(idx)
+ (name, val) = self._printer.child(idx)
else:
(name, val) = self.cache_children()[idx]
# A pretty-printer can return something other than a
@@ -269,7 +266,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 7d0e781..8f8e768 100644
--- a/gdb/python/lib/gdb/disassembler.py
+++ b/gdb/python/lib/gdb/disassembler.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2021-2024 Free Software Foundation, Inc.
+# Copyright (C) 2021-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -81,7 +81,7 @@ def register_disassembler(disassembler, architecture=None):
# Call the private _set_enabled function within the
# _gdb.disassembler module. This function sets a global flag
- # within GDB's C++ code that enables or dissables the Python
+ # within GDB's C++ code that enables or disables the Python
# disassembler functionality, this improves performance of the
# disassembler by avoiding unneeded calls into Python when we know
# that no disassemblers are registered.
diff --git a/gdb/python/lib/gdb/frames.py b/gdb/python/lib/gdb/frames.py
index a3be80c7..96174e9 100644
--- a/gdb/python/lib/gdb/frames.py
+++ b/gdb/python/lib/gdb/frames.py
@@ -1,5 +1,5 @@
# Frame-filter commands.
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/__init__.py b/gdb/python/lib/gdb/function/__init__.py
index 4b64bc3..62b6422 100644
--- a/gdb/python/lib/gdb/function/__init__.py
+++ b/gdb/python/lib/gdb/function/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2024 Free Software Foundation, Inc.
+# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/as_string.py b/gdb/python/lib/gdb/function/as_string.py
index a255fff..ff1304f 100644
--- a/gdb/python/lib/gdb/function/as_string.py
+++ b/gdb/python/lib/gdb/function/as_string.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2016-2024 Free Software Foundation, Inc.
+# Copyright (C) 2016-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/caller_is.py b/gdb/python/lib/gdb/function/caller_is.py
index bacd8c0..3687e4c 100644
--- a/gdb/python/lib/gdb/function/caller_is.py
+++ b/gdb/python/lib/gdb/function/caller_is.py
@@ -1,5 +1,5 @@
# Caller-is functions.
-# Copyright (C) 2008-2024 Free Software Foundation, Inc.
+# Copyright (C) 2008-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/function/strfns.py b/gdb/python/lib/gdb/function/strfns.py
index 90c9cea..a620f4f 100644
--- a/gdb/python/lib/gdb/function/strfns.py
+++ b/gdb/python/lib/gdb/function/strfns.py
@@ -1,5 +1,5 @@
# Useful gdb string convenience functions.
-# Copyright (C) 2012-2024 Free Software Foundation, Inc.
+# Copyright (C) 2012-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/missing_debug.py b/gdb/python/lib/gdb/missing_debug.py
index 2c2ceba..b03aaad 100644
--- a/gdb/python/lib/gdb/missing_debug.py
+++ b/gdb/python/lib/gdb/missing_debug.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2023-2024 Free Software Foundation, Inc.
+# Copyright (C) 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/missing_files.py b/gdb/python/lib/gdb/missing_files.py
index 5f2df88c..9f24db7 100644
--- a/gdb/python/lib/gdb/missing_files.py
+++ b/gdb/python/lib/gdb/missing_files.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2023-2024 Free Software Foundation, Inc.
+# Copyright (C) 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/missing_objfile.py b/gdb/python/lib/gdb/missing_objfile.py
index ace0e13..3d06bdd 100644
--- a/gdb/python/lib/gdb/missing_objfile.py
+++ b/gdb/python/lib/gdb/missing_objfile.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2024 Free Software Foundation, Inc.
+# Copyright (C) 2024-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/printer/__init__.py b/gdb/python/lib/gdb/printer/__init__.py
index 6692044..854ff3a 100644
--- a/gdb/python/lib/gdb/printer/__init__.py
+++ b/gdb/python/lib/gdb/printer/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2014-2024 Free Software Foundation, Inc.
+# Copyright (C) 2014-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py
index 0635993..cba27d2 100644
--- a/gdb/python/lib/gdb/printing.py
+++ b/gdb/python/lib/gdb/printing.py
@@ -1,5 +1,5 @@
# Pretty-printer utilities.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/prompt.py b/gdb/python/lib/gdb/prompt.py
index 497ab83..060474c 100644
--- a/gdb/python/lib/gdb/prompt.py
+++ b/gdb/python/lib/gdb/prompt.py
@@ -1,5 +1,5 @@
# Extended prompt utilities.
-# Copyright (C) 2011-2024 Free Software Foundation, Inc.
+# Copyright (C) 2011-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/ptwrite.py b/gdb/python/lib/gdb/ptwrite.py
index 3be65fe..fcc72de 100644
--- a/gdb/python/lib/gdb/ptwrite.py
+++ b/gdb/python/lib/gdb/ptwrite.py
@@ -1,5 +1,5 @@
# Ptwrite utilities.
-# Copyright (C) 2023 Free Software Foundation, Inc.
+# Copyright (C) 2023-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/styling.py b/gdb/python/lib/gdb/styling.py
index 1c5394e..60c470f 100644
--- a/gdb/python/lib/gdb/styling.py
+++ b/gdb/python/lib/gdb/styling.py
@@ -1,5 +1,5 @@
# Styling related hooks.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@ try:
from pygments import formatters, highlight, lexers
from pygments.filters import TokenMergeFilter
from pygments.token import Comment, Error, Text
+ from pygments.util import ClassNotFound
_formatter = None
@@ -31,10 +32,13 @@ try:
_formatter = formatters.TerminalFormatter()
return _formatter
- def colorize(filename, contents):
+ def colorize(filename, contents, lang):
# Don't want any errors.
try:
- lexer = lexers.get_lexer_for_filename(filename, stripnl=False)
+ try:
+ lexer = lexers.get_lexer_by_name(lang, stripnl=False)
+ except ClassNotFound:
+ lexer = lexers.get_lexer_for_filename(filename, stripnl=False)
formatter = get_formatter()
return highlight(contents, lexer, formatter).encode(
gdb.host_charset(), "backslashreplace"
@@ -76,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())
@@ -94,7 +97,7 @@ try:
except ImportError:
- def colorize(filename, contents):
+ def colorize(filename, contents, lang):
return None
def colorize_disasm(content, gdbarch):
diff --git a/gdb/python/lib/gdb/types.py b/gdb/python/lib/gdb/types.py
index b4af59c..bac1fb9 100644
--- a/gdb/python/lib/gdb/types.py
+++ b/gdb/python/lib/gdb/types.py
@@ -1,5 +1,5 @@
# Type utilities.
-# Copyright (C) 2010-2024 Free Software Foundation, Inc.
+# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/unwinder.py b/gdb/python/lib/gdb/unwinder.py
index bb0db79..3e1f756 100644
--- a/gdb/python/lib/gdb/unwinder.py
+++ b/gdb/python/lib/gdb/unwinder.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2024 Free Software Foundation, Inc.
+# Copyright (C) 2015-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
diff --git a/gdb/python/lib/gdb/xmethod.py b/gdb/python/lib/gdb/xmethod.py
index e12d51c..310585a 100644
--- a/gdb/python/lib/gdb/xmethod.py
+++ b/gdb/python/lib/gdb/xmethod.py
@@ -1,5 +1,5 @@
# Python side of the support for xmethods.
-# Copyright (C) 2013-2024 Free Software Foundation, Inc.
+# Copyright (C) 2013-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by