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