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