diff --git a/mingw32/bin/gdb-add-index b/mingw32/bin/gdb-add-index
index 80bc4956358..00a9bea5779 100644
--- a/mingw32/bin/gdb-add-index
+++ b/mingw32/bin/gdb-add-index
@@ -113,7 +113,7 @@ trap "rm -f $tmp_files" 0
$GDB --batch -nx -iex 'set auto-load no' \
-iex 'set debuginfod enabled off' \
- -ex "file $file" -ex "save gdb-index $dwarf5 $dir" || {
+ -ex "file '$file'" -ex "save gdb-index $dwarf5 '$dir'" || {
# Just in case.
status=$?
echo "$myname: gdb error generating index for $file" 1>&2
@@ -122,7 +122,7 @@ $GDB --batch -nx -iex 'set auto-load no' \
# In some situations gdb can exit without creating an index. This is
# not an error.
-# E.g., if $file is stripped. This behaviour is akin to stripping an
+# E.g., if $file is stripped. This behavior is akin to stripping an
# already stripped binary, it's a no-op.
status=0
@@ -143,35 +143,32 @@ handle_file ()
index="$index5"
section=".debug_names"
fi
- debugstradd=false
- debugstrupdate=false
if test -s "$debugstr"; then
if ! $OBJCOPY --dump-section .debug_str="$debugstrmerge" "$fpath" \
- /dev/null 2>$debugstrerr; then
- cat >&2 $debugstrerr
+ /dev/null 2> "$debugstrerr"; then
+ cat >&2 "$debugstrerr"
exit 1
fi
+ cat "$debugstr" >>"$debugstrmerge"
if grep -q "can't dump section '.debug_str' - it does not exist" \
- $debugstrerr; then
- debugstradd=true
+ "$debugstrerr"; then
+ $OBJCOPY --add-section $section="$index" \
+ --set-section-flags $section=readonly \
+ --add-section .debug_str="$debugstrmerge" \
+ --set-section-flags .debug_str=readonly \
+ "$fpath" "$fpath"
else
- debugstrupdate=true
- cat >&2 $debugstrerr
+ $OBJCOPY --add-section $section="$index" \
+ --set-section-flags $section=readonly \
+ --update-section .debug_str="$debugstrmerge" \
+ "$fpath" "$fpath"
fi
- cat "$debugstr" >>"$debugstrmerge"
+ else
+ $OBJCOPY --add-section $section="$index" \
+ --set-section-flags $section=readonly \
+ "$fpath" "$fpath"
fi
- $OBJCOPY --add-section $section="$index" \
- --set-section-flags $section=readonly \
- $(if $debugstradd; then \
- echo --add-section .debug_str="$debugstrmerge"; \
- echo --set-section-flags .debug_str=readonly; \
- fi; \
- if $debugstrupdate; then \
- echo --update-section .debug_str="$debugstrmerge"; \
- fi) \
- "$fpath" "$fpath"
-
status=$?
else
echo "$myname: No index was created for $fpath" 1>&2
diff --git a/mingw32/bin/gdb.exe b/mingw32/bin/gdb.exe
index 08ef8e2b96a..9eca9522016 100644
Binary files a/mingw32/bin/gdb.exe and b/mingw32/bin/gdb.exe differ
diff --git a/mingw32/bin/gdbserver.exe b/mingw32/bin/gdbserver.exe
index ae68bd0ed79..27cc5dc4f7c 100644
Binary files a/mingw32/bin/gdbserver.exe and b/mingw32/bin/gdbserver.exe differ
diff --git a/mingw32/bin/gstack b/mingw32/bin/gstack
new file mode 100644
index 00000000000..d5625f7cd4a
--- /dev/null
+++ b/mingw32/bin/gstack
@@ -0,0 +1,147 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2024 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 .
+
+# Print a stack trace of a running process.
+# Similar to the gcore command, but instead of creating a core file,
+# we simply have gdb print out the stack backtrace to the terminal.
+
+GDB=${GDB:-$(command -v gdb)}
+GDBARGS=${GDBARGS:-}
+AWK=${AWK:-}
+PKGVERSION=(GDB)
+VERSION=16.1
+
+# Find an appropriate awk interpreter if one was not specified
+# via the environment.
+awk_prog=""
+if [ -z "$AWK" ]; then
+ for prog in gawk mawk nawk awk; do
+ awk_prog=$(command -v $prog)
+ test -n "$awk_prog" && break
+ done
+ AWK="$awk_prog"
+fi
+if [ ! -x "$AWK" ]; then
+ echo "$0: could not find usable awk interpreter" 1>&2
+ exit 2
+fi
+
+function print_usage() {
+ echo "Usage: $0 [-h|--help] [-v|--version] PID"
+}
+
+function print_try_help() {
+ echo "Try '$0 --help' for more information."
+}
+
+function print_help() {
+ print_usage
+ echo "Print a stack trace of a running program"
+ echo
+ echo " -h, --help Print this message then exit."
+ echo " -v, --version Print version information then exit."
+}
+
+function print_version() {
+ echo "GNU gstack (${PKGVERSION}) ${VERSION}"
+}
+
+# Parse options.
+while getopts hv-: OPT; do
+ if [ "$OPT" = "-" ]; then
+ OPT="${OPTARG%%=*}"
+ OPTARG="${OPTARG#'$OPT'}"
+ OPTARG="${OPTARG#=}"
+ fi
+
+ case "$OPT" in
+ h | help)
+ print_help
+ exit 0
+ ;;
+ v | version)
+ print_version
+ exit 0
+ ;;
+ \?)
+ # getopts has already output an error message.
+ print_try_help 1>&2
+ exit 2 ;;
+ *)
+ echo "$0: unrecognized option '--$OPT'" 1>&2
+ print_try_help 1>&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+# The sole remaining argument should be the PID of the process
+# whose backtrace is desired.
+if [ $# -ne 1 ]; then
+ print_usage 1>&2
+ exit 1
+fi
+
+PID=$1
+
+awk_script=$(cat << EOF
+BEGIN {
+ first=1
+ attach_okay=0
+}
+
+/ATTACHED/ {
+ attach_okay=1
+}
+
+/^#/ {
+ if (attach_okay) {
+ print \$0
+ }
+}
+
+/^Thread/ {
+ if (attach_okay) {
+ if (first == 0)
+ print ""
+ first=0
+ print \$0
+ }
+}
+
+END {
+if (attach_okay == 0)
+ exit 2
+}
+EOF
+ )
+
+# Run GDB and remove some unwanted noise.
+"$GDB" --quiet -nx --readnever $GDBARGS < 2:
@@ -68,10 +70,10 @@ def parse_missing_debug_command_args(arg):
)
-class InfoMissingDebugHanders(gdb.Command):
- """GDB command to list missing debug handlers.
+class InfoMissingFileHandlers(gdb.Command):
+ """GDB command to list missing HTYPE handlers.
- Usage: info missing-debug-handlers [LOCUS-REGEXP [NAME-REGEXP]]
+ Usage: info missing-HTYPE-handlers [LOCUS-REGEXP [NAME-REGEXP]]
LOCUS-REGEXP is a regular expression matching the location of the
handler. If it is omitted, all registered handlers from all
@@ -79,38 +81,47 @@ class InfoMissingDebugHanders(gdb.Command):
the handlers from the current progspace, or a regular expression
matching filenames of progspaces.
- NAME-REGEXP is a regular expression to filter missing debug
+ NAME-REGEXP is a regular expression to filter missing HTYPE
handler names. If this omitted for a specified locus, then all
registered handlers in the locus are listed.
"""
- def __init__(self):
- super().__init__("info missing-debug-handlers", gdb.COMMAND_FILES)
+ def __init__(self, handler_type):
+ # Update the doc string before calling the parent constructor,
+ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
+ # The parent constructor will grab a copy of this string to
+ # use as the commands help text.
+ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
+ super().__init__(
+ "info missing-" + handler_type + "-handlers", gdb.COMMAND_FILES
+ )
+ self.handler_type = handler_type
def list_handlers(self, title, handlers, name_re):
- """Lists the missing debug handlers whose name matches regexp.
+ """Lists the missing file handlers whose name matches regexp.
Arguments:
title: The line to print before the list.
- handlers: The list of the missing debug handlers.
+ handlers: The list of the missing file handlers.
name_re: handler name filter.
"""
+
if not handlers:
return
print(title)
- for handler in handlers:
+ for handler in gdb._filter_missing_file_handlers(handlers, self.handler_type):
if name_re.match(handler.name):
print(
" %s%s" % (handler.name, "" if handler.enabled else " [disabled]")
)
def invoke(self, arg, from_tty):
- locus_re, name_re = parse_missing_debug_command_args(arg)
+ locus_re, name_re = parse_missing_file_command_args(arg)
if locus_re.match("progspace") and locus_re.pattern != "":
cp = gdb.current_progspace()
self.list_handlers(
- "Progspace %s:" % cp.filename, cp.missing_debug_handlers, name_re
+ "Progspace %s:" % cp.filename, cp.missing_file_handlers, name_re
)
for progspace in gdb.progspaces():
@@ -125,58 +136,71 @@ def invoke(self, arg, from_tty):
msg = "Progspace %s:" % filename
self.list_handlers(
msg,
- progspace.missing_debug_handlers,
+ progspace.missing_file_handlers,
name_re,
)
# Print global handlers last, as these are invoked last.
if locus_re.match("global"):
- self.list_handlers("Global:", gdb.missing_debug_handlers, name_re)
+ self.list_handlers("Global:", gdb.missing_file_handlers, name_re)
-def do_enable_handler1(handlers, name_re, flag):
- """Enable/disable missing debug handlers whose names match given regex.
+def do_enable_handler1(handlers, name_re, flag, handler_type):
+ """Enable/disable missing file handlers whose names match given regex.
Arguments:
- handlers: The list of missing debug handlers.
+ handlers: The list of missing file handlers.
name_re: Handler name filter.
flag: A boolean indicating if we should enable or disable.
+ handler_type: A string, either 'debug' or 'objfile', use to control
+ which handlers are modified.
Returns:
The number of handlers affected.
"""
+
total = 0
- for handler in handlers:
+ for handler in gdb._filter_missing_file_handlers(handlers, handler_type):
if name_re.match(handler.name) and handler.enabled != flag:
handler.enabled = flag
total += 1
return total
-def do_enable_handler(arg, flag):
- """Enable or disable missing debug handlers."""
- (locus_re, name_re) = parse_missing_debug_command_args(arg)
+def do_enable_handler(arg, flag, handler_type):
+ """Enable or disable missing file handlers."""
+
+ (locus_re, name_re) = parse_missing_file_command_args(arg)
total = 0
if locus_re.match("global"):
- total += do_enable_handler1(gdb.missing_debug_handlers, name_re, flag)
+ total += do_enable_handler1(
+ gdb.missing_file_handlers, name_re, flag, handler_type
+ )
if locus_re.match("progspace") and locus_re.pattern != "":
total += do_enable_handler1(
- gdb.current_progspace().missing_debug_handlers, name_re, flag
+ gdb.current_progspace().missing_file_handlers, name_re, flag, handler_type
)
for progspace in gdb.progspaces():
filename = progspace.filename or ""
if locus_re.match(filename):
- total += do_enable_handler1(progspace.missing_debug_handlers, name_re, flag)
+ total += do_enable_handler1(
+ progspace.missing_file_handlers, name_re, flag, handler_type
+ )
print(
- "%d missing debug handler%s %s"
- % (total, "" if total == 1 else "s", "enabled" if flag else "disabled")
+ "%d missing %s handler%s %s"
+ % (
+ total,
+ handler_type,
+ "" if total == 1 else "s",
+ "enabled" if flag else "disabled",
+ )
)
-class EnableMissingDebugHandler(gdb.Command):
- """GDB command to enable missing debug handlers.
+class EnableMissingFileHandler(gdb.Command):
+ """GDB command to enable missing HTYPE handlers.
- Usage: enable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
+ Usage: enable missing-HTYPE-handler [LOCUS-REGEXP [NAME-REGEXP]]
LOCUS-REGEXP is a regular expression specifying the handlers to
enable. It can be 'global', 'progspace' for the current
@@ -187,18 +211,26 @@ class EnableMissingDebugHandler(gdb.Command):
in the locus are affected.
"""
- def __init__(self):
- super().__init__("enable missing-debug-handler", gdb.COMMAND_FILES)
+ def __init__(self, handler_type):
+ # Update the doc string before calling the parent constructor,
+ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
+ # The parent constructor will grab a copy of this string to
+ # use as the commands help text.
+ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
+ super().__init__(
+ "enable missing-" + handler_type + "-handler", gdb.COMMAND_FILES
+ )
+ self.handler_type = handler_type
def invoke(self, arg, from_tty):
"""GDB calls this to perform the command."""
- do_enable_handler(arg, True)
+ do_enable_handler(arg, True, self.handler_type)
-class DisableMissingDebugHandler(gdb.Command):
- """GDB command to disable missing debug handlers.
+class DisableMissingFileHandler(gdb.Command):
+ """GDB command to disable missing HTYPE handlers.
- Usage: disable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
+ Usage: disable missing-HTYPE-handler [LOCUS-REGEXP [NAME-REGEXP]]
LOCUS-REGEXP is a regular expression specifying the handlers to
enable. It can be 'global', 'progspace' for the current
@@ -209,19 +241,28 @@ class DisableMissingDebugHandler(gdb.Command):
in the locus are affected.
"""
- def __init__(self):
- super().__init__("disable missing-debug-handler", gdb.COMMAND_FILES)
+ def __init__(self, handler_type):
+ # Update the doc string before calling the parent constructor,
+ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
+ # The parent constructor will grab a copy of this string to
+ # use as the commands help text.
+ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
+ super().__init__(
+ "disable missing-" + handler_type + "-handler", gdb.COMMAND_FILES
+ )
+ self.handler_type = handler_type
def invoke(self, arg, from_tty):
"""GDB calls this to perform the command."""
- do_enable_handler(arg, False)
+ do_enable_handler(arg, False, self.handler_type)
-def register_missing_debug_handler_commands():
- """Installs the missing debug handler commands."""
- InfoMissingDebugHanders()
- EnableMissingDebugHandler()
- DisableMissingDebugHandler()
+def register_missing_file_handler_commands():
+ """Installs the missing file handler commands."""
+ for handler_type in ["debug", "objfile"]:
+ InfoMissingFileHandlers(handler_type)
+ EnableMissingFileHandler(handler_type)
+ DisableMissingFileHandler(handler_type)
-register_missing_debug_handler_commands()
+register_missing_file_handler_commands()
diff --git a/mingw32/share/gdb/python/gdb/dap/__init__.py b/mingw32/share/gdb/python/gdb/dap/__init__.py
index 51b95468a70..145aeb611fc 100644
--- a/mingw32/share/gdb/python/gdb/dap/__init__.py
+++ b/mingw32/share/gdb/python/gdb/dap/__init__.py
@@ -92,5 +92,8 @@ def pre_command_loop():
# session.
session_started = True
startup.thread_log("starting DAP server")
+ # 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/mingw32/share/gdb/python/gdb/dap/breakpoint.py b/mingw32/share/gdb/python/gdb/dap/breakpoint.py
index e60265b2f69..f0fe0734a03 100644
--- a/mingw32/share/gdb/python/gdb/dap/breakpoint.py
+++ b/mingw32/share/gdb/python/gdb/dap/breakpoint.py
@@ -21,9 +21,16 @@
import gdb
-from .server import capability, request, send_event
+from .server import capability, export_line, import_line, request, send_event
from .sources import make_source
-from .startup import DAPException, LogLevel, in_gdb_thread, log_stack, parse_and_eval
+from .startup import (
+ DAPException,
+ LogLevel,
+ exec_mi_and_log,
+ in_gdb_thread,
+ log_stack,
+ parse_and_eval,
+)
from .typecheck import type_check
# True when suppressing new breakpoint events.
@@ -97,11 +104,16 @@ def _bp_deleted(event):
@in_gdb_thread
def _breakpoint_descriptor(bp):
"Return the Breakpoint object descriptor given a gdb Breakpoint."
+ # If there are no objfiles (that is, before the launch request),
+ # we consider all breakpoints to be pending. This is done to work
+ # around the gdb oddity that setting a breakpoint by address will
+ # always succeed.
+ pending = bp.pending or len(gdb.objfiles()) == 0
result = {
"id": bp.number,
- "verified": not bp.pending,
+ "verified": not pending,
}
- if bp.pending:
+ if pending:
result["reason"] = "pending"
if bp.locations:
# Just choose the first location, because DAP doesn't allow
@@ -116,7 +128,7 @@ def _breakpoint_descriptor(bp):
result.update(
{
"source": make_source(filename),
- "line": line,
+ "line": export_line(line),
}
)
@@ -196,9 +208,9 @@ def _set_breakpoints_callback(kind, specs, creator):
}
)
- # Delete any breakpoints that were not reused.
- for entry in saved_map.values():
- entry.delete()
+ # Delete any breakpoints that were not reused.
+ for entry in saved_map.values():
+ entry.delete()
return result
@@ -269,14 +281,14 @@ def _rewrite_src_breakpoint(
):
return {
"source": source["path"],
- "line": line,
+ "line": import_line(line),
"condition": condition,
"hitCondition": hitCondition,
"logMessage": logMessage,
}
-@request("setBreakpoints")
+@request("setBreakpoints", expect_stopped=False)
@capability("supportsHitConditionalBreakpoints")
@capability("supportsConditionalBreakpoints")
@capability("supportsLogPoints")
@@ -368,10 +380,13 @@ def _catch_exception(filterId, **args):
cmd = "-catch-" + filterId
else:
raise DAPException("Invalid exception filterID: " + str(filterId))
- result = gdb.execute_mi(cmd)
+ 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"]
# A little lame that there's no more direct way.
for bp in gdb.breakpoints():
- if bp.number == result["bkptno"]:
+ if bp.number == num:
return bp
# Not a DAPException because this is definitely unexpected.
raise Exception("Could not find catchpoint after creating")
diff --git a/mingw32/share/gdb/python/gdb/dap/bt.py b/mingw32/share/gdb/python/gdb/dap/bt.py
index 668bcc7ce23..0fefa694c9a 100644
--- a/mingw32/share/gdb/python/gdb/dap/bt.py
+++ b/mingw32/share/gdb/python/gdb/dap/bt.py
@@ -21,7 +21,7 @@
from .frames import dap_frame_generator
from .modules import module_id
from .scopes import symbol_value
-from .server import capability, request
+from .server import capability, export_line, request
from .sources import make_source
from .startup import in_gdb_thread
from .state import set_thread
@@ -86,8 +86,11 @@ def _backtrace(thread_id, levels, startFrame, stack_format):
}
line = current_frame.line()
if line is not None:
- newframe["line"] = line
+ newframe["line"] = export_line(line)
if stack_format["line"]:
+ # Unclear whether export_line should be called
+ # here, but since it's just for users we pick the
+ # gdb representation.
name += ", line " + str(line)
objfile = gdb.current_progspace().objfile_for_address(pc)
if objfile is not None:
diff --git a/mingw32/share/gdb/python/gdb/dap/disassemble.py b/mingw32/share/gdb/python/gdb/dap/disassemble.py
index d65790a40b0..5389803c744 100644
--- a/mingw32/share/gdb/python/gdb/dap/disassemble.py
+++ b/mingw32/share/gdb/python/gdb/dap/disassemble.py
@@ -15,7 +15,7 @@
import gdb
-from .server import capability, request
+from .server import capability, export_line, request
from .sources import make_source
@@ -27,9 +27,8 @@ def __init__(self):
# just one label -- DAP wouldn't let us return multiple labels
# anyway.
self.labels = {}
- # List of blocks that have already been handled. Note that
- # blocks aren't hashable so a set is not used.
- self.blocks = []
+ # Blocks that have already been handled.
+ self.blocks = set()
# Add a gdb.Block and its superblocks, ignoring the static and
# global block. BLOCK can also be None, which is ignored.
@@ -37,7 +36,7 @@ def add_block(self, block):
while block is not None:
if block.is_static or block.is_global or block in self.blocks:
return
- self.blocks.append(block)
+ self.blocks.add(block)
if block.function is not None:
self.labels[block.start] = block.function.name
for sym in block:
@@ -54,7 +53,7 @@ def add_pc(self, pc, result):
sal = gdb.find_pc_line(pc)
if sal.symtab is not None:
if sal.line != 0:
- result["line"] = sal.line
+ result["line"] = export_line(sal.line)
if sal.symtab.filename is not None:
# The spec says this can be omitted in some
# situations, but it's a little simpler to just always
diff --git a/mingw32/share/gdb/python/gdb/dap/events.py b/mingw32/share/gdb/python/gdb/dap/events.py
index 276d3145b9a..2e6fe989e22 100644
--- a/mingw32/share/gdb/python/gdb/dap/events.py
+++ b/mingw32/share/gdb/python/gdb/dap/events.py
@@ -17,7 +17,7 @@
from .modules import is_module, make_module
from .scopes import set_finish_value
-from .server import send_event
+from .server import send_event, send_event_maybe_later
from .startup import exec_and_log, in_gdb_thread, log
# True when the inferior is thought to be running, False otherwise.
@@ -161,7 +161,7 @@ def expect_stop(reason: str):
@in_gdb_thread
-def exec_and_expect_stop(cmd, expected_pause=False):
+def exec_and_expect_stop(cmd, expected_pause=False, propagate_exception=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):
# continuing.
_suppress_cont = not expected_pause
# FIXME if the call fails should we clear _suppress_cont?
- exec_and_log(cmd)
+ exec_and_log(cmd, propagate_exception)
# Map from gdb stop reasons to DAP stop reasons. Some of these can't
@@ -241,7 +241,7 @@ def _on_stop(event):
global stop_reason_map
obj["reason"] = stop_reason_map[event.details["reason"]]
_expected_pause = False
- send_event("stopped", obj)
+ send_event_maybe_later("stopped", obj)
# This keeps a bit of state between the start of an inferior call and
diff --git a/mingw32/share/gdb/python/gdb/dap/frames.py b/mingw32/share/gdb/python/gdb/dap/frames.py
index 07a4e3ea793..f4e6565b943 100644
--- a/mingw32/share/gdb/python/gdb/dap/frames.py
+++ b/mingw32/share/gdb/python/gdb/dap/frames.py
@@ -14,11 +14,13 @@
# along with this program. If not, see .
import itertools
+from typing import Dict
import gdb
from gdb.frames import frame_iterator
from .startup import in_gdb_thread
+from .state import set_thread
# A list of all the frames we've reported. A frame's index in the
# list is its ID. We don't use a hash here because frames are not
@@ -29,6 +31,9 @@
# Map from a global thread ID to a memoizing frame iterator.
_iter_map = {}
+# Map from a global frame ID to a thread ID.
+thread_ids: Dict[int, int] = {}
+
# Clear all the frame IDs.
@in_gdb_thread
@@ -37,6 +42,8 @@ def _clear_frame_ids(evt):
_all_frames = []
global _iter_map
_iter_map = {}
+ global thread_ids
+ thread_ids = {}
# Clear the frame ID map whenever the inferior runs.
@@ -46,6 +53,11 @@ def _clear_frame_ids(evt):
@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]
@@ -94,6 +106,8 @@ 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
def yield_frames(iterator, for_elided):
diff --git a/mingw32/share/gdb/python/gdb/dap/globalvars.py b/mingw32/share/gdb/python/gdb/dap/globalvars.py
new file mode 100644
index 00000000000..104b242d896
--- /dev/null
+++ b/mingw32/share/gdb/python/gdb/dap/globalvars.py
@@ -0,0 +1,98 @@
+# Copyright 2024 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 .
+
+import gdb
+
+from .sources import make_source
+from .startup import in_gdb_thread
+from .varref import BaseReference
+
+# Map a block identifier to a scope object.
+_id_to_scope = {}
+
+
+# Arrange to clear the scope references when the inferior runs.
+@in_gdb_thread
+def clear(event):
+ global _id_to_scope
+ _id_to_scope = {}
+
+
+gdb.events.cont.connect(clear)
+
+
+# A scope that holds static and/or global variables.
+class _Globals(BaseReference):
+ def __init__(self, filename, var_list):
+ super().__init__("Globals")
+ self.filename = filename
+ self.var_list = var_list
+
+ def to_object(self):
+ result = super().to_object()
+ result["presentationHint"] = "globals"
+ # How would we know?
+ result["expensive"] = False
+ result["namedVariables"] = self.child_count()
+ if self.filename is not None:
+ result["source"] = make_source(self.filename)
+ return result
+
+ def has_children(self):
+ # This object won't even be created if there are no variables
+ # to return.
+ return True
+
+ def child_count(self):
+ return len(self.var_list)
+
+ @in_gdb_thread
+ def fetch_one_child(self, idx):
+ sym = self.var_list[idx]
+ return (sym.name, sym.value())
+
+
+@in_gdb_thread
+def get_global_scope(frame):
+ """Given a frame decorator, return the corresponding global scope
+ object.
+
+ If the frame does not have a block, or if the CU does not have
+ globals (that is, empty static and global blocks), return None."""
+ inf_frame = frame.inferior_frame()
+ # It's unfortunate that this API throws instead of returning None.
+ try:
+ block = inf_frame.block()
+ except RuntimeError:
+ return None
+
+ global _id_to_scope
+ block = block.static_block
+ if block in _id_to_scope:
+ return _id_to_scope[block]
+
+ syms = []
+ block_iter = block
+ while block_iter is not None:
+ syms += [sym for sym in block_iter if sym.is_variable and not sym.is_artificial]
+ block_iter = block_iter.superblock
+
+ if len(syms) == 0:
+ return None
+
+ result = _Globals(frame.filename(), syms)
+ _id_to_scope[block] = result
+
+ return result
diff --git a/mingw32/share/gdb/python/gdb/dap/launch.py b/mingw32/share/gdb/python/gdb/dap/launch.py
index 2674e02eac3..fc1890c5a43 100644
--- a/mingw32/share/gdb/python/gdb/dap/launch.py
+++ b/mingw32/share/gdb/python/gdb/dap/launch.py
@@ -13,20 +13,65 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import re
+
# These are deprecated in 3.9, but required in older versions.
from typing import Mapping, Optional, Sequence
import gdb
from .events import exec_and_expect_stop, expect_process, expect_stop
-from .server import capability, request
-from .startup import DAPException, exec_and_log
+from .server import (
+ DeferredRequest,
+ call_function_later,
+ capability,
+ request,
+ send_gdb,
+ send_gdb_with_response,
+)
+from .startup import DAPException, exec_and_log, in_dap_thread, in_gdb_thread
+
+# A launch or attach promise that that will be fulfilled after a
+# configurationDone request has been processed.
+_launch_or_attach_promise = None
+
+
+# A DeferredRequest that handles either a "launch" or "attach"
+# request.
+class _LaunchOrAttachDeferredRequest(DeferredRequest):
+ def __init__(self, callback):
+ self._callback = callback
+ global _launch_or_attach_promise
+ if _launch_or_attach_promise is not None:
+ raise DAPException("launch or attach already specified")
+ _launch_or_attach_promise = self
+
+ # Invoke the callback and return the result.
+ @in_dap_thread
+ def invoke(self):
+ return self._callback()
+
+ # Override this so we can clear the global when rescheduling.
+ @in_dap_thread
+ def reschedule(self):
+ global _launch_or_attach_promise
+ _launch_or_attach_promise = None
+ super().reschedule()
+
+
+# A wrapper for the 'file' command that correctly quotes its argument.
+@in_gdb_thread
+def file_command(program):
+ # Handle whitespace, quotes, and backslashes here. Exactly what
+ # to quote depends on libiberty's buildargv and safe-ctype.
+ program = re.sub("[ \t\n\r\f\v\\\\'\"]", "\\\\\\g<0>", program)
+ exec_and_log("file " + program)
# Any parameters here are necessarily extensions -- DAP requires this
# from implementations. Any additions or changes here should be
# documented in the gdb manual.
-@request("launch", response=False)
+@request("launch", on_dap_thread=True)
def launch(
*,
program: Optional[str] = None,
@@ -34,27 +79,54 @@ def launch(
args: Sequence[str] = (),
env: Optional[Mapping[str, str]] = None,
stopAtBeginningOfMainSubprogram: bool = False,
+ stopOnEntry: bool = False,
**extra,
):
- if cwd is not None:
- exec_and_log("cd " + cwd)
- if program is not None:
- exec_and_log("file " + program)
- inf = gdb.selected_inferior()
- if stopAtBeginningOfMainSubprogram:
- main = inf.main_name
- if main is not None:
- exec_and_log("tbreak " + main)
- inf.arguments = args
- if env is not None:
- inf.clear_env()
- for name, value in env.items():
- inf.set_env(name, value)
- expect_process("process")
- exec_and_expect_stop("run")
-
-
-@request("attach")
+ # Launch setup is handled here. This is done synchronously so
+ # that errors can be reported in a natural way.
+ @in_gdb_thread
+ def _setup_launch():
+ if cwd is not None:
+ exec_and_log("cd " + cwd)
+ if program is not None:
+ file_command(program)
+ inf = gdb.selected_inferior()
+ inf.arguments = args
+ if env is not None:
+ inf.clear_env()
+ for name, value in env.items():
+ inf.set_env(name, value)
+
+ # Actual launching done here. See below for more info.
+ @in_gdb_thread
+ def _do_launch():
+ expect_process("process")
+ if stopAtBeginningOfMainSubprogram:
+ cmd = "start"
+ elif stopOnEntry:
+ cmd = "starti"
+ else:
+ cmd = "run"
+ exec_and_expect_stop(cmd)
+
+ @in_dap_thread
+ def _launch_impl():
+ send_gdb_with_response(_setup_launch)
+ # We do not wait for the result here. It might be a little
+ # nicer if we did -- perhaps the various thread events would
+ # occur in a more logical sequence -- but if the inferior does
+ # not stop, then the launch response will not be seen either,
+ # which seems worse.
+ send_gdb(_do_launch)
+ # Launch response does not have a body.
+ return None
+
+ # The launch itself is deferred until the configurationDone
+ # request.
+ return _LaunchOrAttachDeferredRequest(_launch_impl)
+
+
+@request("attach", on_dap_thread=True)
def attach(
*,
program: Optional[str] = None,
@@ -62,21 +134,39 @@ def attach(
target: Optional[str] = None,
**args,
):
- if program is not None:
- exec_and_log("file " + program)
- if pid is not None:
- cmd = "attach " + str(pid)
- elif target is not None:
- cmd = "target remote " + target
- else:
- raise DAPException("attach requires either 'pid' or 'target'")
- expect_process("attach")
- expect_stop("attach")
- exec_and_log(cmd)
+ # The actual attach is handled by this function.
+ @in_gdb_thread
+ def _do_attach():
+ if program is not None:
+ file_command(program)
+ if pid is not None:
+ cmd = "attach " + str(pid)
+ elif target is not None:
+ cmd = "target remote " + target
+ else:
+ raise DAPException("attach requires either 'pid' or 'target'")
+ expect_process("attach")
+ expect_stop("attach")
+ exec_and_log(cmd)
+ # Attach response does not have a body.
+ return None
+
+ @in_dap_thread
+ def _attach_impl():
+ return send_gdb_with_response(_do_attach)
+
+ # The attach itself is deferred until the configurationDone
+ # request.
+ return _LaunchOrAttachDeferredRequest(_attach_impl)
@capability("supportsConfigurationDoneRequest")
-@request("configurationDone")
+@request("configurationDone", on_dap_thread=True)
def config_done(**args):
- # Nothing to do.
- return None
+ # 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
+ # configurationDone response has been sent.
+ call_function_later(_launch_or_attach_promise.reschedule)
diff --git a/mingw32/share/gdb/python/gdb/dap/locations.py b/mingw32/share/gdb/python/gdb/dap/locations.py
index 92e68f5e235..1ef5a34b584 100644
--- a/mingw32/share/gdb/python/gdb/dap/locations.py
+++ b/mingw32/share/gdb/python/gdb/dap/locations.py
@@ -16,10 +16,9 @@
# This is deprecated in 3.9, but required in older versions.
from typing import Optional
-import gdb
-
-from .server import capability, request
+from .server import capability, export_line, import_line, request
from .sources import decode_source
+from .startup import exec_mi_and_log
# Note that the spec says that the arguments to this are optional.
@@ -29,15 +28,18 @@
# This points out that fixing this would be an incompatibility but
# goes on to propose "if arguments property is missing, debug adapters
# should return an error".
-@request("breakpointLocations")
+@request("breakpointLocations", expect_stopped=False)
@capability("supportsBreakpointLocationsRequest")
def breakpoint_locations(*, source, line: int, endLine: Optional[int] = None, **extra):
+ line = import_line(line)
if endLine is None:
endLine = line
+ else:
+ endLine = import_line(endLine)
filename = decode_source(source)
lines = set()
- for entry in gdb.execute_mi("-symbol-list-lines", filename)["lines"]:
+ for entry in exec_mi_and_log("-symbol-list-lines", filename)["lines"]:
this_line = entry["line"]
if this_line >= line and this_line <= endLine:
- lines.add(this_line)
+ lines.add(export_line(this_line))
return {"breakpoints": [{"line": x} for x in sorted(lines)]}
diff --git a/mingw32/share/gdb/python/gdb/dap/next.py b/mingw32/share/gdb/python/gdb/dap/next.py
index 1dc1d6dd74d..7e06b1b79dc 100644
--- a/mingw32/share/gdb/python/gdb/dap/next.py
+++ b/mingw32/share/gdb/python/gdb/dap/next.py
@@ -73,10 +73,10 @@ def step_in(
exec_and_expect_stop(cmd)
-@request("stepOut", response=False)
+@request("stepOut", defer_stop_events=True)
def step_out(*, threadId: int, singleThread: bool = False, **args):
_handle_thread_step(threadId, singleThread, True)
- exec_and_expect_stop("finish")
+ exec_and_expect_stop("finish &", propagate_exception=True)
# This is a server-side request because it is funny: it wants to
diff --git a/mingw32/share/gdb/python/gdb/dap/scopes.py b/mingw32/share/gdb/python/gdb/dap/scopes.py
index 8cd860141d6..221ae35a002 100644
--- a/mingw32/share/gdb/python/gdb/dap/scopes.py
+++ b/mingw32/share/gdb/python/gdb/dap/scopes.py
@@ -16,7 +16,9 @@
import gdb
from .frames import frame_for_id
-from .server import request
+from .globalvars import get_global_scope
+from .server import export_line, request
+from .sources import make_source
from .startup import in_gdb_thread
from .varref import BaseReference
@@ -74,13 +76,10 @@ def symbol_value(sym, frame):
class _ScopeReference(BaseReference):
- def __init__(self, name, hint, frame, var_list):
+ def __init__(self, name, hint, frameId: int, var_list):
super().__init__(name)
self.hint = hint
- self.frame = frame
- self.inf_frame = frame.inferior_frame()
- self.func = frame.function()
- self.line = frame.line()
+ 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)
@@ -91,9 +90,12 @@ def to_object(self):
# How would we know?
result["expensive"] = False
result["namedVariables"] = self.child_count()
- if self.line is not None:
- result["line"] = self.line
- # FIXME construct a Source object
+ frame = frame_for_id(self.frameId)
+ if frame.line() is not None:
+ result["line"] = export_line(frame.line())
+ filename = frame.filename()
+ if filename is not None:
+ result["source"] = make_source(filename)
return result
def has_children(self):
@@ -104,36 +106,40 @@ def child_count(self):
@in_gdb_thread
def fetch_one_child(self, idx):
- return symbol_value(self.var_list[idx], self.frame)
+ return symbol_value(self.var_list[idx], frame_for_id(self.frameId))
-# A _ScopeReference that prepends the most recent return value. Note
-# that this object is only created if such a value actually exists.
+# A _ScopeReference that wraps the 'finish' value. Note that this
+# object is only created if such a value actually exists.
class _FinishScopeReference(_ScopeReference):
- def __init__(self, *args):
- super().__init__(*args)
+ def __init__(self, frameId):
+ super().__init__("Return", "returnValue", frameId, ())
def child_count(self):
- return super().child_count() + 1
+ return 1
def fetch_one_child(self, idx):
- if idx == 0:
- global _last_return_value
- return ("(return)", _last_return_value)
- return super().fetch_one_child(idx - 1)
+ assert idx == 0
+ global _last_return_value
+ return ("(return)", _last_return_value)
class _RegisterReference(_ScopeReference):
- def __init__(self, name, frame):
+ def __init__(self, name, frameId):
super().__init__(
- name, "registers", frame, frame.inferior_frame().architecture().registers()
+ name,
+ "registers",
+ frameId,
+ frame_for_id(frameId).inferior_frame().architecture().registers(),
)
@in_gdb_thread
def fetch_one_child(self, idx):
return (
self.var_list[idx].name,
- self.inf_frame.read_register(self.var_list[idx]),
+ frame_for_id(self.frameId)
+ .inferior_frame()
+ .read_register(self.var_list[idx]),
)
@@ -150,15 +156,18 @@ def scopes(*, frameId: int, **extra):
# iterator case.
args = tuple(frame.frame_args() or ())
if args:
- scopes.append(_ScopeReference("Arguments", "arguments", frame, args))
+ scopes.append(_ScopeReference("Arguments", "arguments", frameId, args))
has_return_value = frameId == 0 and _last_return_value is not None
# Make sure to handle the None case as well as the empty
# iterator case.
locs = tuple(frame.frame_locals() or ())
+ if locs:
+ scopes.append(_ScopeReference("Locals", "locals", frameId, locs))
+ scopes.append(_RegisterReference("Registers", frameId))
if has_return_value:
- scopes.append(_FinishScopeReference("Locals", "locals", frame, locs))
- elif locs:
- scopes.append(_ScopeReference("Locals", "locals", frame, locs))
- scopes.append(_RegisterReference("Registers", frame))
+ scopes.append(_FinishScopeReference(frameId))
frame_to_scope[frameId] = scopes
+ global_scope = get_global_scope(frame)
+ if global_scope is not None:
+ scopes.append(global_scope)
return {"scopes": [x.to_object() for x in scopes]}
diff --git a/mingw32/share/gdb/python/gdb/dap/server.py b/mingw32/share/gdb/python/gdb/dap/server.py
index 7eb87177710..6f3af732286 100644
--- a/mingw32/share/gdb/python/gdb/dap/server.py
+++ b/mingw32/share/gdb/python/gdb/dap/server.py
@@ -46,6 +46,56 @@
# The global server.
_server = None
+# This is set by the initialize request and is used when rewriting
+# line numbers.
+_lines_start_at_1 = False
+
+
+class DeferredRequest:
+ """If a DAP request function returns a deferred request, no
+ response is sent immediately.
+
+ Instead, request processing continues, with this particular
+ request remaining un-replied-to.
+
+ Later, when the result is available, the deferred request can be
+ scheduled. This causes 'invoke' to be called and then the
+ response to be sent to the client.
+
+ """
+
+ # This is for internal use by the server. It should not be
+ # overridden by any subclass. This adds the request ID and the
+ # result template object to this object. These are then used
+ # during rescheduling.
+ def set_request(self, req, result):
+ self._req = req
+ self._result = result
+
+ @in_dap_thread
+ def invoke(self):
+ """Implement the deferred request.
+
+ This will be called from 'reschedule' (and should not be
+ called elsewhere). It should return the 'body' that will be
+ sent in the response. None means no 'body' field will be set.
+
+ Subclasses must override this.
+
+ """
+ pass
+
+ @in_dap_thread
+ def reschedule(self):
+ """Call this to reschedule this deferred request.
+
+ This will call 'invoke' after the appropriate bookkeeping and
+ will arrange for its result to be reported to the client.
+
+ """
+ with _server.canceller.current_request(self._req):
+ _server.invoke_request(self._req, self._result, self.invoke)
+
# A subclass of Exception that is used solely for reporting that a
# request needs the inferior to be stopped, but it is not stopped.
@@ -59,21 +109,78 @@ class NotStoppedException(Exception):
class CancellationHandler:
def __init__(self):
# Methods on this class acquire this lock before proceeding.
- self.lock = threading.Lock()
+ # A recursive lock is used to simplify the 'check_cancel'
+ # callers.
+ self.lock = threading.RLock()
# The request currently being handled, or None.
self.in_flight_dap_thread = None
self.in_flight_gdb_thread = None
self.reqs = []
+ # A set holding the request IDs of all deferred requests that
+ # are still unresolved.
+ self.deferred_ids = set()
+
+ @contextmanager
+ def current_request(self, req):
+ """Return a new context manager that registers that request
+ REQ has started."""
+ try:
+ with self.lock:
+ self.in_flight_dap_thread = req
+ # Note we do not call check_cancel here. This is a bit of
+ # a hack, but it's because the direct callers of this
+ # aren't prepared for a KeyboardInterrupt.
+ yield
+ finally:
+ with self.lock:
+ self.in_flight_dap_thread = None
- def starting(self, req):
- """Call at the start of the given request."""
+ def defer_request(self, req):
+ """Indicate that the request REQ has been deferred."""
with self.lock:
- self.in_flight_dap_thread = req
+ self.deferred_ids.add(req)
- def done(self, req):
- """Indicate that the request is done."""
+ def request_finished(self, req):
+ """Indicate that the request REQ is finished.
+
+ It doesn't matter whether REQ succeeded or failed, only that
+ processing for it is done.
+
+ """
with self.lock:
- self.in_flight_dap_thread = None
+ # Use discard here, not remove, because this is called
+ # regardless of whether REQ was deferred.
+ self.deferred_ids.discard(req)
+
+ def check_cancel(self, req):
+ """Check whether request REQ is cancelled.
+ If so, raise KeyboardInterrupt."""
+ with self.lock:
+ # We want to drop any cancellations that come before REQ,
+ # but keep ones for any deferred requests that are still
+ # unresolved. This holds any such requests that were
+ # popped during the loop.
+ deferred = []
+ try:
+ # If the request is cancelled, don't execute the region.
+ 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)
+ if next_id == req:
+ raise KeyboardInterrupt()
+ 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
+ # request 18 here will lose the cancellation.
+ # So, we preserve it.
+ deferred.append(next_id)
+ finally:
+ for x in deferred:
+ heapq.heappush(self.reqs, x)
def cancel(self, req):
"""Call to cancel a request.
@@ -86,7 +193,7 @@ def cancel(self, req):
gdb.interrupt()
else:
# We don't actually ignore the request here, but in
- # the 'starting' method. This way we don't have to
+ # the 'check_cancel' method. This way we don't have to
# track as much state. Also, this implementation has
# the weird property that a request can be cancelled
# before it is even sent. It didn't seem worthwhile
@@ -103,10 +210,7 @@ def interruptable_region(self, req):
return
try:
with self.lock:
- # If the request is cancelled, don't execute the region.
- while len(self.reqs) > 0 and self.reqs[0] <= req:
- if heapq.heappop(self.reqs) == req:
- raise KeyboardInterrupt()
+ self.check_cancel(req)
# Request is being handled by the gdb thread.
self.in_flight_gdb_thread = req
# Execute region. This may be interrupted by gdb.interrupt.
@@ -124,7 +228,9 @@ 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_events = []
+ self.delayed_fns_lock = threading.Lock()
+ self.defer_stop_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.
@@ -137,27 +243,27 @@ def __init__(self, in_stream, out_stream, child_stream):
global _server
_server = self
- # Treat PARAMS as a JSON-RPC request and perform its action.
- # PARAMS is just a dictionary from the JSON.
+ # A helper for request processing. REQ is the request ID. RESULT
+ # is a result "template" -- a dictionary with a few items already
+ # filled in. This helper calls FN and then fills in the remaining
+ # parts of RESULT, as needed. If FN returns an ordinary result,
+ # or if it fails, then the final RESULT is sent as a response to
+ # the client. However, if FN returns a DeferredRequest, then that
+ # request is updated (see DeferredRequest.set_request) and no
+ # response is sent.
@in_dap_thread
- def _handle_command(self, params):
- req = params["seq"]
- result = {
- "request_seq": req,
- "type": "response",
- "command": params["command"],
- }
+ def invoke_request(self, req, result, fn):
try:
- self.canceller.starting(req)
- if "arguments" in params:
- args = params["arguments"]
- else:
- args = {}
- global _commands
- body = _commands[params["command"]](**args)
- if body is not None:
- result["body"] = body
+ self.canceller.check_cancel(req)
+ fn_result = fn()
result["success"] = True
+ if isinstance(fn_result, DeferredRequest):
+ fn_result.set_request(req, result)
+ self.canceller.defer_request(req)
+ # Do not send a response.
+ return
+ elif fn_result is not None:
+ result["body"] = fn_result
except NotStoppedException:
# This is an expected exception, and the result is clearly
# visible in the log, so do not log it.
@@ -177,8 +283,33 @@ def _handle_command(self, params):
log_stack()
result["success"] = False
result["message"] = str(e)
- self.canceller.done(req)
- return result
+
+ self.canceller.request_finished(req)
+ # We have a response for the request, so send it back to the
+ # client.
+ self._send_json(result)
+
+ # Treat PARAMS as a JSON-RPC request and perform its action.
+ # PARAMS is just a dictionary from the JSON.
+ @in_dap_thread
+ def _handle_command(self, params):
+ req = params["seq"]
+ result = {
+ "request_seq": req,
+ "type": "response",
+ "command": params["command"],
+ }
+
+ if "arguments" in params:
+ args = params["arguments"]
+ else:
+ args = {}
+
+ def fn():
+ global _commands
+ return _commands[params["command"]](**args)
+
+ self.invoke_request(req, result, fn)
# Read inferior output and sends OutputEvents to the client. It
# is run in its own thread.
@@ -237,12 +368,16 @@ def main_loop(self):
# A None value here means the reader hit EOF.
if cmd is None:
break
- result = self._handle_command(cmd)
- self._send_json(result)
- events = self.delayed_events
- self.delayed_events = []
- for event, body in events:
- self.send_event(event, body)
+ 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()
# 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.
@@ -254,7 +389,28 @@ def main_loop(self):
def send_event_later(self, event, body=None):
"""Send a DAP event back to the client, but only after the
current request has completed."""
- self.delayed_events.append((event, body))
+ with self.delayed_fns_lock:
+ self.delayed_fns.append(lambda: self.send_event(event, body))
+
+ @in_gdb_thread
+ 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,
+ wait until the response has been sent until the event is sent back to
+ 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))
+ return
+ 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)
# 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
@@ -287,6 +443,21 @@ def send_event(event, body=None):
_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)
+
+
# A helper decorator that checks whether the inferior is running.
def _check_not_running(func):
@functools.wraps(func)
@@ -307,7 +478,8 @@ def request(
*,
response: bool = True,
on_dap_thread: bool = False,
- expect_stopped: bool = True
+ expect_stopped: bool = True,
+ defer_stop_events: bool = False
):
"""A decorator for DAP requests.
@@ -328,6 +500,10 @@ def request(
fail with the 'notStopped' reason if it is processed while the
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.
"""
# Validate the parameters.
@@ -355,6 +531,11 @@ def wrap(func):
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))
@@ -394,15 +575,15 @@ def wrap(func):
return wrap
-def client_bool_capability(name):
+def client_bool_capability(name, default=False):
"""Return the value of a boolean client capability.
If the capability was not specified, or did not have boolean type,
- False is returned."""
+ 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 False
+ return default
@request("initialize", on_dap_thread=True)
@@ -410,6 +591,8 @@ def initialize(**args):
global _server, _capabilities
_server.config = args
_server.send_event_later("initialized")
+ global _lines_start_at_1
+ _lines_start_at_1 = client_bool_capability("linesStartAt1", True)
return _capabilities.copy()
@@ -513,3 +696,27 @@ def send_gdb_with_response(fn):
if isinstance(val, (Exception, KeyboardInterrupt)):
raise val
return val
+
+
+def export_line(line):
+ """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.
+ line = line - 1
+ return line
+
+
+def import_line(line):
+ """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
diff --git a/mingw32/share/gdb/python/gdb/dap/sources.py b/mingw32/share/gdb/python/gdb/dap/sources.py
index ad0c913c8c1..a9f4ea62f69 100644
--- a/mingw32/share/gdb/python/gdb/dap/sources.py
+++ b/mingw32/share/gdb/python/gdb/dap/sources.py
@@ -15,10 +15,8 @@
import os
-import gdb
-
from .server import capability, request
-from .startup import DAPException, in_gdb_thread
+from .startup import DAPException, exec_mi_and_log, in_gdb_thread
# The next available source reference ID. Must be greater than 0.
_next_source = 1
@@ -83,7 +81,7 @@ def decode_source(source):
@capability("supportsLoadedSourcesRequest")
def loaded_sources(**extra):
result = []
- for elt in gdb.execute_mi("-file-list-exec-source-files")["files"]:
+ for elt in exec_mi_and_log("-file-list-exec-source-files")["files"]:
result.append(make_source(elt["fullname"], elt["file"]))
return {
"sources": result,
diff --git a/mingw32/share/gdb/python/gdb/dap/startup.py b/mingw32/share/gdb/python/gdb/dap/startup.py
index 58591c00b97..a3f048bd396 100644
--- a/mingw32/share/gdb/python/gdb/dap/startup.py
+++ b/mingw32/share/gdb/python/gdb/dap/startup.py
@@ -204,7 +204,7 @@ def log_stack(level=LogLevel.DEFAULT):
@in_gdb_thread
-def exec_and_log(cmd):
+def exec_and_log(cmd, propagate_exception=False):
"""Execute the gdb command CMD.
If logging is enabled, log the command and its output."""
log("+++ " + cmd)
@@ -212,5 +212,15 @@ def exec_and_log(cmd):
output = gdb.execute(cmd, from_tty=True, to_string=True)
if output != "":
log(">>> " + output)
- except gdb.error:
- log_stack()
+ except gdb.error as e:
+ if propagate_exception:
+ raise DAPException(str(e)) from e
+ else:
+ log_stack()
+
+
+@in_gdb_thread
+def exec_mi_and_log(*args):
+ """Wrap gdb.execute_mi, logging the command."""
+ log("+++ " + str(args))
+ return gdb.execute_mi(*args)
diff --git a/mingw32/share/gdb/python/gdb/dap/varref.py b/mingw32/share/gdb/python/gdb/dap/varref.py
index 57e84a1676e..0dd98797086 100644
--- a/mingw32/share/gdb/python/gdb/dap/varref.py
+++ b/mingw32/share/gdb/python/gdb/dap/varref.py
@@ -18,6 +18,7 @@
from contextlib import contextmanager
import gdb
+import gdb.printing
from .server import client_bool_capability
from .startup import DAPException, in_gdb_thread
diff --git a/mingw32/share/gdb/python/gdb/disassembler.py b/mingw32/share/gdb/python/gdb/disassembler.py
index 72d311b117f..7d0e781ef21 100644
--- a/mingw32/share/gdb/python/gdb/disassembler.py
+++ b/mingw32/share/gdb/python/gdb/disassembler.py
@@ -147,7 +147,7 @@ def invoke(self, args, from_tty):
# Figure out the name of the current architecture. There
# should always be a current inferior, but if, somehow, there
# isn't, then leave curr_arch as the empty string, which will
- # not then match agaisnt any architecture in the dictionary.
+ # not then match against any architecture in the dictionary.
curr_arch = ""
if gdb.selected_inferior() is not None:
curr_arch = gdb.selected_inferior().architecture().name()
diff --git a/mingw32/share/gdb/python/gdb/missing_debug.py b/mingw32/share/gdb/python/gdb/missing_debug.py
index 6d57462c185..2c2cebacaf8 100644
--- a/mingw32/share/gdb/python/gdb/missing_debug.py
+++ b/mingw32/share/gdb/python/gdb/missing_debug.py
@@ -17,48 +17,11 @@
MissingDebugHandler base class, and register_handler function.
"""
-import sys
-
import gdb
+from gdb.missing_files import MissingFileHandler
-if sys.version_info >= (3, 7):
- # Functions str.isascii() and str.isalnum are available starting Python
- # 3.7.
- def isascii(ch):
- return ch.isascii()
-
- def isalnum(ch):
- return ch.isalnum()
-
-else:
- # Fall back to curses.ascii.isascii() and curses.ascii.isalnum() for
- # earlier versions.
- from curses.ascii import isalnum, isascii
-
-
-def _validate_name(name):
- """Validate a missing debug handler name string.
-
- If name is valid as a missing debug handler name, then this
- function does nothing. If name is not valid then an exception is
- raised.
-
- Arguments:
- name: A string, the name of a missing debug handler.
-
- Returns:
- Nothing.
- Raises:
- ValueError: If name is invalid as a missing debug handler
- name.
- """
- for ch in name:
- if not isascii(ch) or not (isalnum(ch) or ch in "_-"):
- raise ValueError("invalid character '%s' in handler name: %s" % (ch, name))
-
-
-class MissingDebugHandler(object):
+class MissingDebugHandler(MissingFileHandler):
"""Base class for missing debug handlers written in Python.
A missing debug handler has a single method __call__ along with
@@ -69,41 +32,8 @@ class MissingDebugHandler(object):
enabled: When true this handler is enabled.
"""
- def __init__(self, name):
- """Constructor.
-
- Args:
- name: An identifying name for this handler.
-
- Raises:
- TypeError: name is not a string.
- ValueError: name contains invalid characters.
- """
-
- if not isinstance(name, str):
- raise TypeError("incorrect type for name: %s" % type(name))
-
- _validate_name(name)
-
- self._name = name
- self._enabled = True
-
- @property
- def name(self):
- return self._name
-
- @property
- def enabled(self):
- return self._enabled
-
- @enabled.setter
- def enabled(self, value):
- if not isinstance(value, bool):
- raise TypeError("incorrect type for enabled attribute: %s" % type(value))
- self._enabled = value
-
def __call__(self, objfile):
- """GDB handle missing debug information for an objfile.
+ """Handle missing debug information for an objfile.
Arguments:
objfile: A gdb.Objfile for which GDB could not find any
@@ -124,62 +54,5 @@ def __call__(self, objfile):
def register_handler(locus, handler, replace=False):
- """Register handler in given locus.
-
- The handler is prepended to the locus's missing debug handlers
- list. The name of handler should be unique (or replace must be
- True).
-
- Arguments:
- locus: Either a progspace, or None (in which case the unwinder
- is registered globally).
- handler: An object of a gdb.MissingDebugHandler subclass.
-
- replace: If True, replaces existing handler with the same name
- within locus. Otherwise, raises RuntimeException if
- unwinder with the same name already exists.
-
- Returns:
- Nothing.
-
- Raises:
- RuntimeError: The name of handler is not unique.
- TypeError: Bad locus type.
- AttributeError: Required attributes of handler are missing.
- """
-
- if locus is None:
- if gdb.parameter("verbose"):
- gdb.write("Registering global %s handler ...\n" % handler.name)
- locus = gdb
- elif isinstance(locus, gdb.Progspace):
- if gdb.parameter("verbose"):
- gdb.write(
- "Registering %s handler for %s ...\n" % (handler.name, locus.filename)
- )
- else:
- raise TypeError("locus should be gdb.Progspace or None")
-
- # Some sanity checks on HANDLER. Calling getattr will raise an
- # exception if the attribute doesn't exist, which is what we want.
- # These checks are not exhaustive; we don't check the attributes
- # have the correct types, or the method has the correct signature,
- # but this should catch some basic mistakes.
- getattr(handler, "name")
- getattr(handler, "enabled")
- call_method = getattr(handler, "__call__")
- if not callable(call_method):
- raise AttributeError(
- "'%s' object's '__call__' attribute is not callable"
- % type(handler).__name__
- )
-
- i = 0
- for needle in locus.missing_debug_handlers:
- if needle.name == handler.name:
- if replace:
- del locus.missing_debug_handlers[i]
- else:
- raise RuntimeError("Handler %s already exists." % handler.name)
- i += 1
- locus.missing_debug_handlers.insert(0, handler)
+ """See gdb.missing_files.register_handler."""
+ gdb.missing_files.register_handler("debug", locus, handler, replace)
diff --git a/mingw32/share/gdb/python/gdb/missing_files.py b/mingw32/share/gdb/python/gdb/missing_files.py
new file mode 100644
index 00000000000..5f2df88c728
--- /dev/null
+++ b/mingw32/share/gdb/python/gdb/missing_files.py
@@ -0,0 +1,204 @@
+# Copyright (C) 2023-2024 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 .
+
+"""
+MissingFileHandler base class, and support functions used by the
+missing_debug.py and missing_objfile.py modules.
+"""
+
+import sys
+
+import gdb
+
+if sys.version_info >= (3, 7):
+ # Functions str.isascii() and str.isalnum are available starting Python
+ # 3.7.
+ def isascii(ch):
+ return ch.isascii()
+
+ def isalnum(ch):
+ return ch.isalnum()
+
+else:
+ # Older version of Python doesn't have str.isascii() and
+ # str.isalnum() so provide our own.
+ #
+ # We could import isalnum() and isascii() from the curses library,
+ # but that adds an extra dependency. Given these functions are
+ # both small and trivial lets implement them here.
+ #
+ # These definitions are based on those in the curses library, but
+ # simplified as we know C will always be a single character 'str'.
+
+ def isdigit(c):
+ return 48 <= ord(c) <= 57
+
+ def islower(c):
+ return 97 <= ord(c) <= 122
+
+ def isupper(c):
+ return 65 <= ord(c) <= 90
+
+ def isalpha(c):
+ return isupper(c) or islower(c)
+
+ def isalnum(c):
+ return isalpha(c) or isdigit(c)
+
+ def isascii(c):
+ return 0 <= ord(c) <= 127
+
+
+def _validate_name(name):
+ """Validate a missing file handler name string.
+
+ If name is valid as a missing file handler name, then this
+ function does nothing. If name is not valid then an exception is
+ raised.
+
+ Arguments:
+ name: A string, the name of a missing file handler.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ ValueError: If name is invalid as a missing file handler
+ name.
+ """
+
+ for ch in name:
+ if not isascii(ch) or not (isalnum(ch) or ch in "_-"):
+ raise ValueError("invalid character '%s' in handler name: %s" % (ch, name))
+
+
+class MissingFileHandler(object):
+ """Base class for missing file handlers written in Python.
+
+ A missing file handler has a single method __call__ along with the
+ read/write attribute enabled, and a read-only attribute name. The
+ attributes are provided by this class while the __call__ method is
+ provided by a sub-class. Each sub-classes __call__ method will
+ have a different signature.
+
+ Attributes:
+ name: Read-only attribute, the name of this handler.
+ enabled: When true this handler is enabled.
+ """
+
+ def __init__(self, name):
+ """Constructor.
+
+ Args:
+ name: An identifying name for this handler.
+
+ Raises:
+ TypeError: name is not a string.
+ ValueError: name contains invalid characters.
+ """
+
+ if not isinstance(name, str):
+ raise TypeError("incorrect type for name: %s" % type(name))
+
+ _validate_name(name)
+
+ self._name = name
+ self._enabled = True
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def enabled(self):
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, value):
+ if not isinstance(value, bool):
+ raise TypeError("incorrect type for enabled attribute: %s" % type(value))
+ self._enabled = value
+
+
+def register_handler(handler_type, locus, handler, replace=False):
+ """Register handler in given locus.
+
+ The handler is prepended to the locus's missing file handlers
+ list. The name of handler should be unique (or replace must be
+ True), and the name must pass the _validate_name check.
+
+ Arguments:
+ handler_type: A string, either 'debug' or 'objfile' indicating the
+ type of handler to be registered.
+ locus: Either a progspace, or None (in which case the unwinder
+ is registered globally).
+ handler: An object used as a missing file handler. Usually a
+ sub-class of MissingFileHandler.
+ replace: If True, replaces existing handler with the same name
+ within locus. Otherwise, raises RuntimeException if
+ unwinder with the same name already exists.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ RuntimeError: The name of handler is not unique.
+ TypeError: Bad locus type.
+ AttributeError: Required attributes of handler are missing.
+ ValueError: If the name of the handler is invalid, or if
+ handler_type is neither 'debug' or 'objfile'.
+ """
+
+ if handler_type != "debug" and handler_type != "objfile":
+ raise ValueError("handler_type must be 'debug' or 'objfile'")
+
+ if locus is None:
+ if gdb.parameter("verbose"):
+ gdb.write("Registering global %s handler ...\n" % handler.name)
+ locus = gdb
+ elif isinstance(locus, gdb.Progspace):
+ if gdb.parameter("verbose"):
+ gdb.write(
+ "Registering %s handler for %s ...\n" % (handler.name, locus.filename)
+ )
+ else:
+ raise TypeError("locus should be gdb.Progspace or None")
+
+ # Some sanity checks on HANDLER. Calling getattr will raise an
+ # exception if the attribute doesn't exist, which is what we want.
+ # These checks are not exhaustive; we don't check the attributes
+ # have the correct types, or the method has the correct signature,
+ # but this should catch some basic mistakes.
+ name = getattr(handler, "name")
+ _validate_name(name)
+
+ getattr(handler, "enabled")
+
+ call_method = getattr(handler, "__call__")
+ if not callable(call_method):
+ raise AttributeError(
+ "'%s' object's '__call__' attribute is not callable"
+ % type(handler).__name__
+ )
+
+ i = 0
+ for needle in locus.missing_file_handlers:
+ if needle[0] == handler_type and needle[1].name == handler.name:
+ if replace:
+ del locus.missing_file_handlers[i]
+ else:
+ raise RuntimeError("Handler %s already exists." % handler.name)
+ i += 1
+ locus.missing_file_handlers.insert(0, (handler_type, handler))
diff --git a/mingw32/share/gdb/python/gdb/missing_objfile.py b/mingw32/share/gdb/python/gdb/missing_objfile.py
new file mode 100644
index 00000000000..ace0e1315b5
--- /dev/null
+++ b/mingw32/share/gdb/python/gdb/missing_objfile.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2024 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 .
+
+"""
+MissingObjfileHandler base class, and register_handler function.
+"""
+
+import gdb
+from gdb.missing_files import MissingFileHandler
+
+
+class MissingObjfileHandler(MissingFileHandler):
+ """Base class for missing objfile handlers written in Python.
+
+ A missing objfile handler has a single method __call__ along with
+ the read/write attribute enabled, and a read-only attribute name.
+
+ Attributes:
+ name: Read-only attribute, the name of this handler.
+ enabled: When true this handler is enabled.
+ """
+
+ def __call__(self, buildid, filename):
+ """Handle a missing objfile when GDB can knows the build-id.
+
+ Arguments:
+
+ buildid: A string containing the build-id for the objfile
+ GDB is searching for.
+ filename: A string containing the name of the file GDB is
+ searching for. This is provided only for the purpose
+ of creating diagnostic messages. If the file is found
+ it does not have to be placed here, and this file
+ might already exist but GDB has determined it is not
+ suitable for use, e.g. if the build-id doesn't match.
+
+ Returns:
+
+ True: GDB should try again to locate the missing objfile,
+ the handler may have installed the missing file.
+ False: GDB should move on without the objfile. The
+ handler has determined that this objfile is not
+ available.
+ A string: GDB should load the file at the given path; it
+ contains the requested objfile.
+ None: This handler can't help with this objfile. GDB
+ should try any other registered handlers.
+
+ """
+ raise NotImplementedError("MissingObjfileHandler.__call__()")
+
+
+def register_handler(locus, handler, replace=False):
+ """See gdb.missing_files.register_handler."""
+ gdb.missing_files.register_handler("objfile", locus, handler, replace)
diff --git a/mingw32/share/gdb/python/gdb/printer/bound_registers.py b/mingw32/share/gdb/python/gdb/printer/bound_registers.py
deleted file mode 100644
index d00b455ddb9..00000000000
--- a/mingw32/share/gdb/python/gdb/printer/bound_registers.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Pretty-printers for bounds registers.
-# Copyright (C) 2013-2024 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 .
-
-import gdb
-import gdb.printing
-
-
-class MpxBound128Printer(gdb.ValuePrinter):
- """Adds size field to a mpx __gdb_builtin_type_bound128 type."""
-
- def __init__(self, val):
- self.__val = val
-
- def to_string(self):
- upper = self.__val["ubound"]
- lower = self.__val["lbound"]
- size = upper - lower
- if size > -1:
- size = size + 1
- result = "{lbound = %s, ubound = %s} : size %s" % (lower, upper, size)
- return result
-
-
-gdb.printing.add_builtin_pretty_printer(
- "mpx_bound128", "^builtin_type_bound128", MpxBound128Printer
-)
diff --git a/mingw32/share/gdb/python/gdb/printing.py b/mingw32/share/gdb/python/gdb/printing.py
index 55ba43585ec..0635993cac3 100644
--- a/mingw32/share/gdb/python/gdb/printing.py
+++ b/mingw32/share/gdb/python/gdb/printing.py
@@ -281,6 +281,44 @@ def to_string(self):
return self.__value.format_string(raw=True)
+class NoOpStringPrinter(gdb.ValuePrinter):
+ """A no-op pretty printer that wraps a string value."""
+
+ def __init__(self, ty, value):
+ self.__ty = ty
+ self.__value = value
+
+ def to_string(self):
+ # We need some special cases here.
+ #
+ # * If the gdb.Value was created from a Python string, it will
+ # be a non-lazy array -- but will have address 0 and so the
+ # contents will be lost on conversion to lazy string.
+ # (Weirdly, the .address attribute will not be 0 though.)
+ # Since conversion to lazy string is to avoid fetching too
+ # much data, and since the array is already non-lazy, just
+ # return it.
+ #
+ # * To avoid weird printing for a C "string" that is just a
+ # NULL pointer, special case this as well.
+ #
+ # * Lazy strings only understand arrays and pointers; other
+ # string-like objects (like a Rust &str) should simply be
+ # returned.
+ code = self.__ty.code
+ if code == gdb.TYPE_CODE_ARRAY and not self.__value.is_lazy:
+ return self.__value
+ elif code == gdb.TYPE_CODE_PTR and self.__value == 0:
+ return self.__value
+ elif code != gdb.TYPE_CODE_PTR and code != gdb.TYPE_CODE_ARRAY:
+ return self.__value
+ else:
+ return self.__value.lazy_string()
+
+ def display_hint(self):
+ return "string"
+
+
class NoOpPointerReferencePrinter(gdb.ValuePrinter):
"""A no-op pretty printer that wraps a pointer or reference."""
@@ -368,7 +406,7 @@ def make_visualizer(value):
else:
ty = value.type.strip_typedefs()
if ty.is_string_like:
- result = NoOpScalarPrinter(value)
+ result = NoOpStringPrinter(ty, value)
elif ty.code == gdb.TYPE_CODE_ARRAY:
result = NoOpArrayPrinter(ty, value)
elif ty.is_array_like:
diff --git a/mingw32/share/gdb/python/gdb/ptwrite.py b/mingw32/share/gdb/python/gdb/ptwrite.py
new file mode 100644
index 00000000000..3be65fedb67
--- /dev/null
+++ b/mingw32/share/gdb/python/gdb/ptwrite.py
@@ -0,0 +1,77 @@
+# Ptwrite utilities.
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+"""Utilities for working with ptwrite filters."""
+
+import gdb
+
+# _ptwrite_filter contains the per thread copies of the filter function.
+# The keys are tuples of inferior id and thread id.
+# The filter functions are created for each thread by calling the
+# _ptwrite_filter_factory.
+_ptwrite_filter = {}
+_ptwrite_filter_factory = None
+
+
+def _ptwrite_exit_handler(event):
+ """Exit handler to prune _ptwrite_filter on thread exit."""
+ _ptwrite_filter.pop(event.inferior_thread.ptid, None)
+
+
+gdb.events.thread_exited.connect(_ptwrite_exit_handler)
+
+
+def _clear_traces():
+ """Helper function to clear the trace of all threads."""
+ current_thread = gdb.selected_thread()
+
+ for inferior in gdb.inferiors():
+ for thread in inferior.threads():
+ thread.switch()
+ recording = gdb.current_recording()
+ if recording is not None:
+ recording.clear()
+
+ current_thread.switch()
+
+
+def register_filter_factory(filter_factory_):
+ """Register the ptwrite filter factory."""
+ if filter_factory_ is not None and not callable(filter_factory_):
+ raise TypeError("The filter factory must be callable or 'None'.")
+
+ # Clear the traces of all threads of all inferiors to force
+ # re-decoding with the new filter.
+ _clear_traces()
+
+ _ptwrite_filter.clear()
+ global _ptwrite_filter_factory
+ _ptwrite_filter_factory = filter_factory_
+
+
+def get_filter():
+ """Returns the filter of the current thread."""
+ thread = gdb.selected_thread()
+ key = thread.ptid
+
+ # Create a new filter for new threads.
+ if key not in _ptwrite_filter:
+ if _ptwrite_filter_factory is not None:
+ _ptwrite_filter[key] = _ptwrite_filter_factory(thread)
+ else:
+ return None
+
+ return _ptwrite_filter[key]
diff --git a/mingw32/share/gdb/python/gdb/xmethod.py b/mingw32/share/gdb/python/gdb/xmethod.py
index c98402d271f..e12d51c2c95 100644
--- a/mingw32/share/gdb/python/gdb/xmethod.py
+++ b/mingw32/share/gdb/python/gdb/xmethod.py
@@ -266,9 +266,14 @@ def register_xmethod_matcher(locus, matcher, replace=False):
del locus.xmethods[index]
else:
raise RuntimeError(
- "Xmethod matcher already registered with "
- "%s: %s" % (locus_name, matcher.name)
+ "Xmethod matcher already registered with {}: {}".format(
+ locus_name, matcher.name
+ )
)
if gdb.parameter("verbose"):
- gdb.write("Registering xmethod matcher '%s' with %s' ...\n")
+ gdb.write(
+ "Registering xmethod matcher '{}' with '{}' ...\n".format(
+ locus_name, matcher.name
+ )
+ )
locus.xmethods.insert(0, matcher)
diff --git a/mingw32/share/gdb/syscalls/aarch64-linux.xml b/mingw32/share/gdb/syscalls/aarch64-linux.xml
index 14caea5ab07..a56b1af45d2 100644
--- a/mingw32/share/gdb/syscalls/aarch64-linux.xml
+++ b/mingw32/share/gdb/syscalls/aarch64-linux.xml
@@ -4,8 +4,12 @@
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
- notice and this notice are preserved. This file is offered as-is,
- without any warranty. -->
+ notice and this notice are preserved. -->
+
@@ -251,7 +255,6 @@
-
@@ -265,5 +268,69 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/amd64-linux.xml b/mingw32/share/gdb/syscalls/amd64-linux.xml
index ddc0b37f3f7..a0e31836338 100644
--- a/mingw32/share/gdb/syscalls/amd64-linux.xml
+++ b/mingw32/share/gdb/syscalls/amd64-linux.xml
@@ -346,6 +346,7 @@
+
@@ -384,4 +385,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/arm-linux.xml b/mingw32/share/gdb/syscalls/arm-linux.xml
index 72a64566133..1cd40b7b45d 100644
--- a/mingw32/share/gdb/syscalls/arm-linux.xml
+++ b/mingw32/share/gdb/syscalls/arm-linux.xml
@@ -4,14 +4,13 @@
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
- notice and this notice are preserved. This file is offered as-is,
- without any warranty. -->
-
+
+ The files mentioned above belong to the Linux Kernel. -->
@@ -72,7 +71,7 @@
-
+
@@ -387,9 +386,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/i386-linux.xml b/mingw32/share/gdb/syscalls/i386-linux.xml
index 531bf73da45..4df3a779e76 100644
--- a/mingw32/share/gdb/syscalls/i386-linux.xml
+++ b/mingw32/share/gdb/syscalls/i386-linux.xml
@@ -462,4 +462,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/loongarch-linux.xml b/mingw32/share/gdb/syscalls/loongarch-linux.xml
index 6e85dbd7166..9130e45b42b 100644
--- a/mingw32/share/gdb/syscalls/loongarch-linux.xml
+++ b/mingw32/share/gdb/syscalls/loongarch-linux.xml
@@ -9,29 +9,7 @@
- The file mentioned above belongs to the Linux Kernel.
-
- Note that the system header file /usr/include/asm-generic/unistd.h
- may be different with the latest upstream Linux kernel uapi header
- file include/uapi/asm-generic/unistd.h, it is better to copy the
- upstream header file into the system header file when generating
- loongarch-linux.xml.in.
-
- There exist some __NR3264_ prefixed syscall numbers, replace them
- with digital numbers according to /usr/include/asm-generic/unistd.h
- and sort them by syscall number manually, maybe we can modify the
- script to do it automatically in the future.
-
-
-
-
-
-
-
-
-
-
--->
+ The file mentioned above belongs to the Linux Kernel. -->
@@ -111,6 +89,8 @@
+
+
@@ -345,5 +325,10 @@
-
+
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/mips-n32-linux.xml b/mingw32/share/gdb/syscalls/mips-n32-linux.xml
index 911cb4158ef..3c6e8ddd43b 100644
--- a/mingw32/share/gdb/syscalls/mips-n32-linux.xml
+++ b/mingw32/share/gdb/syscalls/mips-n32-linux.xml
@@ -398,4 +398,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/mips-n64-linux.xml b/mingw32/share/gdb/syscalls/mips-n64-linux.xml
index 858a42333e7..5ef2d693906 100644
--- a/mingw32/share/gdb/syscalls/mips-n64-linux.xml
+++ b/mingw32/share/gdb/syscalls/mips-n64-linux.xml
@@ -373,4 +373,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/mips-o32-linux.xml b/mingw32/share/gdb/syscalls/mips-o32-linux.xml
index 345d2201003..71e5d1f2c43 100644
--- a/mingw32/share/gdb/syscalls/mips-o32-linux.xml
+++ b/mingw32/share/gdb/syscalls/mips-o32-linux.xml
@@ -438,4 +438,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/ppc-linux.xml b/mingw32/share/gdb/syscalls/ppc-linux.xml
index 0a3131407db..812abaa59d8 100644
--- a/mingw32/share/gdb/syscalls/ppc-linux.xml
+++ b/mingw32/share/gdb/syscalls/ppc-linux.xml
@@ -453,4 +453,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/ppc64-linux.xml b/mingw32/share/gdb/syscalls/ppc64-linux.xml
index 99435187098..0e4b3335542 100644
--- a/mingw32/share/gdb/syscalls/ppc64-linux.xml
+++ b/mingw32/share/gdb/syscalls/ppc64-linux.xml
@@ -425,4 +425,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/s390-linux.xml b/mingw32/share/gdb/syscalls/s390-linux.xml
index 9c2933284a3..bc6b4c310fb 100644
--- a/mingw32/share/gdb/syscalls/s390-linux.xml
+++ b/mingw32/share/gdb/syscalls/s390-linux.xml
@@ -443,4 +443,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/s390x-linux.xml b/mingw32/share/gdb/syscalls/s390x-linux.xml
index d3da20f9300..03e70bfc549 100644
--- a/mingw32/share/gdb/syscalls/s390x-linux.xml
+++ b/mingw32/share/gdb/syscalls/s390x-linux.xml
@@ -391,4 +391,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/sparc-linux.xml b/mingw32/share/gdb/syscalls/sparc-linux.xml
index cc2971bb2a7..b33e8c879ce 100644
--- a/mingw32/share/gdb/syscalls/sparc-linux.xml
+++ b/mingw32/share/gdb/syscalls/sparc-linux.xml
@@ -441,4 +441,9 @@
+
+
+
+
+
diff --git a/mingw32/share/gdb/syscalls/sparc64-linux.xml b/mingw32/share/gdb/syscalls/sparc64-linux.xml
index f69dd9f0245..f96035f6484 100644
--- a/mingw32/share/gdb/syscalls/sparc64-linux.xml
+++ b/mingw32/share/gdb/syscalls/sparc64-linux.xml
@@ -404,4 +404,9 @@
+
+
+
+
+
diff --git a/mingw32/share/locale/de/LC_MESSAGES/opcodes.mo b/mingw32/share/locale/de/LC_MESSAGES/opcodes.mo
index fd9bd7b0d21..4e30221075b 100644
Binary files a/mingw32/share/locale/de/LC_MESSAGES/opcodes.mo and b/mingw32/share/locale/de/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw32/share/locale/es/LC_MESSAGES/bfd.mo b/mingw32/share/locale/es/LC_MESSAGES/bfd.mo
index 48b357e9123..7fbb9bd18e9 100644
Binary files a/mingw32/share/locale/es/LC_MESSAGES/bfd.mo and b/mingw32/share/locale/es/LC_MESSAGES/bfd.mo differ
diff --git a/mingw32/share/locale/fr/LC_MESSAGES/bfd.mo b/mingw32/share/locale/fr/LC_MESSAGES/bfd.mo
index fc52247d310..ebeb7cb9fdf 100644
Binary files a/mingw32/share/locale/fr/LC_MESSAGES/bfd.mo and b/mingw32/share/locale/fr/LC_MESSAGES/bfd.mo differ
diff --git a/mingw32/share/locale/fr/LC_MESSAGES/opcodes.mo b/mingw32/share/locale/fr/LC_MESSAGES/opcodes.mo
index 6166f2cfe85..4bba71e7877 100644
Binary files a/mingw32/share/locale/fr/LC_MESSAGES/opcodes.mo and b/mingw32/share/locale/fr/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw32/share/locale/ms/LC_MESSAGES/bfd.mo b/mingw32/share/locale/ms/LC_MESSAGES/bfd.mo
new file mode 100644
index 00000000000..2777b3b74f2
Binary files /dev/null and b/mingw32/share/locale/ms/LC_MESSAGES/bfd.mo differ
diff --git a/mingw32/share/locale/ro/LC_MESSAGES/bfd.mo b/mingw32/share/locale/ro/LC_MESSAGES/bfd.mo
index c781327497a..7a5f7595b9a 100644
Binary files a/mingw32/share/locale/ro/LC_MESSAGES/bfd.mo and b/mingw32/share/locale/ro/LC_MESSAGES/bfd.mo differ
diff --git a/mingw32/share/locale/ro/LC_MESSAGES/opcodes.mo b/mingw32/share/locale/ro/LC_MESSAGES/opcodes.mo
index 1ebde6faae1..82cb1b94a73 100644
Binary files a/mingw32/share/locale/ro/LC_MESSAGES/opcodes.mo and b/mingw32/share/locale/ro/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw32/share/locale/ru/LC_MESSAGES/bfd.mo b/mingw32/share/locale/ru/LC_MESSAGES/bfd.mo
index 6708c32d3b2..ddcb9ae804e 100644
Binary files a/mingw32/share/locale/ru/LC_MESSAGES/bfd.mo and b/mingw32/share/locale/ru/LC_MESSAGES/bfd.mo differ
diff --git a/mingw32/share/locale/sr/LC_MESSAGES/bfd.mo b/mingw32/share/locale/sr/LC_MESSAGES/bfd.mo
index 6db738f45d0..e6f41d7ee64 100644
Binary files a/mingw32/share/locale/sr/LC_MESSAGES/bfd.mo and b/mingw32/share/locale/sr/LC_MESSAGES/bfd.mo differ
diff --git a/mingw32/share/locale/sr/LC_MESSAGES/opcodes.mo b/mingw32/share/locale/sr/LC_MESSAGES/opcodes.mo
index 86992217b7e..145d60f2e7b 100644
Binary files a/mingw32/share/locale/sr/LC_MESSAGES/opcodes.mo and b/mingw32/share/locale/sr/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw32/share/locale/uk/LC_MESSAGES/bfd.mo b/mingw32/share/locale/uk/LC_MESSAGES/bfd.mo
index 92c7b2daed9..77660e1bf66 100644
Binary files a/mingw32/share/locale/uk/LC_MESSAGES/bfd.mo and b/mingw32/share/locale/uk/LC_MESSAGES/bfd.mo differ
diff --git a/mingw32/share/locale/uk/LC_MESSAGES/opcodes.mo b/mingw32/share/locale/uk/LC_MESSAGES/opcodes.mo
index 3924ae5df10..f35a36e6176 100644
Binary files a/mingw32/share/locale/uk/LC_MESSAGES/opcodes.mo and b/mingw32/share/locale/uk/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw64/bin/gdb-add-index b/mingw64/bin/gdb-add-index
index 80bc4956358..00a9bea5779 100644
--- a/mingw64/bin/gdb-add-index
+++ b/mingw64/bin/gdb-add-index
@@ -113,7 +113,7 @@ trap "rm -f $tmp_files" 0
$GDB --batch -nx -iex 'set auto-load no' \
-iex 'set debuginfod enabled off' \
- -ex "file $file" -ex "save gdb-index $dwarf5 $dir" || {
+ -ex "file '$file'" -ex "save gdb-index $dwarf5 '$dir'" || {
# Just in case.
status=$?
echo "$myname: gdb error generating index for $file" 1>&2
@@ -122,7 +122,7 @@ $GDB --batch -nx -iex 'set auto-load no' \
# In some situations gdb can exit without creating an index. This is
# not an error.
-# E.g., if $file is stripped. This behaviour is akin to stripping an
+# E.g., if $file is stripped. This behavior is akin to stripping an
# already stripped binary, it's a no-op.
status=0
@@ -143,35 +143,32 @@ handle_file ()
index="$index5"
section=".debug_names"
fi
- debugstradd=false
- debugstrupdate=false
if test -s "$debugstr"; then
if ! $OBJCOPY --dump-section .debug_str="$debugstrmerge" "$fpath" \
- /dev/null 2>$debugstrerr; then
- cat >&2 $debugstrerr
+ /dev/null 2> "$debugstrerr"; then
+ cat >&2 "$debugstrerr"
exit 1
fi
+ cat "$debugstr" >>"$debugstrmerge"
if grep -q "can't dump section '.debug_str' - it does not exist" \
- $debugstrerr; then
- debugstradd=true
+ "$debugstrerr"; then
+ $OBJCOPY --add-section $section="$index" \
+ --set-section-flags $section=readonly \
+ --add-section .debug_str="$debugstrmerge" \
+ --set-section-flags .debug_str=readonly \
+ "$fpath" "$fpath"
else
- debugstrupdate=true
- cat >&2 $debugstrerr
+ $OBJCOPY --add-section $section="$index" \
+ --set-section-flags $section=readonly \
+ --update-section .debug_str="$debugstrmerge" \
+ "$fpath" "$fpath"
fi
- cat "$debugstr" >>"$debugstrmerge"
+ else
+ $OBJCOPY --add-section $section="$index" \
+ --set-section-flags $section=readonly \
+ "$fpath" "$fpath"
fi
- $OBJCOPY --add-section $section="$index" \
- --set-section-flags $section=readonly \
- $(if $debugstradd; then \
- echo --add-section .debug_str="$debugstrmerge"; \
- echo --set-section-flags .debug_str=readonly; \
- fi; \
- if $debugstrupdate; then \
- echo --update-section .debug_str="$debugstrmerge"; \
- fi) \
- "$fpath" "$fpath"
-
status=$?
else
echo "$myname: No index was created for $fpath" 1>&2
diff --git a/mingw64/bin/gdb.exe b/mingw64/bin/gdb.exe
index 70555a8f04a..71d3fa74e02 100755
Binary files a/mingw64/bin/gdb.exe and b/mingw64/bin/gdb.exe differ
diff --git a/mingw64/bin/gdbserver.exe b/mingw64/bin/gdbserver.exe
index 02b243b99c6..73031a577a6 100755
Binary files a/mingw64/bin/gdbserver.exe and b/mingw64/bin/gdbserver.exe differ
diff --git a/mingw64/bin/gstack b/mingw64/bin/gstack
new file mode 100644
index 00000000000..d5625f7cd4a
--- /dev/null
+++ b/mingw64/bin/gstack
@@ -0,0 +1,147 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2024 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 .
+
+# Print a stack trace of a running process.
+# Similar to the gcore command, but instead of creating a core file,
+# we simply have gdb print out the stack backtrace to the terminal.
+
+GDB=${GDB:-$(command -v gdb)}
+GDBARGS=${GDBARGS:-}
+AWK=${AWK:-}
+PKGVERSION=(GDB)
+VERSION=16.1
+
+# Find an appropriate awk interpreter if one was not specified
+# via the environment.
+awk_prog=""
+if [ -z "$AWK" ]; then
+ for prog in gawk mawk nawk awk; do
+ awk_prog=$(command -v $prog)
+ test -n "$awk_prog" && break
+ done
+ AWK="$awk_prog"
+fi
+if [ ! -x "$AWK" ]; then
+ echo "$0: could not find usable awk interpreter" 1>&2
+ exit 2
+fi
+
+function print_usage() {
+ echo "Usage: $0 [-h|--help] [-v|--version] PID"
+}
+
+function print_try_help() {
+ echo "Try '$0 --help' for more information."
+}
+
+function print_help() {
+ print_usage
+ echo "Print a stack trace of a running program"
+ echo
+ echo " -h, --help Print this message then exit."
+ echo " -v, --version Print version information then exit."
+}
+
+function print_version() {
+ echo "GNU gstack (${PKGVERSION}) ${VERSION}"
+}
+
+# Parse options.
+while getopts hv-: OPT; do
+ if [ "$OPT" = "-" ]; then
+ OPT="${OPTARG%%=*}"
+ OPTARG="${OPTARG#'$OPT'}"
+ OPTARG="${OPTARG#=}"
+ fi
+
+ case "$OPT" in
+ h | help)
+ print_help
+ exit 0
+ ;;
+ v | version)
+ print_version
+ exit 0
+ ;;
+ \?)
+ # getopts has already output an error message.
+ print_try_help 1>&2
+ exit 2 ;;
+ *)
+ echo "$0: unrecognized option '--$OPT'" 1>&2
+ print_try_help 1>&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND-1))
+
+# The sole remaining argument should be the PID of the process
+# whose backtrace is desired.
+if [ $# -ne 1 ]; then
+ print_usage 1>&2
+ exit 1
+fi
+
+PID=$1
+
+awk_script=$(cat << EOF
+BEGIN {
+ first=1
+ attach_okay=0
+}
+
+/ATTACHED/ {
+ attach_okay=1
+}
+
+/^#/ {
+ if (attach_okay) {
+ print \$0
+ }
+}
+
+/^Thread/ {
+ if (attach_okay) {
+ if (first == 0)
+ print ""
+ first=0
+ print \$0
+ }
+}
+
+END {
+if (attach_okay == 0)
+ exit 2
+}
+EOF
+ )
+
+# Run GDB and remove some unwanted noise.
+"$GDB" --quiet -nx --readnever $GDBARGS < 2:
@@ -68,10 +70,10 @@ def parse_missing_debug_command_args(arg):
)
-class InfoMissingDebugHanders(gdb.Command):
- """GDB command to list missing debug handlers.
+class InfoMissingFileHandlers(gdb.Command):
+ """GDB command to list missing HTYPE handlers.
- Usage: info missing-debug-handlers [LOCUS-REGEXP [NAME-REGEXP]]
+ Usage: info missing-HTYPE-handlers [LOCUS-REGEXP [NAME-REGEXP]]
LOCUS-REGEXP is a regular expression matching the location of the
handler. If it is omitted, all registered handlers from all
@@ -79,38 +81,47 @@ class InfoMissingDebugHanders(gdb.Command):
the handlers from the current progspace, or a regular expression
matching filenames of progspaces.
- NAME-REGEXP is a regular expression to filter missing debug
+ NAME-REGEXP is a regular expression to filter missing HTYPE
handler names. If this omitted for a specified locus, then all
registered handlers in the locus are listed.
"""
- def __init__(self):
- super().__init__("info missing-debug-handlers", gdb.COMMAND_FILES)
+ def __init__(self, handler_type):
+ # Update the doc string before calling the parent constructor,
+ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
+ # The parent constructor will grab a copy of this string to
+ # use as the commands help text.
+ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
+ super().__init__(
+ "info missing-" + handler_type + "-handlers", gdb.COMMAND_FILES
+ )
+ self.handler_type = handler_type
def list_handlers(self, title, handlers, name_re):
- """Lists the missing debug handlers whose name matches regexp.
+ """Lists the missing file handlers whose name matches regexp.
Arguments:
title: The line to print before the list.
- handlers: The list of the missing debug handlers.
+ handlers: The list of the missing file handlers.
name_re: handler name filter.
"""
+
if not handlers:
return
print(title)
- for handler in handlers:
+ for handler in gdb._filter_missing_file_handlers(handlers, self.handler_type):
if name_re.match(handler.name):
print(
" %s%s" % (handler.name, "" if handler.enabled else " [disabled]")
)
def invoke(self, arg, from_tty):
- locus_re, name_re = parse_missing_debug_command_args(arg)
+ locus_re, name_re = parse_missing_file_command_args(arg)
if locus_re.match("progspace") and locus_re.pattern != "":
cp = gdb.current_progspace()
self.list_handlers(
- "Progspace %s:" % cp.filename, cp.missing_debug_handlers, name_re
+ "Progspace %s:" % cp.filename, cp.missing_file_handlers, name_re
)
for progspace in gdb.progspaces():
@@ -125,58 +136,71 @@ def invoke(self, arg, from_tty):
msg = "Progspace %s:" % filename
self.list_handlers(
msg,
- progspace.missing_debug_handlers,
+ progspace.missing_file_handlers,
name_re,
)
# Print global handlers last, as these are invoked last.
if locus_re.match("global"):
- self.list_handlers("Global:", gdb.missing_debug_handlers, name_re)
+ self.list_handlers("Global:", gdb.missing_file_handlers, name_re)
-def do_enable_handler1(handlers, name_re, flag):
- """Enable/disable missing debug handlers whose names match given regex.
+def do_enable_handler1(handlers, name_re, flag, handler_type):
+ """Enable/disable missing file handlers whose names match given regex.
Arguments:
- handlers: The list of missing debug handlers.
+ handlers: The list of missing file handlers.
name_re: Handler name filter.
flag: A boolean indicating if we should enable or disable.
+ handler_type: A string, either 'debug' or 'objfile', use to control
+ which handlers are modified.
Returns:
The number of handlers affected.
"""
+
total = 0
- for handler in handlers:
+ for handler in gdb._filter_missing_file_handlers(handlers, handler_type):
if name_re.match(handler.name) and handler.enabled != flag:
handler.enabled = flag
total += 1
return total
-def do_enable_handler(arg, flag):
- """Enable or disable missing debug handlers."""
- (locus_re, name_re) = parse_missing_debug_command_args(arg)
+def do_enable_handler(arg, flag, handler_type):
+ """Enable or disable missing file handlers."""
+
+ (locus_re, name_re) = parse_missing_file_command_args(arg)
total = 0
if locus_re.match("global"):
- total += do_enable_handler1(gdb.missing_debug_handlers, name_re, flag)
+ total += do_enable_handler1(
+ gdb.missing_file_handlers, name_re, flag, handler_type
+ )
if locus_re.match("progspace") and locus_re.pattern != "":
total += do_enable_handler1(
- gdb.current_progspace().missing_debug_handlers, name_re, flag
+ gdb.current_progspace().missing_file_handlers, name_re, flag, handler_type
)
for progspace in gdb.progspaces():
filename = progspace.filename or ""
if locus_re.match(filename):
- total += do_enable_handler1(progspace.missing_debug_handlers, name_re, flag)
+ total += do_enable_handler1(
+ progspace.missing_file_handlers, name_re, flag, handler_type
+ )
print(
- "%d missing debug handler%s %s"
- % (total, "" if total == 1 else "s", "enabled" if flag else "disabled")
+ "%d missing %s handler%s %s"
+ % (
+ total,
+ handler_type,
+ "" if total == 1 else "s",
+ "enabled" if flag else "disabled",
+ )
)
-class EnableMissingDebugHandler(gdb.Command):
- """GDB command to enable missing debug handlers.
+class EnableMissingFileHandler(gdb.Command):
+ """GDB command to enable missing HTYPE handlers.
- Usage: enable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
+ Usage: enable missing-HTYPE-handler [LOCUS-REGEXP [NAME-REGEXP]]
LOCUS-REGEXP is a regular expression specifying the handlers to
enable. It can be 'global', 'progspace' for the current
@@ -187,18 +211,26 @@ class EnableMissingDebugHandler(gdb.Command):
in the locus are affected.
"""
- def __init__(self):
- super().__init__("enable missing-debug-handler", gdb.COMMAND_FILES)
+ def __init__(self, handler_type):
+ # Update the doc string before calling the parent constructor,
+ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
+ # The parent constructor will grab a copy of this string to
+ # use as the commands help text.
+ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
+ super().__init__(
+ "enable missing-" + handler_type + "-handler", gdb.COMMAND_FILES
+ )
+ self.handler_type = handler_type
def invoke(self, arg, from_tty):
"""GDB calls this to perform the command."""
- do_enable_handler(arg, True)
+ do_enable_handler(arg, True, self.handler_type)
-class DisableMissingDebugHandler(gdb.Command):
- """GDB command to disable missing debug handlers.
+class DisableMissingFileHandler(gdb.Command):
+ """GDB command to disable missing HTYPE handlers.
- Usage: disable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
+ Usage: disable missing-HTYPE-handler [LOCUS-REGEXP [NAME-REGEXP]]
LOCUS-REGEXP is a regular expression specifying the handlers to
enable. It can be 'global', 'progspace' for the current
@@ -209,19 +241,28 @@ class DisableMissingDebugHandler(gdb.Command):
in the locus are affected.
"""
- def __init__(self):
- super().__init__("disable missing-debug-handler", gdb.COMMAND_FILES)
+ def __init__(self, handler_type):
+ # Update the doc string before calling the parent constructor,
+ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
+ # The parent constructor will grab a copy of this string to
+ # use as the commands help text.
+ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
+ super().__init__(
+ "disable missing-" + handler_type + "-handler", gdb.COMMAND_FILES
+ )
+ self.handler_type = handler_type
def invoke(self, arg, from_tty):
"""GDB calls this to perform the command."""
- do_enable_handler(arg, False)
+ do_enable_handler(arg, False, self.handler_type)
-def register_missing_debug_handler_commands():
- """Installs the missing debug handler commands."""
- InfoMissingDebugHanders()
- EnableMissingDebugHandler()
- DisableMissingDebugHandler()
+def register_missing_file_handler_commands():
+ """Installs the missing file handler commands."""
+ for handler_type in ["debug", "objfile"]:
+ InfoMissingFileHandlers(handler_type)
+ EnableMissingFileHandler(handler_type)
+ DisableMissingFileHandler(handler_type)
-register_missing_debug_handler_commands()
+register_missing_file_handler_commands()
diff --git a/mingw64/share/gdb/python/gdb/dap/__init__.py b/mingw64/share/gdb/python/gdb/dap/__init__.py
index 51b95468a70..145aeb611fc 100644
--- a/mingw64/share/gdb/python/gdb/dap/__init__.py
+++ b/mingw64/share/gdb/python/gdb/dap/__init__.py
@@ -92,5 +92,8 @@ def pre_command_loop():
# session.
session_started = True
startup.thread_log("starting DAP server")
+ # 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/mingw64/share/gdb/python/gdb/dap/breakpoint.py b/mingw64/share/gdb/python/gdb/dap/breakpoint.py
index e60265b2f69..f0fe0734a03 100644
--- a/mingw64/share/gdb/python/gdb/dap/breakpoint.py
+++ b/mingw64/share/gdb/python/gdb/dap/breakpoint.py
@@ -21,9 +21,16 @@
import gdb
-from .server import capability, request, send_event
+from .server import capability, export_line, import_line, request, send_event
from .sources import make_source
-from .startup import DAPException, LogLevel, in_gdb_thread, log_stack, parse_and_eval
+from .startup import (
+ DAPException,
+ LogLevel,
+ exec_mi_and_log,
+ in_gdb_thread,
+ log_stack,
+ parse_and_eval,
+)
from .typecheck import type_check
# True when suppressing new breakpoint events.
@@ -97,11 +104,16 @@ def _bp_deleted(event):
@in_gdb_thread
def _breakpoint_descriptor(bp):
"Return the Breakpoint object descriptor given a gdb Breakpoint."
+ # If there are no objfiles (that is, before the launch request),
+ # we consider all breakpoints to be pending. This is done to work
+ # around the gdb oddity that setting a breakpoint by address will
+ # always succeed.
+ pending = bp.pending or len(gdb.objfiles()) == 0
result = {
"id": bp.number,
- "verified": not bp.pending,
+ "verified": not pending,
}
- if bp.pending:
+ if pending:
result["reason"] = "pending"
if bp.locations:
# Just choose the first location, because DAP doesn't allow
@@ -116,7 +128,7 @@ def _breakpoint_descriptor(bp):
result.update(
{
"source": make_source(filename),
- "line": line,
+ "line": export_line(line),
}
)
@@ -196,9 +208,9 @@ def _set_breakpoints_callback(kind, specs, creator):
}
)
- # Delete any breakpoints that were not reused.
- for entry in saved_map.values():
- entry.delete()
+ # Delete any breakpoints that were not reused.
+ for entry in saved_map.values():
+ entry.delete()
return result
@@ -269,14 +281,14 @@ def _rewrite_src_breakpoint(
):
return {
"source": source["path"],
- "line": line,
+ "line": import_line(line),
"condition": condition,
"hitCondition": hitCondition,
"logMessage": logMessage,
}
-@request("setBreakpoints")
+@request("setBreakpoints", expect_stopped=False)
@capability("supportsHitConditionalBreakpoints")
@capability("supportsConditionalBreakpoints")
@capability("supportsLogPoints")
@@ -368,10 +380,13 @@ def _catch_exception(filterId, **args):
cmd = "-catch-" + filterId
else:
raise DAPException("Invalid exception filterID: " + str(filterId))
- result = gdb.execute_mi(cmd)
+ 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"]
# A little lame that there's no more direct way.
for bp in gdb.breakpoints():
- if bp.number == result["bkptno"]:
+ if bp.number == num:
return bp
# Not a DAPException because this is definitely unexpected.
raise Exception("Could not find catchpoint after creating")
diff --git a/mingw64/share/gdb/python/gdb/dap/bt.py b/mingw64/share/gdb/python/gdb/dap/bt.py
index 668bcc7ce23..0fefa694c9a 100644
--- a/mingw64/share/gdb/python/gdb/dap/bt.py
+++ b/mingw64/share/gdb/python/gdb/dap/bt.py
@@ -21,7 +21,7 @@
from .frames import dap_frame_generator
from .modules import module_id
from .scopes import symbol_value
-from .server import capability, request
+from .server import capability, export_line, request
from .sources import make_source
from .startup import in_gdb_thread
from .state import set_thread
@@ -86,8 +86,11 @@ def _backtrace(thread_id, levels, startFrame, stack_format):
}
line = current_frame.line()
if line is not None:
- newframe["line"] = line
+ newframe["line"] = export_line(line)
if stack_format["line"]:
+ # Unclear whether export_line should be called
+ # here, but since it's just for users we pick the
+ # gdb representation.
name += ", line " + str(line)
objfile = gdb.current_progspace().objfile_for_address(pc)
if objfile is not None:
diff --git a/mingw64/share/gdb/python/gdb/dap/disassemble.py b/mingw64/share/gdb/python/gdb/dap/disassemble.py
index d65790a40b0..5389803c744 100644
--- a/mingw64/share/gdb/python/gdb/dap/disassemble.py
+++ b/mingw64/share/gdb/python/gdb/dap/disassemble.py
@@ -15,7 +15,7 @@
import gdb
-from .server import capability, request
+from .server import capability, export_line, request
from .sources import make_source
@@ -27,9 +27,8 @@ def __init__(self):
# just one label -- DAP wouldn't let us return multiple labels
# anyway.
self.labels = {}
- # List of blocks that have already been handled. Note that
- # blocks aren't hashable so a set is not used.
- self.blocks = []
+ # Blocks that have already been handled.
+ self.blocks = set()
# Add a gdb.Block and its superblocks, ignoring the static and
# global block. BLOCK can also be None, which is ignored.
@@ -37,7 +36,7 @@ def add_block(self, block):
while block is not None:
if block.is_static or block.is_global or block in self.blocks:
return
- self.blocks.append(block)
+ self.blocks.add(block)
if block.function is not None:
self.labels[block.start] = block.function.name
for sym in block:
@@ -54,7 +53,7 @@ def add_pc(self, pc, result):
sal = gdb.find_pc_line(pc)
if sal.symtab is not None:
if sal.line != 0:
- result["line"] = sal.line
+ result["line"] = export_line(sal.line)
if sal.symtab.filename is not None:
# The spec says this can be omitted in some
# situations, but it's a little simpler to just always
diff --git a/mingw64/share/gdb/python/gdb/dap/events.py b/mingw64/share/gdb/python/gdb/dap/events.py
index 276d3145b9a..2e6fe989e22 100644
--- a/mingw64/share/gdb/python/gdb/dap/events.py
+++ b/mingw64/share/gdb/python/gdb/dap/events.py
@@ -17,7 +17,7 @@
from .modules import is_module, make_module
from .scopes import set_finish_value
-from .server import send_event
+from .server import send_event, send_event_maybe_later
from .startup import exec_and_log, in_gdb_thread, log
# True when the inferior is thought to be running, False otherwise.
@@ -161,7 +161,7 @@ def expect_stop(reason: str):
@in_gdb_thread
-def exec_and_expect_stop(cmd, expected_pause=False):
+def exec_and_expect_stop(cmd, expected_pause=False, propagate_exception=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):
# continuing.
_suppress_cont = not expected_pause
# FIXME if the call fails should we clear _suppress_cont?
- exec_and_log(cmd)
+ exec_and_log(cmd, propagate_exception)
# Map from gdb stop reasons to DAP stop reasons. Some of these can't
@@ -241,7 +241,7 @@ def _on_stop(event):
global stop_reason_map
obj["reason"] = stop_reason_map[event.details["reason"]]
_expected_pause = False
- send_event("stopped", obj)
+ send_event_maybe_later("stopped", obj)
# This keeps a bit of state between the start of an inferior call and
diff --git a/mingw64/share/gdb/python/gdb/dap/frames.py b/mingw64/share/gdb/python/gdb/dap/frames.py
index 07a4e3ea793..f4e6565b943 100644
--- a/mingw64/share/gdb/python/gdb/dap/frames.py
+++ b/mingw64/share/gdb/python/gdb/dap/frames.py
@@ -14,11 +14,13 @@
# along with this program. If not, see .
import itertools
+from typing import Dict
import gdb
from gdb.frames import frame_iterator
from .startup import in_gdb_thread
+from .state import set_thread
# A list of all the frames we've reported. A frame's index in the
# list is its ID. We don't use a hash here because frames are not
@@ -29,6 +31,9 @@
# Map from a global thread ID to a memoizing frame iterator.
_iter_map = {}
+# Map from a global frame ID to a thread ID.
+thread_ids: Dict[int, int] = {}
+
# Clear all the frame IDs.
@in_gdb_thread
@@ -37,6 +42,8 @@ def _clear_frame_ids(evt):
_all_frames = []
global _iter_map
_iter_map = {}
+ global thread_ids
+ thread_ids = {}
# Clear the frame ID map whenever the inferior runs.
@@ -46,6 +53,11 @@ def _clear_frame_ids(evt):
@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]
@@ -94,6 +106,8 @@ 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
def yield_frames(iterator, for_elided):
diff --git a/mingw64/share/gdb/python/gdb/dap/globalvars.py b/mingw64/share/gdb/python/gdb/dap/globalvars.py
new file mode 100644
index 00000000000..104b242d896
--- /dev/null
+++ b/mingw64/share/gdb/python/gdb/dap/globalvars.py
@@ -0,0 +1,98 @@
+# Copyright 2024 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 .
+
+import gdb
+
+from .sources import make_source
+from .startup import in_gdb_thread
+from .varref import BaseReference
+
+# Map a block identifier to a scope object.
+_id_to_scope = {}
+
+
+# Arrange to clear the scope references when the inferior runs.
+@in_gdb_thread
+def clear(event):
+ global _id_to_scope
+ _id_to_scope = {}
+
+
+gdb.events.cont.connect(clear)
+
+
+# A scope that holds static and/or global variables.
+class _Globals(BaseReference):
+ def __init__(self, filename, var_list):
+ super().__init__("Globals")
+ self.filename = filename
+ self.var_list = var_list
+
+ def to_object(self):
+ result = super().to_object()
+ result["presentationHint"] = "globals"
+ # How would we know?
+ result["expensive"] = False
+ result["namedVariables"] = self.child_count()
+ if self.filename is not None:
+ result["source"] = make_source(self.filename)
+ return result
+
+ def has_children(self):
+ # This object won't even be created if there are no variables
+ # to return.
+ return True
+
+ def child_count(self):
+ return len(self.var_list)
+
+ @in_gdb_thread
+ def fetch_one_child(self, idx):
+ sym = self.var_list[idx]
+ return (sym.name, sym.value())
+
+
+@in_gdb_thread
+def get_global_scope(frame):
+ """Given a frame decorator, return the corresponding global scope
+ object.
+
+ If the frame does not have a block, or if the CU does not have
+ globals (that is, empty static and global blocks), return None."""
+ inf_frame = frame.inferior_frame()
+ # It's unfortunate that this API throws instead of returning None.
+ try:
+ block = inf_frame.block()
+ except RuntimeError:
+ return None
+
+ global _id_to_scope
+ block = block.static_block
+ if block in _id_to_scope:
+ return _id_to_scope[block]
+
+ syms = []
+ block_iter = block
+ while block_iter is not None:
+ syms += [sym for sym in block_iter if sym.is_variable and not sym.is_artificial]
+ block_iter = block_iter.superblock
+
+ if len(syms) == 0:
+ return None
+
+ result = _Globals(frame.filename(), syms)
+ _id_to_scope[block] = result
+
+ return result
diff --git a/mingw64/share/gdb/python/gdb/dap/launch.py b/mingw64/share/gdb/python/gdb/dap/launch.py
index 2674e02eac3..fc1890c5a43 100644
--- a/mingw64/share/gdb/python/gdb/dap/launch.py
+++ b/mingw64/share/gdb/python/gdb/dap/launch.py
@@ -13,20 +13,65 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+import re
+
# These are deprecated in 3.9, but required in older versions.
from typing import Mapping, Optional, Sequence
import gdb
from .events import exec_and_expect_stop, expect_process, expect_stop
-from .server import capability, request
-from .startup import DAPException, exec_and_log
+from .server import (
+ DeferredRequest,
+ call_function_later,
+ capability,
+ request,
+ send_gdb,
+ send_gdb_with_response,
+)
+from .startup import DAPException, exec_and_log, in_dap_thread, in_gdb_thread
+
+# A launch or attach promise that that will be fulfilled after a
+# configurationDone request has been processed.
+_launch_or_attach_promise = None
+
+
+# A DeferredRequest that handles either a "launch" or "attach"
+# request.
+class _LaunchOrAttachDeferredRequest(DeferredRequest):
+ def __init__(self, callback):
+ self._callback = callback
+ global _launch_or_attach_promise
+ if _launch_or_attach_promise is not None:
+ raise DAPException("launch or attach already specified")
+ _launch_or_attach_promise = self
+
+ # Invoke the callback and return the result.
+ @in_dap_thread
+ def invoke(self):
+ return self._callback()
+
+ # Override this so we can clear the global when rescheduling.
+ @in_dap_thread
+ def reschedule(self):
+ global _launch_or_attach_promise
+ _launch_or_attach_promise = None
+ super().reschedule()
+
+
+# A wrapper for the 'file' command that correctly quotes its argument.
+@in_gdb_thread
+def file_command(program):
+ # Handle whitespace, quotes, and backslashes here. Exactly what
+ # to quote depends on libiberty's buildargv and safe-ctype.
+ program = re.sub("[ \t\n\r\f\v\\\\'\"]", "\\\\\\g<0>", program)
+ exec_and_log("file " + program)
# Any parameters here are necessarily extensions -- DAP requires this
# from implementations. Any additions or changes here should be
# documented in the gdb manual.
-@request("launch", response=False)
+@request("launch", on_dap_thread=True)
def launch(
*,
program: Optional[str] = None,
@@ -34,27 +79,54 @@ def launch(
args: Sequence[str] = (),
env: Optional[Mapping[str, str]] = None,
stopAtBeginningOfMainSubprogram: bool = False,
+ stopOnEntry: bool = False,
**extra,
):
- if cwd is not None:
- exec_and_log("cd " + cwd)
- if program is not None:
- exec_and_log("file " + program)
- inf = gdb.selected_inferior()
- if stopAtBeginningOfMainSubprogram:
- main = inf.main_name
- if main is not None:
- exec_and_log("tbreak " + main)
- inf.arguments = args
- if env is not None:
- inf.clear_env()
- for name, value in env.items():
- inf.set_env(name, value)
- expect_process("process")
- exec_and_expect_stop("run")
-
-
-@request("attach")
+ # Launch setup is handled here. This is done synchronously so
+ # that errors can be reported in a natural way.
+ @in_gdb_thread
+ def _setup_launch():
+ if cwd is not None:
+ exec_and_log("cd " + cwd)
+ if program is not None:
+ file_command(program)
+ inf = gdb.selected_inferior()
+ inf.arguments = args
+ if env is not None:
+ inf.clear_env()
+ for name, value in env.items():
+ inf.set_env(name, value)
+
+ # Actual launching done here. See below for more info.
+ @in_gdb_thread
+ def _do_launch():
+ expect_process("process")
+ if stopAtBeginningOfMainSubprogram:
+ cmd = "start"
+ elif stopOnEntry:
+ cmd = "starti"
+ else:
+ cmd = "run"
+ exec_and_expect_stop(cmd)
+
+ @in_dap_thread
+ def _launch_impl():
+ send_gdb_with_response(_setup_launch)
+ # We do not wait for the result here. It might be a little
+ # nicer if we did -- perhaps the various thread events would
+ # occur in a more logical sequence -- but if the inferior does
+ # not stop, then the launch response will not be seen either,
+ # which seems worse.
+ send_gdb(_do_launch)
+ # Launch response does not have a body.
+ return None
+
+ # The launch itself is deferred until the configurationDone
+ # request.
+ return _LaunchOrAttachDeferredRequest(_launch_impl)
+
+
+@request("attach", on_dap_thread=True)
def attach(
*,
program: Optional[str] = None,
@@ -62,21 +134,39 @@ def attach(
target: Optional[str] = None,
**args,
):
- if program is not None:
- exec_and_log("file " + program)
- if pid is not None:
- cmd = "attach " + str(pid)
- elif target is not None:
- cmd = "target remote " + target
- else:
- raise DAPException("attach requires either 'pid' or 'target'")
- expect_process("attach")
- expect_stop("attach")
- exec_and_log(cmd)
+ # The actual attach is handled by this function.
+ @in_gdb_thread
+ def _do_attach():
+ if program is not None:
+ file_command(program)
+ if pid is not None:
+ cmd = "attach " + str(pid)
+ elif target is not None:
+ cmd = "target remote " + target
+ else:
+ raise DAPException("attach requires either 'pid' or 'target'")
+ expect_process("attach")
+ expect_stop("attach")
+ exec_and_log(cmd)
+ # Attach response does not have a body.
+ return None
+
+ @in_dap_thread
+ def _attach_impl():
+ return send_gdb_with_response(_do_attach)
+
+ # The attach itself is deferred until the configurationDone
+ # request.
+ return _LaunchOrAttachDeferredRequest(_attach_impl)
@capability("supportsConfigurationDoneRequest")
-@request("configurationDone")
+@request("configurationDone", on_dap_thread=True)
def config_done(**args):
- # Nothing to do.
- return None
+ # 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
+ # configurationDone response has been sent.
+ call_function_later(_launch_or_attach_promise.reschedule)
diff --git a/mingw64/share/gdb/python/gdb/dap/locations.py b/mingw64/share/gdb/python/gdb/dap/locations.py
index 92e68f5e235..1ef5a34b584 100644
--- a/mingw64/share/gdb/python/gdb/dap/locations.py
+++ b/mingw64/share/gdb/python/gdb/dap/locations.py
@@ -16,10 +16,9 @@
# This is deprecated in 3.9, but required in older versions.
from typing import Optional
-import gdb
-
-from .server import capability, request
+from .server import capability, export_line, import_line, request
from .sources import decode_source
+from .startup import exec_mi_and_log
# Note that the spec says that the arguments to this are optional.
@@ -29,15 +28,18 @@
# This points out that fixing this would be an incompatibility but
# goes on to propose "if arguments property is missing, debug adapters
# should return an error".
-@request("breakpointLocations")
+@request("breakpointLocations", expect_stopped=False)
@capability("supportsBreakpointLocationsRequest")
def breakpoint_locations(*, source, line: int, endLine: Optional[int] = None, **extra):
+ line = import_line(line)
if endLine is None:
endLine = line
+ else:
+ endLine = import_line(endLine)
filename = decode_source(source)
lines = set()
- for entry in gdb.execute_mi("-symbol-list-lines", filename)["lines"]:
+ for entry in exec_mi_and_log("-symbol-list-lines", filename)["lines"]:
this_line = entry["line"]
if this_line >= line and this_line <= endLine:
- lines.add(this_line)
+ lines.add(export_line(this_line))
return {"breakpoints": [{"line": x} for x in sorted(lines)]}
diff --git a/mingw64/share/gdb/python/gdb/dap/next.py b/mingw64/share/gdb/python/gdb/dap/next.py
index 1dc1d6dd74d..7e06b1b79dc 100644
--- a/mingw64/share/gdb/python/gdb/dap/next.py
+++ b/mingw64/share/gdb/python/gdb/dap/next.py
@@ -73,10 +73,10 @@ def step_in(
exec_and_expect_stop(cmd)
-@request("stepOut", response=False)
+@request("stepOut", defer_stop_events=True)
def step_out(*, threadId: int, singleThread: bool = False, **args):
_handle_thread_step(threadId, singleThread, True)
- exec_and_expect_stop("finish")
+ exec_and_expect_stop("finish &", propagate_exception=True)
# This is a server-side request because it is funny: it wants to
diff --git a/mingw64/share/gdb/python/gdb/dap/scopes.py b/mingw64/share/gdb/python/gdb/dap/scopes.py
index 8cd860141d6..221ae35a002 100644
--- a/mingw64/share/gdb/python/gdb/dap/scopes.py
+++ b/mingw64/share/gdb/python/gdb/dap/scopes.py
@@ -16,7 +16,9 @@
import gdb
from .frames import frame_for_id
-from .server import request
+from .globalvars import get_global_scope
+from .server import export_line, request
+from .sources import make_source
from .startup import in_gdb_thread
from .varref import BaseReference
@@ -74,13 +76,10 @@ def symbol_value(sym, frame):
class _ScopeReference(BaseReference):
- def __init__(self, name, hint, frame, var_list):
+ def __init__(self, name, hint, frameId: int, var_list):
super().__init__(name)
self.hint = hint
- self.frame = frame
- self.inf_frame = frame.inferior_frame()
- self.func = frame.function()
- self.line = frame.line()
+ 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)
@@ -91,9 +90,12 @@ def to_object(self):
# How would we know?
result["expensive"] = False
result["namedVariables"] = self.child_count()
- if self.line is not None:
- result["line"] = self.line
- # FIXME construct a Source object
+ frame = frame_for_id(self.frameId)
+ if frame.line() is not None:
+ result["line"] = export_line(frame.line())
+ filename = frame.filename()
+ if filename is not None:
+ result["source"] = make_source(filename)
return result
def has_children(self):
@@ -104,36 +106,40 @@ def child_count(self):
@in_gdb_thread
def fetch_one_child(self, idx):
- return symbol_value(self.var_list[idx], self.frame)
+ return symbol_value(self.var_list[idx], frame_for_id(self.frameId))
-# A _ScopeReference that prepends the most recent return value. Note
-# that this object is only created if such a value actually exists.
+# A _ScopeReference that wraps the 'finish' value. Note that this
+# object is only created if such a value actually exists.
class _FinishScopeReference(_ScopeReference):
- def __init__(self, *args):
- super().__init__(*args)
+ def __init__(self, frameId):
+ super().__init__("Return", "returnValue", frameId, ())
def child_count(self):
- return super().child_count() + 1
+ return 1
def fetch_one_child(self, idx):
- if idx == 0:
- global _last_return_value
- return ("(return)", _last_return_value)
- return super().fetch_one_child(idx - 1)
+ assert idx == 0
+ global _last_return_value
+ return ("(return)", _last_return_value)
class _RegisterReference(_ScopeReference):
- def __init__(self, name, frame):
+ def __init__(self, name, frameId):
super().__init__(
- name, "registers", frame, frame.inferior_frame().architecture().registers()
+ name,
+ "registers",
+ frameId,
+ frame_for_id(frameId).inferior_frame().architecture().registers(),
)
@in_gdb_thread
def fetch_one_child(self, idx):
return (
self.var_list[idx].name,
- self.inf_frame.read_register(self.var_list[idx]),
+ frame_for_id(self.frameId)
+ .inferior_frame()
+ .read_register(self.var_list[idx]),
)
@@ -150,15 +156,18 @@ def scopes(*, frameId: int, **extra):
# iterator case.
args = tuple(frame.frame_args() or ())
if args:
- scopes.append(_ScopeReference("Arguments", "arguments", frame, args))
+ scopes.append(_ScopeReference("Arguments", "arguments", frameId, args))
has_return_value = frameId == 0 and _last_return_value is not None
# Make sure to handle the None case as well as the empty
# iterator case.
locs = tuple(frame.frame_locals() or ())
+ if locs:
+ scopes.append(_ScopeReference("Locals", "locals", frameId, locs))
+ scopes.append(_RegisterReference("Registers", frameId))
if has_return_value:
- scopes.append(_FinishScopeReference("Locals", "locals", frame, locs))
- elif locs:
- scopes.append(_ScopeReference("Locals", "locals", frame, locs))
- scopes.append(_RegisterReference("Registers", frame))
+ scopes.append(_FinishScopeReference(frameId))
frame_to_scope[frameId] = scopes
+ global_scope = get_global_scope(frame)
+ if global_scope is not None:
+ scopes.append(global_scope)
return {"scopes": [x.to_object() for x in scopes]}
diff --git a/mingw64/share/gdb/python/gdb/dap/server.py b/mingw64/share/gdb/python/gdb/dap/server.py
index 7eb87177710..6f3af732286 100644
--- a/mingw64/share/gdb/python/gdb/dap/server.py
+++ b/mingw64/share/gdb/python/gdb/dap/server.py
@@ -46,6 +46,56 @@
# The global server.
_server = None
+# This is set by the initialize request and is used when rewriting
+# line numbers.
+_lines_start_at_1 = False
+
+
+class DeferredRequest:
+ """If a DAP request function returns a deferred request, no
+ response is sent immediately.
+
+ Instead, request processing continues, with this particular
+ request remaining un-replied-to.
+
+ Later, when the result is available, the deferred request can be
+ scheduled. This causes 'invoke' to be called and then the
+ response to be sent to the client.
+
+ """
+
+ # This is for internal use by the server. It should not be
+ # overridden by any subclass. This adds the request ID and the
+ # result template object to this object. These are then used
+ # during rescheduling.
+ def set_request(self, req, result):
+ self._req = req
+ self._result = result
+
+ @in_dap_thread
+ def invoke(self):
+ """Implement the deferred request.
+
+ This will be called from 'reschedule' (and should not be
+ called elsewhere). It should return the 'body' that will be
+ sent in the response. None means no 'body' field will be set.
+
+ Subclasses must override this.
+
+ """
+ pass
+
+ @in_dap_thread
+ def reschedule(self):
+ """Call this to reschedule this deferred request.
+
+ This will call 'invoke' after the appropriate bookkeeping and
+ will arrange for its result to be reported to the client.
+
+ """
+ with _server.canceller.current_request(self._req):
+ _server.invoke_request(self._req, self._result, self.invoke)
+
# A subclass of Exception that is used solely for reporting that a
# request needs the inferior to be stopped, but it is not stopped.
@@ -59,21 +109,78 @@ class NotStoppedException(Exception):
class CancellationHandler:
def __init__(self):
# Methods on this class acquire this lock before proceeding.
- self.lock = threading.Lock()
+ # A recursive lock is used to simplify the 'check_cancel'
+ # callers.
+ self.lock = threading.RLock()
# The request currently being handled, or None.
self.in_flight_dap_thread = None
self.in_flight_gdb_thread = None
self.reqs = []
+ # A set holding the request IDs of all deferred requests that
+ # are still unresolved.
+ self.deferred_ids = set()
+
+ @contextmanager
+ def current_request(self, req):
+ """Return a new context manager that registers that request
+ REQ has started."""
+ try:
+ with self.lock:
+ self.in_flight_dap_thread = req
+ # Note we do not call check_cancel here. This is a bit of
+ # a hack, but it's because the direct callers of this
+ # aren't prepared for a KeyboardInterrupt.
+ yield
+ finally:
+ with self.lock:
+ self.in_flight_dap_thread = None
- def starting(self, req):
- """Call at the start of the given request."""
+ def defer_request(self, req):
+ """Indicate that the request REQ has been deferred."""
with self.lock:
- self.in_flight_dap_thread = req
+ self.deferred_ids.add(req)
- def done(self, req):
- """Indicate that the request is done."""
+ def request_finished(self, req):
+ """Indicate that the request REQ is finished.
+
+ It doesn't matter whether REQ succeeded or failed, only that
+ processing for it is done.
+
+ """
with self.lock:
- self.in_flight_dap_thread = None
+ # Use discard here, not remove, because this is called
+ # regardless of whether REQ was deferred.
+ self.deferred_ids.discard(req)
+
+ def check_cancel(self, req):
+ """Check whether request REQ is cancelled.
+ If so, raise KeyboardInterrupt."""
+ with self.lock:
+ # We want to drop any cancellations that come before REQ,
+ # but keep ones for any deferred requests that are still
+ # unresolved. This holds any such requests that were
+ # popped during the loop.
+ deferred = []
+ try:
+ # If the request is cancelled, don't execute the region.
+ 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)
+ if next_id == req:
+ raise KeyboardInterrupt()
+ 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
+ # request 18 here will lose the cancellation.
+ # So, we preserve it.
+ deferred.append(next_id)
+ finally:
+ for x in deferred:
+ heapq.heappush(self.reqs, x)
def cancel(self, req):
"""Call to cancel a request.
@@ -86,7 +193,7 @@ def cancel(self, req):
gdb.interrupt()
else:
# We don't actually ignore the request here, but in
- # the 'starting' method. This way we don't have to
+ # the 'check_cancel' method. This way we don't have to
# track as much state. Also, this implementation has
# the weird property that a request can be cancelled
# before it is even sent. It didn't seem worthwhile
@@ -103,10 +210,7 @@ def interruptable_region(self, req):
return
try:
with self.lock:
- # If the request is cancelled, don't execute the region.
- while len(self.reqs) > 0 and self.reqs[0] <= req:
- if heapq.heappop(self.reqs) == req:
- raise KeyboardInterrupt()
+ self.check_cancel(req)
# Request is being handled by the gdb thread.
self.in_flight_gdb_thread = req
# Execute region. This may be interrupted by gdb.interrupt.
@@ -124,7 +228,9 @@ 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_events = []
+ self.delayed_fns_lock = threading.Lock()
+ self.defer_stop_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.
@@ -137,27 +243,27 @@ def __init__(self, in_stream, out_stream, child_stream):
global _server
_server = self
- # Treat PARAMS as a JSON-RPC request and perform its action.
- # PARAMS is just a dictionary from the JSON.
+ # A helper for request processing. REQ is the request ID. RESULT
+ # is a result "template" -- a dictionary with a few items already
+ # filled in. This helper calls FN and then fills in the remaining
+ # parts of RESULT, as needed. If FN returns an ordinary result,
+ # or if it fails, then the final RESULT is sent as a response to
+ # the client. However, if FN returns a DeferredRequest, then that
+ # request is updated (see DeferredRequest.set_request) and no
+ # response is sent.
@in_dap_thread
- def _handle_command(self, params):
- req = params["seq"]
- result = {
- "request_seq": req,
- "type": "response",
- "command": params["command"],
- }
+ def invoke_request(self, req, result, fn):
try:
- self.canceller.starting(req)
- if "arguments" in params:
- args = params["arguments"]
- else:
- args = {}
- global _commands
- body = _commands[params["command"]](**args)
- if body is not None:
- result["body"] = body
+ self.canceller.check_cancel(req)
+ fn_result = fn()
result["success"] = True
+ if isinstance(fn_result, DeferredRequest):
+ fn_result.set_request(req, result)
+ self.canceller.defer_request(req)
+ # Do not send a response.
+ return
+ elif fn_result is not None:
+ result["body"] = fn_result
except NotStoppedException:
# This is an expected exception, and the result is clearly
# visible in the log, so do not log it.
@@ -177,8 +283,33 @@ def _handle_command(self, params):
log_stack()
result["success"] = False
result["message"] = str(e)
- self.canceller.done(req)
- return result
+
+ self.canceller.request_finished(req)
+ # We have a response for the request, so send it back to the
+ # client.
+ self._send_json(result)
+
+ # Treat PARAMS as a JSON-RPC request and perform its action.
+ # PARAMS is just a dictionary from the JSON.
+ @in_dap_thread
+ def _handle_command(self, params):
+ req = params["seq"]
+ result = {
+ "request_seq": req,
+ "type": "response",
+ "command": params["command"],
+ }
+
+ if "arguments" in params:
+ args = params["arguments"]
+ else:
+ args = {}
+
+ def fn():
+ global _commands
+ return _commands[params["command"]](**args)
+
+ self.invoke_request(req, result, fn)
# Read inferior output and sends OutputEvents to the client. It
# is run in its own thread.
@@ -237,12 +368,16 @@ def main_loop(self):
# A None value here means the reader hit EOF.
if cmd is None:
break
- result = self._handle_command(cmd)
- self._send_json(result)
- events = self.delayed_events
- self.delayed_events = []
- for event, body in events:
- self.send_event(event, body)
+ 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()
# 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.
@@ -254,7 +389,28 @@ def main_loop(self):
def send_event_later(self, event, body=None):
"""Send a DAP event back to the client, but only after the
current request has completed."""
- self.delayed_events.append((event, body))
+ with self.delayed_fns_lock:
+ self.delayed_fns.append(lambda: self.send_event(event, body))
+
+ @in_gdb_thread
+ 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,
+ wait until the response has been sent until the event is sent back to
+ 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))
+ return
+ 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)
# 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
@@ -287,6 +443,21 @@ def send_event(event, body=None):
_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)
+
+
# A helper decorator that checks whether the inferior is running.
def _check_not_running(func):
@functools.wraps(func)
@@ -307,7 +478,8 @@ def request(
*,
response: bool = True,
on_dap_thread: bool = False,
- expect_stopped: bool = True
+ expect_stopped: bool = True,
+ defer_stop_events: bool = False
):
"""A decorator for DAP requests.
@@ -328,6 +500,10 @@ def request(
fail with the 'notStopped' reason if it is processed while the
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.
"""
# Validate the parameters.
@@ -355,6 +531,11 @@ def wrap(func):
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))
@@ -394,15 +575,15 @@ def wrap(func):
return wrap
-def client_bool_capability(name):
+def client_bool_capability(name, default=False):
"""Return the value of a boolean client capability.
If the capability was not specified, or did not have boolean type,
- False is returned."""
+ 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 False
+ return default
@request("initialize", on_dap_thread=True)
@@ -410,6 +591,8 @@ def initialize(**args):
global _server, _capabilities
_server.config = args
_server.send_event_later("initialized")
+ global _lines_start_at_1
+ _lines_start_at_1 = client_bool_capability("linesStartAt1", True)
return _capabilities.copy()
@@ -513,3 +696,27 @@ def send_gdb_with_response(fn):
if isinstance(val, (Exception, KeyboardInterrupt)):
raise val
return val
+
+
+def export_line(line):
+ """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.
+ line = line - 1
+ return line
+
+
+def import_line(line):
+ """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
diff --git a/mingw64/share/gdb/python/gdb/dap/sources.py b/mingw64/share/gdb/python/gdb/dap/sources.py
index ad0c913c8c1..a9f4ea62f69 100644
--- a/mingw64/share/gdb/python/gdb/dap/sources.py
+++ b/mingw64/share/gdb/python/gdb/dap/sources.py
@@ -15,10 +15,8 @@
import os
-import gdb
-
from .server import capability, request
-from .startup import DAPException, in_gdb_thread
+from .startup import DAPException, exec_mi_and_log, in_gdb_thread
# The next available source reference ID. Must be greater than 0.
_next_source = 1
@@ -83,7 +81,7 @@ def decode_source(source):
@capability("supportsLoadedSourcesRequest")
def loaded_sources(**extra):
result = []
- for elt in gdb.execute_mi("-file-list-exec-source-files")["files"]:
+ for elt in exec_mi_and_log("-file-list-exec-source-files")["files"]:
result.append(make_source(elt["fullname"], elt["file"]))
return {
"sources": result,
diff --git a/mingw64/share/gdb/python/gdb/dap/startup.py b/mingw64/share/gdb/python/gdb/dap/startup.py
index 58591c00b97..a3f048bd396 100644
--- a/mingw64/share/gdb/python/gdb/dap/startup.py
+++ b/mingw64/share/gdb/python/gdb/dap/startup.py
@@ -204,7 +204,7 @@ def log_stack(level=LogLevel.DEFAULT):
@in_gdb_thread
-def exec_and_log(cmd):
+def exec_and_log(cmd, propagate_exception=False):
"""Execute the gdb command CMD.
If logging is enabled, log the command and its output."""
log("+++ " + cmd)
@@ -212,5 +212,15 @@ def exec_and_log(cmd):
output = gdb.execute(cmd, from_tty=True, to_string=True)
if output != "":
log(">>> " + output)
- except gdb.error:
- log_stack()
+ except gdb.error as e:
+ if propagate_exception:
+ raise DAPException(str(e)) from e
+ else:
+ log_stack()
+
+
+@in_gdb_thread
+def exec_mi_and_log(*args):
+ """Wrap gdb.execute_mi, logging the command."""
+ log("+++ " + str(args))
+ return gdb.execute_mi(*args)
diff --git a/mingw64/share/gdb/python/gdb/dap/varref.py b/mingw64/share/gdb/python/gdb/dap/varref.py
index 57e84a1676e..0dd98797086 100644
--- a/mingw64/share/gdb/python/gdb/dap/varref.py
+++ b/mingw64/share/gdb/python/gdb/dap/varref.py
@@ -18,6 +18,7 @@
from contextlib import contextmanager
import gdb
+import gdb.printing
from .server import client_bool_capability
from .startup import DAPException, in_gdb_thread
diff --git a/mingw64/share/gdb/python/gdb/disassembler.py b/mingw64/share/gdb/python/gdb/disassembler.py
index 72d311b117f..7d0e781ef21 100644
--- a/mingw64/share/gdb/python/gdb/disassembler.py
+++ b/mingw64/share/gdb/python/gdb/disassembler.py
@@ -147,7 +147,7 @@ def invoke(self, args, from_tty):
# Figure out the name of the current architecture. There
# should always be a current inferior, but if, somehow, there
# isn't, then leave curr_arch as the empty string, which will
- # not then match agaisnt any architecture in the dictionary.
+ # not then match against any architecture in the dictionary.
curr_arch = ""
if gdb.selected_inferior() is not None:
curr_arch = gdb.selected_inferior().architecture().name()
diff --git a/mingw64/share/gdb/python/gdb/missing_debug.py b/mingw64/share/gdb/python/gdb/missing_debug.py
index 6d57462c185..2c2cebacaf8 100644
--- a/mingw64/share/gdb/python/gdb/missing_debug.py
+++ b/mingw64/share/gdb/python/gdb/missing_debug.py
@@ -17,48 +17,11 @@
MissingDebugHandler base class, and register_handler function.
"""
-import sys
-
import gdb
+from gdb.missing_files import MissingFileHandler
-if sys.version_info >= (3, 7):
- # Functions str.isascii() and str.isalnum are available starting Python
- # 3.7.
- def isascii(ch):
- return ch.isascii()
-
- def isalnum(ch):
- return ch.isalnum()
-
-else:
- # Fall back to curses.ascii.isascii() and curses.ascii.isalnum() for
- # earlier versions.
- from curses.ascii import isalnum, isascii
-
-
-def _validate_name(name):
- """Validate a missing debug handler name string.
-
- If name is valid as a missing debug handler name, then this
- function does nothing. If name is not valid then an exception is
- raised.
-
- Arguments:
- name: A string, the name of a missing debug handler.
-
- Returns:
- Nothing.
- Raises:
- ValueError: If name is invalid as a missing debug handler
- name.
- """
- for ch in name:
- if not isascii(ch) or not (isalnum(ch) or ch in "_-"):
- raise ValueError("invalid character '%s' in handler name: %s" % (ch, name))
-
-
-class MissingDebugHandler(object):
+class MissingDebugHandler(MissingFileHandler):
"""Base class for missing debug handlers written in Python.
A missing debug handler has a single method __call__ along with
@@ -69,41 +32,8 @@ class MissingDebugHandler(object):
enabled: When true this handler is enabled.
"""
- def __init__(self, name):
- """Constructor.
-
- Args:
- name: An identifying name for this handler.
-
- Raises:
- TypeError: name is not a string.
- ValueError: name contains invalid characters.
- """
-
- if not isinstance(name, str):
- raise TypeError("incorrect type for name: %s" % type(name))
-
- _validate_name(name)
-
- self._name = name
- self._enabled = True
-
- @property
- def name(self):
- return self._name
-
- @property
- def enabled(self):
- return self._enabled
-
- @enabled.setter
- def enabled(self, value):
- if not isinstance(value, bool):
- raise TypeError("incorrect type for enabled attribute: %s" % type(value))
- self._enabled = value
-
def __call__(self, objfile):
- """GDB handle missing debug information for an objfile.
+ """Handle missing debug information for an objfile.
Arguments:
objfile: A gdb.Objfile for which GDB could not find any
@@ -124,62 +54,5 @@ def __call__(self, objfile):
def register_handler(locus, handler, replace=False):
- """Register handler in given locus.
-
- The handler is prepended to the locus's missing debug handlers
- list. The name of handler should be unique (or replace must be
- True).
-
- Arguments:
- locus: Either a progspace, or None (in which case the unwinder
- is registered globally).
- handler: An object of a gdb.MissingDebugHandler subclass.
-
- replace: If True, replaces existing handler with the same name
- within locus. Otherwise, raises RuntimeException if
- unwinder with the same name already exists.
-
- Returns:
- Nothing.
-
- Raises:
- RuntimeError: The name of handler is not unique.
- TypeError: Bad locus type.
- AttributeError: Required attributes of handler are missing.
- """
-
- if locus is None:
- if gdb.parameter("verbose"):
- gdb.write("Registering global %s handler ...\n" % handler.name)
- locus = gdb
- elif isinstance(locus, gdb.Progspace):
- if gdb.parameter("verbose"):
- gdb.write(
- "Registering %s handler for %s ...\n" % (handler.name, locus.filename)
- )
- else:
- raise TypeError("locus should be gdb.Progspace or None")
-
- # Some sanity checks on HANDLER. Calling getattr will raise an
- # exception if the attribute doesn't exist, which is what we want.
- # These checks are not exhaustive; we don't check the attributes
- # have the correct types, or the method has the correct signature,
- # but this should catch some basic mistakes.
- getattr(handler, "name")
- getattr(handler, "enabled")
- call_method = getattr(handler, "__call__")
- if not callable(call_method):
- raise AttributeError(
- "'%s' object's '__call__' attribute is not callable"
- % type(handler).__name__
- )
-
- i = 0
- for needle in locus.missing_debug_handlers:
- if needle.name == handler.name:
- if replace:
- del locus.missing_debug_handlers[i]
- else:
- raise RuntimeError("Handler %s already exists." % handler.name)
- i += 1
- locus.missing_debug_handlers.insert(0, handler)
+ """See gdb.missing_files.register_handler."""
+ gdb.missing_files.register_handler("debug", locus, handler, replace)
diff --git a/mingw64/share/gdb/python/gdb/missing_files.py b/mingw64/share/gdb/python/gdb/missing_files.py
new file mode 100644
index 00000000000..5f2df88c728
--- /dev/null
+++ b/mingw64/share/gdb/python/gdb/missing_files.py
@@ -0,0 +1,204 @@
+# Copyright (C) 2023-2024 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 .
+
+"""
+MissingFileHandler base class, and support functions used by the
+missing_debug.py and missing_objfile.py modules.
+"""
+
+import sys
+
+import gdb
+
+if sys.version_info >= (3, 7):
+ # Functions str.isascii() and str.isalnum are available starting Python
+ # 3.7.
+ def isascii(ch):
+ return ch.isascii()
+
+ def isalnum(ch):
+ return ch.isalnum()
+
+else:
+ # Older version of Python doesn't have str.isascii() and
+ # str.isalnum() so provide our own.
+ #
+ # We could import isalnum() and isascii() from the curses library,
+ # but that adds an extra dependency. Given these functions are
+ # both small and trivial lets implement them here.
+ #
+ # These definitions are based on those in the curses library, but
+ # simplified as we know C will always be a single character 'str'.
+
+ def isdigit(c):
+ return 48 <= ord(c) <= 57
+
+ def islower(c):
+ return 97 <= ord(c) <= 122
+
+ def isupper(c):
+ return 65 <= ord(c) <= 90
+
+ def isalpha(c):
+ return isupper(c) or islower(c)
+
+ def isalnum(c):
+ return isalpha(c) or isdigit(c)
+
+ def isascii(c):
+ return 0 <= ord(c) <= 127
+
+
+def _validate_name(name):
+ """Validate a missing file handler name string.
+
+ If name is valid as a missing file handler name, then this
+ function does nothing. If name is not valid then an exception is
+ raised.
+
+ Arguments:
+ name: A string, the name of a missing file handler.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ ValueError: If name is invalid as a missing file handler
+ name.
+ """
+
+ for ch in name:
+ if not isascii(ch) or not (isalnum(ch) or ch in "_-"):
+ raise ValueError("invalid character '%s' in handler name: %s" % (ch, name))
+
+
+class MissingFileHandler(object):
+ """Base class for missing file handlers written in Python.
+
+ A missing file handler has a single method __call__ along with the
+ read/write attribute enabled, and a read-only attribute name. The
+ attributes are provided by this class while the __call__ method is
+ provided by a sub-class. Each sub-classes __call__ method will
+ have a different signature.
+
+ Attributes:
+ name: Read-only attribute, the name of this handler.
+ enabled: When true this handler is enabled.
+ """
+
+ def __init__(self, name):
+ """Constructor.
+
+ Args:
+ name: An identifying name for this handler.
+
+ Raises:
+ TypeError: name is not a string.
+ ValueError: name contains invalid characters.
+ """
+
+ if not isinstance(name, str):
+ raise TypeError("incorrect type for name: %s" % type(name))
+
+ _validate_name(name)
+
+ self._name = name
+ self._enabled = True
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def enabled(self):
+ return self._enabled
+
+ @enabled.setter
+ def enabled(self, value):
+ if not isinstance(value, bool):
+ raise TypeError("incorrect type for enabled attribute: %s" % type(value))
+ self._enabled = value
+
+
+def register_handler(handler_type, locus, handler, replace=False):
+ """Register handler in given locus.
+
+ The handler is prepended to the locus's missing file handlers
+ list. The name of handler should be unique (or replace must be
+ True), and the name must pass the _validate_name check.
+
+ Arguments:
+ handler_type: A string, either 'debug' or 'objfile' indicating the
+ type of handler to be registered.
+ locus: Either a progspace, or None (in which case the unwinder
+ is registered globally).
+ handler: An object used as a missing file handler. Usually a
+ sub-class of MissingFileHandler.
+ replace: If True, replaces existing handler with the same name
+ within locus. Otherwise, raises RuntimeException if
+ unwinder with the same name already exists.
+
+ Returns:
+ Nothing.
+
+ Raises:
+ RuntimeError: The name of handler is not unique.
+ TypeError: Bad locus type.
+ AttributeError: Required attributes of handler are missing.
+ ValueError: If the name of the handler is invalid, or if
+ handler_type is neither 'debug' or 'objfile'.
+ """
+
+ if handler_type != "debug" and handler_type != "objfile":
+ raise ValueError("handler_type must be 'debug' or 'objfile'")
+
+ if locus is None:
+ if gdb.parameter("verbose"):
+ gdb.write("Registering global %s handler ...\n" % handler.name)
+ locus = gdb
+ elif isinstance(locus, gdb.Progspace):
+ if gdb.parameter("verbose"):
+ gdb.write(
+ "Registering %s handler for %s ...\n" % (handler.name, locus.filename)
+ )
+ else:
+ raise TypeError("locus should be gdb.Progspace or None")
+
+ # Some sanity checks on HANDLER. Calling getattr will raise an
+ # exception if the attribute doesn't exist, which is what we want.
+ # These checks are not exhaustive; we don't check the attributes
+ # have the correct types, or the method has the correct signature,
+ # but this should catch some basic mistakes.
+ name = getattr(handler, "name")
+ _validate_name(name)
+
+ getattr(handler, "enabled")
+
+ call_method = getattr(handler, "__call__")
+ if not callable(call_method):
+ raise AttributeError(
+ "'%s' object's '__call__' attribute is not callable"
+ % type(handler).__name__
+ )
+
+ i = 0
+ for needle in locus.missing_file_handlers:
+ if needle[0] == handler_type and needle[1].name == handler.name:
+ if replace:
+ del locus.missing_file_handlers[i]
+ else:
+ raise RuntimeError("Handler %s already exists." % handler.name)
+ i += 1
+ locus.missing_file_handlers.insert(0, (handler_type, handler))
diff --git a/mingw64/share/gdb/python/gdb/missing_objfile.py b/mingw64/share/gdb/python/gdb/missing_objfile.py
new file mode 100644
index 00000000000..ace0e1315b5
--- /dev/null
+++ b/mingw64/share/gdb/python/gdb/missing_objfile.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2024 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 .
+
+"""
+MissingObjfileHandler base class, and register_handler function.
+"""
+
+import gdb
+from gdb.missing_files import MissingFileHandler
+
+
+class MissingObjfileHandler(MissingFileHandler):
+ """Base class for missing objfile handlers written in Python.
+
+ A missing objfile handler has a single method __call__ along with
+ the read/write attribute enabled, and a read-only attribute name.
+
+ Attributes:
+ name: Read-only attribute, the name of this handler.
+ enabled: When true this handler is enabled.
+ """
+
+ def __call__(self, buildid, filename):
+ """Handle a missing objfile when GDB can knows the build-id.
+
+ Arguments:
+
+ buildid: A string containing the build-id for the objfile
+ GDB is searching for.
+ filename: A string containing the name of the file GDB is
+ searching for. This is provided only for the purpose
+ of creating diagnostic messages. If the file is found
+ it does not have to be placed here, and this file
+ might already exist but GDB has determined it is not
+ suitable for use, e.g. if the build-id doesn't match.
+
+ Returns:
+
+ True: GDB should try again to locate the missing objfile,
+ the handler may have installed the missing file.
+ False: GDB should move on without the objfile. The
+ handler has determined that this objfile is not
+ available.
+ A string: GDB should load the file at the given path; it
+ contains the requested objfile.
+ None: This handler can't help with this objfile. GDB
+ should try any other registered handlers.
+
+ """
+ raise NotImplementedError("MissingObjfileHandler.__call__()")
+
+
+def register_handler(locus, handler, replace=False):
+ """See gdb.missing_files.register_handler."""
+ gdb.missing_files.register_handler("objfile", locus, handler, replace)
diff --git a/mingw64/share/gdb/python/gdb/printer/bound_registers.py b/mingw64/share/gdb/python/gdb/printer/bound_registers.py
deleted file mode 100644
index d00b455ddb9..00000000000
--- a/mingw64/share/gdb/python/gdb/printer/bound_registers.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Pretty-printers for bounds registers.
-# Copyright (C) 2013-2024 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 .
-
-import gdb
-import gdb.printing
-
-
-class MpxBound128Printer(gdb.ValuePrinter):
- """Adds size field to a mpx __gdb_builtin_type_bound128 type."""
-
- def __init__(self, val):
- self.__val = val
-
- def to_string(self):
- upper = self.__val["ubound"]
- lower = self.__val["lbound"]
- size = upper - lower
- if size > -1:
- size = size + 1
- result = "{lbound = %s, ubound = %s} : size %s" % (lower, upper, size)
- return result
-
-
-gdb.printing.add_builtin_pretty_printer(
- "mpx_bound128", "^builtin_type_bound128", MpxBound128Printer
-)
diff --git a/mingw64/share/gdb/python/gdb/printing.py b/mingw64/share/gdb/python/gdb/printing.py
index 55ba43585ec..0635993cac3 100644
--- a/mingw64/share/gdb/python/gdb/printing.py
+++ b/mingw64/share/gdb/python/gdb/printing.py
@@ -281,6 +281,44 @@ def to_string(self):
return self.__value.format_string(raw=True)
+class NoOpStringPrinter(gdb.ValuePrinter):
+ """A no-op pretty printer that wraps a string value."""
+
+ def __init__(self, ty, value):
+ self.__ty = ty
+ self.__value = value
+
+ def to_string(self):
+ # We need some special cases here.
+ #
+ # * If the gdb.Value was created from a Python string, it will
+ # be a non-lazy array -- but will have address 0 and so the
+ # contents will be lost on conversion to lazy string.
+ # (Weirdly, the .address attribute will not be 0 though.)
+ # Since conversion to lazy string is to avoid fetching too
+ # much data, and since the array is already non-lazy, just
+ # return it.
+ #
+ # * To avoid weird printing for a C "string" that is just a
+ # NULL pointer, special case this as well.
+ #
+ # * Lazy strings only understand arrays and pointers; other
+ # string-like objects (like a Rust &str) should simply be
+ # returned.
+ code = self.__ty.code
+ if code == gdb.TYPE_CODE_ARRAY and not self.__value.is_lazy:
+ return self.__value
+ elif code == gdb.TYPE_CODE_PTR and self.__value == 0:
+ return self.__value
+ elif code != gdb.TYPE_CODE_PTR and code != gdb.TYPE_CODE_ARRAY:
+ return self.__value
+ else:
+ return self.__value.lazy_string()
+
+ def display_hint(self):
+ return "string"
+
+
class NoOpPointerReferencePrinter(gdb.ValuePrinter):
"""A no-op pretty printer that wraps a pointer or reference."""
@@ -368,7 +406,7 @@ def make_visualizer(value):
else:
ty = value.type.strip_typedefs()
if ty.is_string_like:
- result = NoOpScalarPrinter(value)
+ result = NoOpStringPrinter(ty, value)
elif ty.code == gdb.TYPE_CODE_ARRAY:
result = NoOpArrayPrinter(ty, value)
elif ty.is_array_like:
diff --git a/mingw64/share/gdb/python/gdb/ptwrite.py b/mingw64/share/gdb/python/gdb/ptwrite.py
new file mode 100644
index 00000000000..3be65fedb67
--- /dev/null
+++ b/mingw64/share/gdb/python/gdb/ptwrite.py
@@ -0,0 +1,77 @@
+# Ptwrite utilities.
+# Copyright (C) 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+"""Utilities for working with ptwrite filters."""
+
+import gdb
+
+# _ptwrite_filter contains the per thread copies of the filter function.
+# The keys are tuples of inferior id and thread id.
+# The filter functions are created for each thread by calling the
+# _ptwrite_filter_factory.
+_ptwrite_filter = {}
+_ptwrite_filter_factory = None
+
+
+def _ptwrite_exit_handler(event):
+ """Exit handler to prune _ptwrite_filter on thread exit."""
+ _ptwrite_filter.pop(event.inferior_thread.ptid, None)
+
+
+gdb.events.thread_exited.connect(_ptwrite_exit_handler)
+
+
+def _clear_traces():
+ """Helper function to clear the trace of all threads."""
+ current_thread = gdb.selected_thread()
+
+ for inferior in gdb.inferiors():
+ for thread in inferior.threads():
+ thread.switch()
+ recording = gdb.current_recording()
+ if recording is not None:
+ recording.clear()
+
+ current_thread.switch()
+
+
+def register_filter_factory(filter_factory_):
+ """Register the ptwrite filter factory."""
+ if filter_factory_ is not None and not callable(filter_factory_):
+ raise TypeError("The filter factory must be callable or 'None'.")
+
+ # Clear the traces of all threads of all inferiors to force
+ # re-decoding with the new filter.
+ _clear_traces()
+
+ _ptwrite_filter.clear()
+ global _ptwrite_filter_factory
+ _ptwrite_filter_factory = filter_factory_
+
+
+def get_filter():
+ """Returns the filter of the current thread."""
+ thread = gdb.selected_thread()
+ key = thread.ptid
+
+ # Create a new filter for new threads.
+ if key not in _ptwrite_filter:
+ if _ptwrite_filter_factory is not None:
+ _ptwrite_filter[key] = _ptwrite_filter_factory(thread)
+ else:
+ return None
+
+ return _ptwrite_filter[key]
diff --git a/mingw64/share/gdb/python/gdb/xmethod.py b/mingw64/share/gdb/python/gdb/xmethod.py
index c98402d271f..e12d51c2c95 100644
--- a/mingw64/share/gdb/python/gdb/xmethod.py
+++ b/mingw64/share/gdb/python/gdb/xmethod.py
@@ -266,9 +266,14 @@ def register_xmethod_matcher(locus, matcher, replace=False):
del locus.xmethods[index]
else:
raise RuntimeError(
- "Xmethod matcher already registered with "
- "%s: %s" % (locus_name, matcher.name)
+ "Xmethod matcher already registered with {}: {}".format(
+ locus_name, matcher.name
+ )
)
if gdb.parameter("verbose"):
- gdb.write("Registering xmethod matcher '%s' with %s' ...\n")
+ gdb.write(
+ "Registering xmethod matcher '{}' with '{}' ...\n".format(
+ locus_name, matcher.name
+ )
+ )
locus.xmethods.insert(0, matcher)
diff --git a/mingw64/share/gdb/syscalls/aarch64-linux.xml b/mingw64/share/gdb/syscalls/aarch64-linux.xml
index 14caea5ab07..a56b1af45d2 100644
--- a/mingw64/share/gdb/syscalls/aarch64-linux.xml
+++ b/mingw64/share/gdb/syscalls/aarch64-linux.xml
@@ -4,8 +4,12 @@
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
- notice and this notice are preserved. This file is offered as-is,
- without any warranty. -->
+ notice and this notice are preserved. -->
+
@@ -251,7 +255,6 @@
-
@@ -265,5 +268,69 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/amd64-linux.xml b/mingw64/share/gdb/syscalls/amd64-linux.xml
index ddc0b37f3f7..a0e31836338 100644
--- a/mingw64/share/gdb/syscalls/amd64-linux.xml
+++ b/mingw64/share/gdb/syscalls/amd64-linux.xml
@@ -346,6 +346,7 @@
+
@@ -384,4 +385,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/arm-linux.xml b/mingw64/share/gdb/syscalls/arm-linux.xml
index 72a64566133..1cd40b7b45d 100644
--- a/mingw64/share/gdb/syscalls/arm-linux.xml
+++ b/mingw64/share/gdb/syscalls/arm-linux.xml
@@ -4,14 +4,13 @@
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
- notice and this notice are preserved. This file is offered as-is,
- without any warranty. -->
-
+
+ The files mentioned above belong to the Linux Kernel. -->
@@ -72,7 +71,7 @@
-
+
@@ -387,9 +386,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/i386-linux.xml b/mingw64/share/gdb/syscalls/i386-linux.xml
index 531bf73da45..4df3a779e76 100644
--- a/mingw64/share/gdb/syscalls/i386-linux.xml
+++ b/mingw64/share/gdb/syscalls/i386-linux.xml
@@ -462,4 +462,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/loongarch-linux.xml b/mingw64/share/gdb/syscalls/loongarch-linux.xml
index 6e85dbd7166..9130e45b42b 100644
--- a/mingw64/share/gdb/syscalls/loongarch-linux.xml
+++ b/mingw64/share/gdb/syscalls/loongarch-linux.xml
@@ -9,29 +9,7 @@
- The file mentioned above belongs to the Linux Kernel.
-
- Note that the system header file /usr/include/asm-generic/unistd.h
- may be different with the latest upstream Linux kernel uapi header
- file include/uapi/asm-generic/unistd.h, it is better to copy the
- upstream header file into the system header file when generating
- loongarch-linux.xml.in.
-
- There exist some __NR3264_ prefixed syscall numbers, replace them
- with digital numbers according to /usr/include/asm-generic/unistd.h
- and sort them by syscall number manually, maybe we can modify the
- script to do it automatically in the future.
-
-
-
-
-
-
-
-
-
-
--->
+ The file mentioned above belongs to the Linux Kernel. -->
@@ -111,6 +89,8 @@
+
+
@@ -345,5 +325,10 @@
-
+
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/mips-n32-linux.xml b/mingw64/share/gdb/syscalls/mips-n32-linux.xml
index 911cb4158ef..3c6e8ddd43b 100644
--- a/mingw64/share/gdb/syscalls/mips-n32-linux.xml
+++ b/mingw64/share/gdb/syscalls/mips-n32-linux.xml
@@ -398,4 +398,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/mips-n64-linux.xml b/mingw64/share/gdb/syscalls/mips-n64-linux.xml
index 858a42333e7..5ef2d693906 100644
--- a/mingw64/share/gdb/syscalls/mips-n64-linux.xml
+++ b/mingw64/share/gdb/syscalls/mips-n64-linux.xml
@@ -373,4 +373,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/mips-o32-linux.xml b/mingw64/share/gdb/syscalls/mips-o32-linux.xml
index 345d2201003..71e5d1f2c43 100644
--- a/mingw64/share/gdb/syscalls/mips-o32-linux.xml
+++ b/mingw64/share/gdb/syscalls/mips-o32-linux.xml
@@ -438,4 +438,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/ppc-linux.xml b/mingw64/share/gdb/syscalls/ppc-linux.xml
index 0a3131407db..812abaa59d8 100644
--- a/mingw64/share/gdb/syscalls/ppc-linux.xml
+++ b/mingw64/share/gdb/syscalls/ppc-linux.xml
@@ -453,4 +453,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/ppc64-linux.xml b/mingw64/share/gdb/syscalls/ppc64-linux.xml
index 99435187098..0e4b3335542 100644
--- a/mingw64/share/gdb/syscalls/ppc64-linux.xml
+++ b/mingw64/share/gdb/syscalls/ppc64-linux.xml
@@ -425,4 +425,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/s390-linux.xml b/mingw64/share/gdb/syscalls/s390-linux.xml
index 9c2933284a3..bc6b4c310fb 100644
--- a/mingw64/share/gdb/syscalls/s390-linux.xml
+++ b/mingw64/share/gdb/syscalls/s390-linux.xml
@@ -443,4 +443,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/s390x-linux.xml b/mingw64/share/gdb/syscalls/s390x-linux.xml
index d3da20f9300..03e70bfc549 100644
--- a/mingw64/share/gdb/syscalls/s390x-linux.xml
+++ b/mingw64/share/gdb/syscalls/s390x-linux.xml
@@ -391,4 +391,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/sparc-linux.xml b/mingw64/share/gdb/syscalls/sparc-linux.xml
index cc2971bb2a7..b33e8c879ce 100644
--- a/mingw64/share/gdb/syscalls/sparc-linux.xml
+++ b/mingw64/share/gdb/syscalls/sparc-linux.xml
@@ -441,4 +441,9 @@
+
+
+
+
+
diff --git a/mingw64/share/gdb/syscalls/sparc64-linux.xml b/mingw64/share/gdb/syscalls/sparc64-linux.xml
index f69dd9f0245..f96035f6484 100644
--- a/mingw64/share/gdb/syscalls/sparc64-linux.xml
+++ b/mingw64/share/gdb/syscalls/sparc64-linux.xml
@@ -404,4 +404,9 @@
+
+
+
+
+
diff --git a/mingw64/share/locale/de/LC_MESSAGES/opcodes.mo b/mingw64/share/locale/de/LC_MESSAGES/opcodes.mo
index fd9bd7b0d21..4e30221075b 100644
Binary files a/mingw64/share/locale/de/LC_MESSAGES/opcodes.mo and b/mingw64/share/locale/de/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw64/share/locale/es/LC_MESSAGES/bfd.mo b/mingw64/share/locale/es/LC_MESSAGES/bfd.mo
index 48b357e9123..7fbb9bd18e9 100644
Binary files a/mingw64/share/locale/es/LC_MESSAGES/bfd.mo and b/mingw64/share/locale/es/LC_MESSAGES/bfd.mo differ
diff --git a/mingw64/share/locale/fr/LC_MESSAGES/bfd.mo b/mingw64/share/locale/fr/LC_MESSAGES/bfd.mo
index fc52247d310..ebeb7cb9fdf 100644
Binary files a/mingw64/share/locale/fr/LC_MESSAGES/bfd.mo and b/mingw64/share/locale/fr/LC_MESSAGES/bfd.mo differ
diff --git a/mingw64/share/locale/fr/LC_MESSAGES/opcodes.mo b/mingw64/share/locale/fr/LC_MESSAGES/opcodes.mo
index 6166f2cfe85..4bba71e7877 100644
Binary files a/mingw64/share/locale/fr/LC_MESSAGES/opcodes.mo and b/mingw64/share/locale/fr/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw64/share/locale/ms/LC_MESSAGES/bfd.mo b/mingw64/share/locale/ms/LC_MESSAGES/bfd.mo
new file mode 100644
index 00000000000..2777b3b74f2
Binary files /dev/null and b/mingw64/share/locale/ms/LC_MESSAGES/bfd.mo differ
diff --git a/mingw64/share/locale/ro/LC_MESSAGES/bfd.mo b/mingw64/share/locale/ro/LC_MESSAGES/bfd.mo
index c781327497a..7a5f7595b9a 100644
Binary files a/mingw64/share/locale/ro/LC_MESSAGES/bfd.mo and b/mingw64/share/locale/ro/LC_MESSAGES/bfd.mo differ
diff --git a/mingw64/share/locale/ro/LC_MESSAGES/opcodes.mo b/mingw64/share/locale/ro/LC_MESSAGES/opcodes.mo
index 1ebde6faae1..82cb1b94a73 100644
Binary files a/mingw64/share/locale/ro/LC_MESSAGES/opcodes.mo and b/mingw64/share/locale/ro/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw64/share/locale/ru/LC_MESSAGES/bfd.mo b/mingw64/share/locale/ru/LC_MESSAGES/bfd.mo
index 6708c32d3b2..ddcb9ae804e 100644
Binary files a/mingw64/share/locale/ru/LC_MESSAGES/bfd.mo and b/mingw64/share/locale/ru/LC_MESSAGES/bfd.mo differ
diff --git a/mingw64/share/locale/sr/LC_MESSAGES/bfd.mo b/mingw64/share/locale/sr/LC_MESSAGES/bfd.mo
index 6db738f45d0..e6f41d7ee64 100644
Binary files a/mingw64/share/locale/sr/LC_MESSAGES/bfd.mo and b/mingw64/share/locale/sr/LC_MESSAGES/bfd.mo differ
diff --git a/mingw64/share/locale/sr/LC_MESSAGES/opcodes.mo b/mingw64/share/locale/sr/LC_MESSAGES/opcodes.mo
index 86992217b7e..145d60f2e7b 100644
Binary files a/mingw64/share/locale/sr/LC_MESSAGES/opcodes.mo and b/mingw64/share/locale/sr/LC_MESSAGES/opcodes.mo differ
diff --git a/mingw64/share/locale/uk/LC_MESSAGES/bfd.mo b/mingw64/share/locale/uk/LC_MESSAGES/bfd.mo
index 92c7b2daed9..77660e1bf66 100644
Binary files a/mingw64/share/locale/uk/LC_MESSAGES/bfd.mo and b/mingw64/share/locale/uk/LC_MESSAGES/bfd.mo differ
diff --git a/mingw64/share/locale/uk/LC_MESSAGES/opcodes.mo b/mingw64/share/locale/uk/LC_MESSAGES/opcodes.mo
index 3924ae5df10..f35a36e6176 100644
Binary files a/mingw64/share/locale/uk/LC_MESSAGES/opcodes.mo and b/mingw64/share/locale/uk/LC_MESSAGES/opcodes.mo differ
diff --git a/var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/mtree b/var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/mtree
deleted file mode 100644
index aba4d90cd44..00000000000
Binary files a/var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/mtree and /dev/null differ
diff --git a/var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/desc b/var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/desc
similarity index 86%
rename from var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/desc
rename to var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/desc
index da02f92769a..08be1985ba3 100644
--- a/var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/desc
+++ b/var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/desc
@@ -2,7 +2,7 @@
mingw-w64-i686-gdb
%VERSION%
-15.2-2
+16.1-1
%BASE%
mingw-w64-gdb
@@ -17,16 +17,16 @@ https://www.gnu.org/software/gdb/
any
%BUILDDATE%
-1730677137
+1737216058
%INSTALLDATE%
-1731121895
+1737342687
%PACKAGER%
-CI (msys2/msys2-autobuild/acafab9b/11654549310)
+CI (msys2/msys2-autobuild/a977f9de/12845424930)
%SIZE%
-14423744
+14738679
%GROUPS%
mingw-w64-i686-toolchain
@@ -57,5 +57,5 @@ mingw-w64-i686-zstd
mingw-w64-i686-python-pygments: for syntax highlighting
%XDATA%
-pkgtype=split
+pkgtype=pkg
diff --git a/var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/files b/var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/files
similarity index 94%
rename from var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/files
rename to var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/files
index 98bef148331..2310cade681 100644
--- a/var/lib/pacman/local/mingw-w64-i686-gdb-15.2-2/files
+++ b/var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/files
@@ -4,6 +4,7 @@ mingw32/bin/
mingw32/bin/gdb-add-index
mingw32/bin/gdb.exe
mingw32/bin/gdbserver.exe
+mingw32/bin/gstack
mingw32/include/
mingw32/include/gdb/
mingw32/include/gdb/jit-reader.h
@@ -17,7 +18,7 @@ mingw32/share/gdb/python/gdb/command/
mingw32/share/gdb/python/gdb/command/__init__.py
mingw32/share/gdb/python/gdb/command/explore.py
mingw32/share/gdb/python/gdb/command/frame_filters.py
-mingw32/share/gdb/python/gdb/command/missing_debug.py
+mingw32/share/gdb/python/gdb/command/missing_files.py
mingw32/share/gdb/python/gdb/command/pretty_printers.py
mingw32/share/gdb/python/gdb/command/prompt.py
mingw32/share/gdb/python/gdb/command/type_printers.py
@@ -31,6 +32,7 @@ mingw32/share/gdb/python/gdb/dap/disassemble.py
mingw32/share/gdb/python/gdb/dap/evaluate.py
mingw32/share/gdb/python/gdb/dap/events.py
mingw32/share/gdb/python/gdb/dap/frames.py
+mingw32/share/gdb/python/gdb/dap/globalvars.py
mingw32/share/gdb/python/gdb/dap/io.py
mingw32/share/gdb/python/gdb/dap/launch.py
mingw32/share/gdb/python/gdb/dap/locations.py
@@ -56,11 +58,13 @@ mingw32/share/gdb/python/gdb/function/as_string.py
mingw32/share/gdb/python/gdb/function/caller_is.py
mingw32/share/gdb/python/gdb/function/strfns.py
mingw32/share/gdb/python/gdb/missing_debug.py
+mingw32/share/gdb/python/gdb/missing_files.py
+mingw32/share/gdb/python/gdb/missing_objfile.py
mingw32/share/gdb/python/gdb/printer/
mingw32/share/gdb/python/gdb/printer/__init__.py
-mingw32/share/gdb/python/gdb/printer/bound_registers.py
mingw32/share/gdb/python/gdb/printing.py
mingw32/share/gdb/python/gdb/prompt.py
+mingw32/share/gdb/python/gdb/ptwrite.py
mingw32/share/gdb/python/gdb/styling.py
mingw32/share/gdb/python/gdb/types.py
mingw32/share/gdb/python/gdb/unwinder.py
@@ -125,6 +129,9 @@ mingw32/share/locale/ja/LC_MESSAGES/bfd.mo
mingw32/share/locale/ka/
mingw32/share/locale/ka/LC_MESSAGES/
mingw32/share/locale/ka/LC_MESSAGES/bfd.mo
+mingw32/share/locale/ms/
+mingw32/share/locale/ms/LC_MESSAGES/
+mingw32/share/locale/ms/LC_MESSAGES/bfd.mo
mingw32/share/locale/nl/
mingw32/share/locale/nl/LC_MESSAGES/
mingw32/share/locale/nl/LC_MESSAGES/opcodes.mo
diff --git a/var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/mtree b/var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/mtree
new file mode 100644
index 00000000000..81db3a1701a
Binary files /dev/null and b/var/lib/pacman/local/mingw-w64-i686-gdb-16.1-1/mtree differ
diff --git a/var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/mtree b/var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/mtree
deleted file mode 100644
index b3530eaeae2..00000000000
Binary files a/var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/mtree and /dev/null differ
diff --git a/var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/desc b/var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/desc
similarity index 89%
rename from var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/desc
rename to var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/desc
index 7b1363d264b..261f6f0c67b 100644
--- a/var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/desc
+++ b/var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/desc
@@ -2,7 +2,7 @@
mingw-w64-x86_64-gdb
%VERSION%
-15.2-2
+16.1-1
%BASE%
mingw-w64-gdb
@@ -17,16 +17,16 @@ https://www.gnu.org/software/gdb/
any
%BUILDDATE%
-1730690239
+1737216057
%INSTALLDATE%
-1731121908
+1737342687
%PACKAGER%
-CI (msys2/msys2-autobuild/acafab9b/11657424971)
+CI (msys2/msys2-autobuild/a977f9de/12845424930)
%SIZE%
-14143127
+14445199
%GROUPS%
mingw-w64-x86_64-toolchain
diff --git a/var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/files b/var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/files
similarity index 94%
rename from var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/files
rename to var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/files
index 5615909b8d6..30c43274930 100644
--- a/var/lib/pacman/local/mingw-w64-x86_64-gdb-15.2-2/files
+++ b/var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/files
@@ -4,6 +4,7 @@ mingw64/bin/
mingw64/bin/gdb-add-index
mingw64/bin/gdb.exe
mingw64/bin/gdbserver.exe
+mingw64/bin/gstack
mingw64/include/
mingw64/include/gdb/
mingw64/include/gdb/jit-reader.h
@@ -17,7 +18,7 @@ mingw64/share/gdb/python/gdb/command/
mingw64/share/gdb/python/gdb/command/__init__.py
mingw64/share/gdb/python/gdb/command/explore.py
mingw64/share/gdb/python/gdb/command/frame_filters.py
-mingw64/share/gdb/python/gdb/command/missing_debug.py
+mingw64/share/gdb/python/gdb/command/missing_files.py
mingw64/share/gdb/python/gdb/command/pretty_printers.py
mingw64/share/gdb/python/gdb/command/prompt.py
mingw64/share/gdb/python/gdb/command/type_printers.py
@@ -31,6 +32,7 @@ mingw64/share/gdb/python/gdb/dap/disassemble.py
mingw64/share/gdb/python/gdb/dap/evaluate.py
mingw64/share/gdb/python/gdb/dap/events.py
mingw64/share/gdb/python/gdb/dap/frames.py
+mingw64/share/gdb/python/gdb/dap/globalvars.py
mingw64/share/gdb/python/gdb/dap/io.py
mingw64/share/gdb/python/gdb/dap/launch.py
mingw64/share/gdb/python/gdb/dap/locations.py
@@ -56,11 +58,13 @@ mingw64/share/gdb/python/gdb/function/as_string.py
mingw64/share/gdb/python/gdb/function/caller_is.py
mingw64/share/gdb/python/gdb/function/strfns.py
mingw64/share/gdb/python/gdb/missing_debug.py
+mingw64/share/gdb/python/gdb/missing_files.py
+mingw64/share/gdb/python/gdb/missing_objfile.py
mingw64/share/gdb/python/gdb/printer/
mingw64/share/gdb/python/gdb/printer/__init__.py
-mingw64/share/gdb/python/gdb/printer/bound_registers.py
mingw64/share/gdb/python/gdb/printing.py
mingw64/share/gdb/python/gdb/prompt.py
+mingw64/share/gdb/python/gdb/ptwrite.py
mingw64/share/gdb/python/gdb/styling.py
mingw64/share/gdb/python/gdb/types.py
mingw64/share/gdb/python/gdb/unwinder.py
@@ -125,6 +129,9 @@ mingw64/share/locale/ja/LC_MESSAGES/bfd.mo
mingw64/share/locale/ka/
mingw64/share/locale/ka/LC_MESSAGES/
mingw64/share/locale/ka/LC_MESSAGES/bfd.mo
+mingw64/share/locale/ms/
+mingw64/share/locale/ms/LC_MESSAGES/
+mingw64/share/locale/ms/LC_MESSAGES/bfd.mo
mingw64/share/locale/nl/
mingw64/share/locale/nl/LC_MESSAGES/
mingw64/share/locale/nl/LC_MESSAGES/opcodes.mo
diff --git a/var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/mtree b/var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/mtree
new file mode 100644
index 00000000000..d2e4b2ca869
Binary files /dev/null and b/var/lib/pacman/local/mingw-w64-x86_64-gdb-16.1-1/mtree differ
diff --git a/var/lib/pacman/local/mingw-w64-x86_64-git-extra-1.1.641.031e03baf-1/desc b/var/lib/pacman/local/mingw-w64-x86_64-git-extra-1.1.641.031e03baf-1/desc
index 130d60f46d7..ae1ff6a6989 100644
--- a/var/lib/pacman/local/mingw-w64-x86_64-git-extra-1.1.641.031e03baf-1/desc
+++ b/var/lib/pacman/local/mingw-w64-x86_64-git-extra-1.1.641.031e03baf-1/desc
@@ -20,7 +20,7 @@ any
1718204260
%INSTALLDATE%
-1737256384
+1737342689
%PACKAGER%
Johannes Schindelin
diff --git a/var/lib/pacman/sync/clang64.db b/var/lib/pacman/sync/clang64.db
index 0f6289dc5e0..f3efb8f05f6 100644
Binary files a/var/lib/pacman/sync/clang64.db and b/var/lib/pacman/sync/clang64.db differ
diff --git a/var/lib/pacman/sync/clang64.db.sig b/var/lib/pacman/sync/clang64.db.sig
index 13f87923eea..b6acad11b08 100644
Binary files a/var/lib/pacman/sync/clang64.db.sig and b/var/lib/pacman/sync/clang64.db.sig differ
diff --git a/var/lib/pacman/sync/clangarm64.db b/var/lib/pacman/sync/clangarm64.db
index 8c7bc5fd063..82dcc93efac 100644
Binary files a/var/lib/pacman/sync/clangarm64.db and b/var/lib/pacman/sync/clangarm64.db differ
diff --git a/var/lib/pacman/sync/clangarm64.db.sig b/var/lib/pacman/sync/clangarm64.db.sig
index a08c5b040d7..e3a73f0ad9e 100644
Binary files a/var/lib/pacman/sync/clangarm64.db.sig and b/var/lib/pacman/sync/clangarm64.db.sig differ
diff --git a/var/lib/pacman/sync/mingw32.db b/var/lib/pacman/sync/mingw32.db
index 07cea42f669..89879d7807c 100644
Binary files a/var/lib/pacman/sync/mingw32.db and b/var/lib/pacman/sync/mingw32.db differ
diff --git a/var/lib/pacman/sync/mingw32.db.sig b/var/lib/pacman/sync/mingw32.db.sig
index 395a5d60516..67340ad1ebb 100644
Binary files a/var/lib/pacman/sync/mingw32.db.sig and b/var/lib/pacman/sync/mingw32.db.sig differ
diff --git a/var/lib/pacman/sync/mingw64.db b/var/lib/pacman/sync/mingw64.db
index de34ff0ee5a..0d5fc17ee79 100644
Binary files a/var/lib/pacman/sync/mingw64.db and b/var/lib/pacman/sync/mingw64.db differ
diff --git a/var/lib/pacman/sync/mingw64.db.sig b/var/lib/pacman/sync/mingw64.db.sig
index 7589102e5e3..7c032bd5bbf 100644
Binary files a/var/lib/pacman/sync/mingw64.db.sig and b/var/lib/pacman/sync/mingw64.db.sig differ
diff --git a/var/lib/pacman/sync/ucrt64.db b/var/lib/pacman/sync/ucrt64.db
index ff77d18c735..ae3b0f49829 100644
Binary files a/var/lib/pacman/sync/ucrt64.db and b/var/lib/pacman/sync/ucrt64.db differ
diff --git a/var/lib/pacman/sync/ucrt64.db.sig b/var/lib/pacman/sync/ucrt64.db.sig
index 44f4f0a0628..16a1a7ea51f 100644
Binary files a/var/lib/pacman/sync/ucrt64.db.sig and b/var/lib/pacman/sync/ucrt64.db.sig differ