Blob Blame History Raw
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
From: Andrew Burgess <aburgess@redhat.com>
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 <http://www.gnu.org/licenses/>.
+
+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 = []