From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 From: Andrew Burgess Date: Thu, 7 Mar 2024 15:14:23 +0000 Subject: gdb-add-rpm-suggestion-script.patch ;; Not a backport. Add a new script which hooks into GDB and suggests ;; RPMs to install when GDB finds an objfile with no debug info. gdb: add script which will suggest debuginfo RPMs to install This script hooks into GDB's missing debug info Python API and suggests debuginfo RPMs to install. diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -86,6 +86,7 @@ PYTHON_FILE_LIST = \ gdb/command/missing_debug.py \ gdb/command/pretty_printers.py \ gdb/command/prompt.py \ + gdb/command/rpm-suggestions.py \ gdb/command/type_printers.py \ gdb/command/unwinders.py \ gdb/command/xmethods.py \ diff --git a/gdb/python/lib/gdb/command/rpm-suggestions.py b/gdb/python/lib/gdb/command/rpm-suggestions.py new file mode 100644 --- /dev/null +++ b/gdb/python/lib/gdb/command/rpm-suggestions.py @@ -0,0 +1,111 @@ +# Copyright 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 . + +import gdb +import gdb.missing_debug +import rpm + +# Track all the RPMs suggested during a single debug session so we +# don't suggest the same RPM twice. This is only cleared when the +# main executable is changed. +__missing_rpms = {} + +# Track any missing RPMs that have been discovered since the last time +# the prompt was displayed. RPMs in here are also present in the +# __MISSING_RPMS dictionary, but this dictionary is cleared each time +# the prompt is shown. +__suggest_rpms = {} + + +# Lookup RPMs that might provide the debug information for FILENAME, +# which is a string containing the path to an object file GDB could +# not find any debug information for. +# +# If a possible RPM is found then this is added to the globals +# __MISSING_RPMS and __SUGGEST_RPMS, which are used elsewhere in this +# script. +def find_suggestions(filename): + ts = rpm.TransactionSet() + + mi = ts.dbMatch(rpm.RPMDBI_BASENAMES, filename) + for h in mi: + # Build the debuginfo package name. + obj = h.format("%{name}-debuginfo-%{version}-%{release}.%{arch}") + + # Check to see if the package is installed. + mi2 = ts.dbMatch(rpm.RPMDBI_LABEL, str(obj)) + if len(mi2) > 0: + continue + + # Now build the name of the package FILENAME came from. + obj = h.format("%{name}-%{version}-%{release}.%{arch}") + rpm_name = str(obj) + if not rpm_name in __missing_rpms: + __suggest_rpms[rpm_name] = True + __missing_rpms[rpm_name] = True + + +# A missing debug handler class. Just forwards the name of the +# objfile for which we are missing debug information to +# find_suggestions. +class RPMSuggestionHandler(gdb.missing_debug.MissingDebugHandler): + def __init__(self): + super().__init__("rpm-suggestions") + + def __call__(self, objfile): + # Traditionally the 'build-id-verbose' parameter is what + # controlled all RPM suggestion. Maybe once all the RPM + # suggestion is performed via Python extensions then we might + # consider renaming this parameter to something else, but for + # now, for backward compatibility, I've retained this name. + if gdb.parameter("build-id-verbose") > 0: + find_suggestions(objfile.filename) + return False + return None + + +# Called before GDB displays its prompt. If the global __SUGGEST_RPMS +# dictionary is not empty, then this hook prints treats the keys of +# this dictionary as strings which are the names of RPMs. This hook +# formats each RPM name into a suggested debuginfo-install command and +# suggests this to the user. +def before_prompt(): + global __suggest_rpms + + if len(__suggest_rpms) > 0: + for p in __suggest_rpms.keys(): + print("Missing debuginfo, try: dnf debuginfo-install " + p) + __suggest_rpms = {} + + +# Called when the executable within a progrm space is changed. Clear +# the lists of RPM suggestions. We only clear the previous suggestion +# list when the executable really changes. If the user simply +# recompiles the executable, then we don't both clearing this list. +def executable_changed_handler(event): + global __missing_rpms + global __suggest_rpms + + if not event.reload: + __missing_rpms = {} + __suggest_rpms = {} + + +# Attach to the required GDB events. +gdb.events.executable_changed.connect(executable_changed_handler) +gdb.events.before_prompt.connect(before_prompt) + +# Register the missing debug handler with GDB. +gdb.missing_debug.register_handler(None, RPMSuggestionHandler()) diff --git a/gdb/testsuite/gdb.python/py-missing-debug.py b/gdb/testsuite/gdb.python/py-missing-debug.py --- a/gdb/testsuite/gdb.python/py-missing-debug.py +++ b/gdb/testsuite/gdb.python/py-missing-debug.py @@ -18,6 +18,13 @@ from gdb.missing_debug import MissingDebugHandler from enum import Enum import os +# This is a RHEL/Fedora work around: There's already a +# missing-debug-info handler registered for these versions of GDB. +# Discard the handler now so that the tests will pass (the tests +# assume no handler is currently registered). +gdb.missing_debug_handlers = [] + + # A global log that is filled in by instances of the LOG_HANDLER class # when they are called. handler_call_log = []