From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 From: Andrew Burgess Date: Thu, 7 Mar 2024 15:14:23 +0000 Subject: gdb-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 @@ -92,6 +92,7 @@ PYTHON_FILE_LIST = \ gdb/command/missing_files.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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -186,6 +186,7 @@ software in general. We will miss him. * Trace File Format:: @value{GDBN} trace file format * Index Section Format:: .gdb_index section format * Debuginfod:: Download debugging resources with @code{debuginfod} +* RPM Suggestions:: RPM Suggestions from GDB * Man Pages:: Manual pages * Copying:: GNU General Public License says how you can copy and share @value{GDBN} @@ -50588,6 +50589,111 @@ Show the current verbosity setting. @end table +@node RPM Suggestions +@appendix Receiving RPM installation suggestions +@cindex rpm suggestions + +When @value{GDBN} loads an executable, or shared library, and cannot +find the corresponding debug information, @value{GDBN} will check to +see if an RPM is available which could provide the missing debug +information. If a suitable RPM is found then @value{GDBN} will print +a hint before the next prompt is displayed: + +@smallexample +(@value{GDBP}) file /bin/ls +Reading symbols from /bin/ls... +Reading symbols from .gnu_debugdata for /usr/bin/ls... +(No debugging symbols found in .gnu_debugdata for /usr/bin/ls) +Missing rpms, try: dnf --enablerepo='*debug*' install coreutils-debuginfo-9.3-7.fc39.x86_64 +(@value{GDBP}) +@end smallexample + +In this case, installing @file{coreutils-debuginfo-9.3-7.fc39.x86_64} +will provide the missing debug information for @file{/bin/ls}. It is +up to you to install the suggested package, if possible, and after +that reload the executable in @value{GDBN} so that the newly installed +debug information can be found. + +The RPM suggestion feature can be disabled: + +@table @code +@kindex set rpm-suggestion enabled +@kindex show rpm-suggestion enabled +@cindex rpm suggestions, disabling +@item set rpm-suggestion enabled @r{[}on@r{|}off@r{]} +@itemx show rpm-suggestion enabled +When @samp{on} @value{GDBN} will make RPM suggestions where possible. +When @samp{off} all RPM suggestion will be disabled. +@end table + +When opening a core file (@pxref{core-file command}), it may be the +case that not only is the debug information missing, but the +corresponding executable itself is missing. For example, if a core +file is copied from one machine to another in order to debug. + +In this case @value{GDBN} is able to suggest a command which will +install the missing executable based on the build-id of the +executable. For example: + +@smallexample +(@value{GDBP}) core-file /tmp/core.5489 +warning: Can't open file /usr/bin/sl during file-backed mapping note processing +[New LWP 5489] +Core was generated by `sl'. +Program terminated with signal SIGQUIT, Quit. +#0 0x00007f1b41ced1a7 in ?? () +Missing file(s), try: dnf --enablerepo='*debug*' install /usr/lib/.build-id/33/2f1a8e56693960e3beb2d70cd79ddfec451cc3 /usr/lib/debug/.build-id/33/2f1a8e56693960e3beb2d70cd79ddfec451cc3.debug +(@value{GDBP}) +@end smallexample + +The core file was generated from the @file{/usr/bin/sl} binary, which +is not present on the machine opening the core file. @value{GDBN} has +suggested a command, based on the build-id of the binary, which was +extracted from the core file, that would install both the missing +binary, and the corresponding debug information. + +Unfortunately, @value{GDBN} doesn't know if the suggested command will +actually find a matching RPM or not. Querying the RPM database to see +which packages, if any, will provide a file with the given build-id, +is rather slow. As @file{/usr/bin/sl} is available in an RPM, then +the above command will succeed. + +It is possible to have @value{GDBN} check to see if there is a package +available before making the suggestion, but this is significantly +slower. To enable this mode use the following command: + +@table @code +@kindex set rpm-suggestion build-id-mode +@kindex show rpm-suggestion build-id-mode +@cindex rpm suggestions, build-id-mode +@item set rpm-suggestion build-id-mode @r{[}fast@r{|}slow@r{]} +@itemx show rpm-suggestion build-id-mode +When set to @samp{fast}, which is the default, @value{GDBN} will offer +suggestions based on the build-id of any missing executables or shared +libraries while opening a core file. This is fast, but @value{GDBN} +has not checked if there is a package available that can supply the +required file, so running the suggested command might not install any +packages. + +When set to @samp{slow}, each time @value{GDBN} encounters an +executable, or shared library, that is missing, @value{GDBN} will +check to see if there is an RPM available that will supply the missing +binary. If a suitable RPM is found then @value{GDBN} will offer a +command which will install the missing RPM. If no suitable RPM is +found then @value{GDBN} will make no suggestions. +@end table + +It is possible to review all of the previous RPM suggestions that +@value{GDBN} has made using the following command: + +@table @code +@kindex info rpm-suggestions +@cindex rpm suggestions, listing +@item info rpm-suggestions +List all of the RPM suggestions @value{GDBN} has made since the +executable was last changed. +@end table + @node Man Pages @appendix Manual pages @cindex Man pages 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,527 @@ +# 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 gdb.missing_objfile + +# These modules are all system modules, and should be available on any +# correctly setup Python install. +import sys +import os +import subprocess +import re +from threading import Thread +import time + +try: + import rpm +except ModuleNotFoundError: + # The "RPM suggestions" mechanism will not work without the (python) + # rpm module. Inform the user of this, but wait to do so until + # just prior to printing the GDB prompt. If we do it right away, + # the message typically appears before the version and copyright + # info, which is easily missed by many users. Additionally, it + # seems that several other packages which parse GDB version info + # are confused by an early error message regarding a missing + # python3-rpm package, so waiting to print the error allows those + # applications to work as they used to. + def before_prompt(): + print( + ("\nUnable to load the Python 'rpm' module. Lack of this module disables\n" + "the RPM suggestions mechanism which recommends shell commands for\n" + "installing missing debuginfo packages. To enable this functionality,\n" + "please install the python3-rpm package."), + file=sys.stderr + ) + gdb.events.before_prompt.disconnect(before_prompt) + + gdb.events.before_prompt.connect(before_prompt) + + # Implement 'info rpm-suggestions' when the 'rpm' module is not + # available. Just throws an error. + def info_rpm_suggestions(): + raise gdb.GdbError("rpm-suggestions are disabled as the Python 'rpm' module is not installed") +else: + # 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 = {} + + # Track all the build-ids suggested during a single debug session so we + # don't suggest installing using the same build-id twice. This is only + # cleared when the main executable is changed. + __missing_build_ids = {} + + # Track any build-ids that have been discovered missing since the last + # time the prompt was displayed. Build-ids in here are also present in + # the __MISSING_BUILD_IDS dictionary, but this dictionary is cleared + # each time the prompt is shown. + __suggest_build_ids = {} + + # The build-id to RPM lookup is very slow. This cache maps + # build-ids to the set of RPM we can suggest installing. The key + # is the build-id string, and the value is a list of RPM names, or + # None if there was an error with the build-id to RPM lookup. + # + # This cache is never cleared, even when the executable is + # changed. The build-ids should be unique, so a build-id lookup + # should be good for the lifetime of the session. + __build_id_lookup_cache = {} + + # When searching for an RPM given a build-id, if the search takes + # too long, then a message is printed to the user. We only print + # the message once between each GDB prompt. This flag is set True + # when the message is printed, and reset to False when a prompt is + # displayed. + __searching_message_printed = False + + # Add a suggestion to install RPM_NAME (the full name of an RPM) + # to the list of suggestions to make the next time a prompt is + # displayed. + def add_rpm_suggestion(rpm_name): + global __missing_rpms + global __suggest_rpms + + if not rpm_name in __missing_rpms: + __suggest_rpms[rpm_name] = True + __missing_rpms[rpm_name] = True + + # Return True if RPM_NAME is installed, where RPM_NAME is the full + # name of an RPM. + def is_rpm_installed(ts, rpm_name): + res = ts.dbMatch(rpm.RPMDBI_LABEL, rpm_name) + return len(res) > 0 + + # Add a suggestion to install RPMs based on BUILD_ID, a string + # containing a build-id, to the list of suggestions to make the next + # time a prompt is displayed. + def add_build_id_suggestion(build_id): + global __missing_build_ids + global __suggest_build_ids + + if not build_id in __missing_build_ids: + __suggest_build_ids[build_id] = True + __missing_build_ids[build_id] = True + + # Return true if '/usr/lib' is in the debug-file-directory list. + # System packages install their debug information into /usr/lib, + # so if GDB isn't looking in that directory, then there's no + # reason to try and figure out a suitable RPM to install. + def using_suitable_debug_file_directory(): + debug_file_directories = gdb.parameter("debug-file-directory") + for d in debug_file_directories.split(os.pathsep): + if d[:8] == "/usr/lib": + return True + return False + + # Return True if rpm-suggestion is disabled for any reason. + def rpm_suggestion_is_disabled(): + # If /usr/lib/ is not being used to find debug information + # then there's no point offering any RPMs as GDB would not + # find the newly installed content. + if not using_suitable_debug_file_directory(): + return True + + # Is 'rpm-suggestion enabled' set to 'off'? + if not param_rpm_suggestion_enabled.value: + return True + + return False + + + # 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_debug_suggestions(filename): + if rpm_suggestion_is_disabled(): + return + + 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}") + rpm_name = str(obj) + + # Check to see if the package is installed. + if is_rpm_installed(ts, rpm_name): + continue + + add_rpm_suggestion(rpm_name) + + + # Return a string which is the filename of the filename + # corresponding to BUILD_ID in one of the two locations under + # /usr/lib. + # + # When DEBUG_P is True, return a filename within: + # /usr/lib/debug/.build-id/ and when DEBUG_P is False, return a + # filename within: /usr/lib/.build-id/. + # + # The SUFFIX string is appended to the generated filename. + def build_id_to_usr_lib_filename(build_id, debug_p, suffix = ""): + key = build_id[:2] + rst = build_id[2:] + + filename = '/usr/lib' + if debug_p: + filename += '/debug' + filename += '/.build-id/' + key + '/' + rst + suffix + + return filename + + # A regexp object used to match against the output of `dnf`. This + # is initialised the first time it is needed. + find_objfile_suggestions_re = None + + # Given BUILD_ID, a string containing a build-id, run a `dnf` + # command to figure out if any RPMs can provide a file with that + # build-id. + # + # If any suitable RPMs are found then `add_rpm_suggestion` is called + # to register the suggestion. + # + # This function is pretty slow, which is a shame, as the results + # returned are much nicer than just telling the user to try the + # lookup command for themselves. + def find_objfile_suggestions_in_thread(build_id): + global find_objfile_suggestions_re + + if find_objfile_suggestions_re is None: + find_objfile_suggestions_re = re.compile("^(.*)-debuginfo-(.*) : Debug information for package (.*)$") + + result = subprocess.run(['dnf', "--enablerepo=*debug*", '--nogpgcheck', '-C', 'provides', + build_id_to_usr_lib_filename(build_id, True)], + capture_output=True, timeout=30) + + lines = result.stdout.decode('utf-8').splitlines() + ts = rpm.TransactionSet() + + for l in lines: + m = find_objfile_suggestions_re.match(l) + if not m: + continue + if m.group(1) != m.group(3): + return + + main_rpm = m.group(1) + '-' + m.group(2) + dbg_rpm = m.group(1) + '-debuginfo-' + m.group(2) + + if not is_rpm_installed(ts, main_rpm): + add_rpm_suggestion(main_rpm) + + if not is_rpm_installed(ts, dbg_rpm): + add_rpm_suggestion(dbg_rpm) + + return + + # Call `find_objfile_suggestions_in_thread` is a separate thread, + # then wait for the thread to complete. Don't touch any shared + # state while waiting for the thread to complete. There's no + # locking on any of our caches, and the worker thread will add RPM + # suggestions as it wants. + # + # If thre thread takes too long to complete then print a message + # to the user telling them what's going on. + def find_objfile_suggestions_slow_core(build_id): + global __searching_message_printed + + thread = Thread(target = find_objfile_suggestions_in_thread, args = (build_id, )) + thread.start() + start = time.time_ns() + + while thread.is_alive (): + time.sleep(0.05) + now = time.time_ns () + if not __searching_message_printed and (now - start > 1000000000): + print("Searching for packages to install that could improve debugging...") + __searching_message_printed = True + + thread.join() + + + # Given BUILD_ID, a string containing a build-id, lookup suitable + # RPMs that could be installed to provide a file with the required + # build-id. + # + # Any suitable RPMs are recorded by calling `add_rpm_suggestion`, and + # will be printed before the next prompt. + def find_objfile_suggestions_slow(build_id): + global __build_id_lookup_cache + global __suggest_rpms + + # The code to lookup an RPM given only a build-id is pretty + # slow. Cache the results to try and reduce the UI delays. + if build_id in __build_id_lookup_cache: + rpms = __build_id_lookup_cache[build_id] + if rpms is not None: + for r in rpms: + add_rpm_suggestion(r) + return + + # Make sure the cache entry exists before we do the lookup. + # If, for any reason, the lookup raises an exception, then + # having a cache entry will prevent us retrying this lookup in + # the future. + __build_id_lookup_cache[build_id] = None + + # Now do the lookup. This is the slow part. + find_objfile_suggestions_slow_core(build_id) + + # Fill in the cache, for a given build-id which RPMs were + # suggested. + rpms = [] + for r in __suggest_rpms: + rpms.append(r) + __build_id_lookup_cache[build_id] = rpms + + + # Given BUILD_ID, a string containing a build-id, just record that we + # should advise the user to try installing RPMs based on this build-id. + def find_objfile_suggestions_fast(build_id): + add_build_id_suggestion(build_id) + + # Given BUILD_ID, a string containing a build-id, which GDB has failed + # to find, possibly record some information so that we can, at the next + # prompt, give some RPM installation advice to the user. + # + # We have two different strategies for RPM lookup based on a build-id, + # one approach is that we actually lookup the RPMs and only suggest + # something if there is a suitable RPM. However, this is pretty slow, + # and users will probably find this annoying. + # + # So we also have a fast way. With this approach we just record the + # build-id that was missing and tell the user to try installing based on + # the build-id. The downside with this is that if there is no RPM for + # that build-id, then the user will try the command, but nothing will + # install. + def find_objfile_suggestions(build_id): + if rpm_suggestion_is_disabled(): + return + + if param_rpm_suggestion_build_id_mode.fast_mode(): + find_objfile_suggestions_fast(build_id) + else: + find_objfile_suggestions_slow(build_id) + + # A missing debug handler class. Just forwards the name of the + # objfile for which we are missing debug information to + # find_debug_suggestions. + class RPM_MissingDebugHandler(gdb.missing_debug.MissingDebugHandler): + def __init__(self): + super().__init__("rpm-suggestions") + + def __call__(self, objfile): + find_debug_suggestions(objfile.filename) + return False + + # A missing objfile handler class. Just forwards the build-id of + # the objfile that is missing to find_objfile_suggestions. + class RPM_MissingObjfileHandler(gdb.missing_objfile.MissingObjfileHandler): + def __init__(self): + super().__init__("rpm-suggestions") + + def __call__(self, pspace, build_id, filename): + find_objfile_suggestions(build_id) + return False + + # Take a non-empty list of RPM names and print a command line a + # user could run to install these RPMs. + def print_rpm_suggestions(rpm_name_list): + print("Missing rpms, try: dnf --enablerepo='*debug*' install " + ' '.join(rpm_name_list)) + + # Take a non-empty list of build-id strings and print a series of + # lines that a user could run to instll the RPMs that provide + # files with this build-id. + # + # The printed commands will also install the corresponding debug + # packages for the executable with the given build-id. + def print_build_id_suggestions(build_id_list): + for build_id in build_id_list: + print("Missing file(s), try: dnf --enablerepo='*debug*' install " + + build_id_to_usr_lib_filename(build_id, False) + + ' ' + + build_id_to_usr_lib_filename(build_id, True, ".debug")) + + # Called before GDB displays its prompt. If the global __SUGGEST_RPMS + # dictionary is not empty, then this hook prints the keys of this + # dictionary as strings which are the names of RPMs. This hook formats + # each RPM name into a suggested 'dnf install' command and suggests this + # to the user. + # + # Additionally, if the global __SUGGEST_BUILD_IDS dictionary is not + # empty, then this hook uses the keys of this dictionary as build-ids + # that were found to be missing, and formats these into some file based + # 'dnf install' suggestions to the user. + def before_prompt(): + global __suggest_rpms + global __suggest_build_ids + global __searching_message_printed + + # We allow the searching message to be printed just once + # between prompts. + __searching_message_printed = False + + if len(__suggest_rpms) > 0: + print_rpm_suggestions(__suggest_rpms.keys()) + __suggest_rpms = {} + + if len(__suggest_build_ids) > 0: + print_build_id_suggestions(__suggest_build_ids.keys()) + __suggest_build_ids = {} + + # 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 + global __suggest_build_ids + global __missing_build_ids + + if not event.reload: + __missing_rpms = {} + __suggest_rpms = {} + __missing_build_ids = {} + __suggest_build_ids = {} + + + # 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 and missing objfile handlers with GDB. + gdb.missing_debug.register_handler(None, RPM_MissingDebugHandler()) + gdb.missing_objfile.register_handler(None, RPM_MissingObjfileHandler()) + + # Implement the core of 'info rpm-suggestions'. Reprint all rpm + # suggestions. + def info_rpm_suggestions(): + global __missing_rpms + global __missing_build_ids + + if len(__missing_rpms) == 0 and len(__missing_build_ids) == 0: + print("No RPM suggestions have been made so far.") + return + + if len(__missing_rpms) > 0: + print_rpm_suggestions(__missing_rpms.keys()) + if len(__missing_build_ids) > 0: + print_build_id_suggestions(__missing_build_ids.keys()) + +#################################################################### +# The following code is outside the 'else' block of the attempt to # +# load the 'rpm' module. Nothing after this point depends on the # +# 'rpm' module. # +#################################################################### + +# The 'set rpm-suggestion' prefix command. +class rpm_suggestion_set_prefix(gdb.Command): + """Prefix command for 'set' rpm-suggestion related commands.""" + + def __init__(self): + super().__init__("set rpm-suggestion", gdb.COMMAND_NONE, + gdb.COMPLETE_NONE, True) + +# The 'show rpm-suggestion' prefix command. +class rpm_suggestion_show_prefix(gdb.Command): + """Prefix command for 'show' rpm-suggestion related commands.""" + + def __init__(self): + super().__init__("show rpm-suggestion", gdb.COMMAND_NONE, + gdb.COMPLETE_NONE, True) + +# The 'set/show rpm-suggestion enabled' command. +class rpm_suggestion_enabled(gdb.Parameter): + """ + When 'on' GDB will search for RPMS that might provide additional + debug information, or provide missing executables or shared + libraries (when opening a core file), and will make suggestions + about what should be installed. + + When 'off' GDB will not make these suggestions. + """ + + set_doc = "Set whether to perform rpm-suggestion." + show_doc = "Show whether rpm-suggestion is enabled." + + def __init__(self): + super().__init__("rpm-suggestion enabled", gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN) + self.value = True + +# The 'set/show rpm-suggestion enabled' command. +class rpm_suggestion_build_id_mode(gdb.Parameter): + """ + When set to 'fast' (the default), GDB doesn't try to map a build-id to + an actual RPM, instead, GDB just suggests a command based on the + build-id which might install some RPMs, if there are any RPMs that + supply that build-id. However, it is equally possible that there are no + suitable RPMs, and nothing will install. This approach has almost zero + overhead. + + When set to 'slow', GDB first does the build-id to RPM check itself, and + only if something is found are RPMs installation commands suggested. + The suggested command will include the name of the RPM to install. This + approach is considerably slower as querying the RPM database for the RPM + that supplies a specific file is slow. + """ + + set_doc = "Set how build-id based rpm suggestions should be performed." + show_doc = "Show how build-id based rpm suggestions shoud be performed." + + def __init__(self): + super().__init__("rpm-suggestion build-id-mode", + gdb.COMMAND_NONE, gdb.PARAM_ENUM, ["fast", "slow"]) + self.value = "fast" + + def fast_mode(self): + return self.value == "fast" + +# The 'info rpm-suggestions' command. +class rpm_suggestion_info(gdb.Command): + """Relist RPM suggestions. + + Relist any RPM installation suggestions that have been made + since the executable was last changed.""" + def __init__(self): + super().__init__("info rpm-suggestions", gdb.COMMAND_NONE, gdb.COMPLETE_NONE) + + def invoke(self, args, from_tty): + if args != "": + raise gdb.GdbError("unexpected arguments: %s" % args) + + info_rpm_suggestions () + + +# Create the 'set/show rpm-suggestion' commands. +rpm_suggestion_set_prefix() +rpm_suggestion_show_prefix() +param_rpm_suggestion_enabled = rpm_suggestion_enabled() +param_rpm_suggestion_build_id_mode = rpm_suggestion_build_id_mode() + +# Create the 'info rpm-suggestions' commands. +rpm_suggestion_info() diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c new file mode 100644 --- /dev/null +++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c @@ -0,0 +1,21 @@ +/* Copyright 2010 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +void +lib (void) +{ +} diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c new file mode 100644 --- /dev/null +++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c @@ -0,0 +1,25 @@ +/* Copyright 2010 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +extern void lib (void); + +int +main (void) +{ + lib (); + return 0; +} diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp new file mode 100644 --- /dev/null +++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp @@ -0,0 +1,104 @@ +# Copyright 2016 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 . + +require allow_shlib_tests + +set testfile "gcore-buildid-exec-but-not-solib" +set srcmainfile ${testfile}-main.c +set srclibfile ${testfile}-lib.c +set libfile [standard_output_file ${testfile}-lib.so] +set objfile [standard_output_file ${testfile}-main.o] +set executable ${testfile}-main +set binfile [standard_output_file ${executable}] +set gcorefile [standard_output_file ${executable}.gcore] +set outdir [file dirname $binfile] + +if { [gdb_compile_shlib ${srcdir}/${subdir}/${srclibfile} ${libfile} "debug additional_flags=-Wl,--build-id"] != "" + || [gdb_compile ${srcdir}/${subdir}/${srcmainfile} ${objfile} object {debug}] != "" } { + unsupported "-Wl,--build-id compilation failed" + return -1 +} +set opts [list debug shlib=${libfile} "additional_flags=-Wl,--build-id"] +if { [gdb_compile ${objfile} ${binfile} executable $opts] != "" } { + unsupported "-Wl,--build-id compilation failed" + return -1 +} + +clean_restart $executable +gdb_load_shlib $libfile + +# Does this gdb support gcore? +set test "help gcore" +gdb_test_multiple $test $test { + -re "Undefined command: .gcore.*\r\n$gdb_prompt $" { + # gcore command not supported -- nothing to test here. + unsupported "gdb does not support gcore on this target" + return -1; + } + -re "Save a core file .*\r\n$gdb_prompt $" { + pass $test + } +} + +if { ![runto lib] } then { + return -1 +} + +set escapedfilename [string_to_regexp ${gcorefile}] + +set test "save a corefile" +gdb_test_multiple "gcore ${gcorefile}" $test { + -re "Saved corefile ${escapedfilename}\r\n$gdb_prompt $" { + pass $test + } + -re "Can't create a corefile\r\n$gdb_prompt $" { + unsupported $test + return -1 + } +} + +# Now restart gdb and load the corefile. + +clean_restart $executable +gdb_load_shlib $libfile + +set buildid [build_id_debug_filename_get $libfile] + +regsub {\.debug$} $buildid {} buildid + +set debugdir [standard_output_file ${testfile}-debugdir] +file delete -force -- $debugdir + +file mkdir $debugdir/[file dirname $libfile] +file copy $libfile $debugdir/${libfile} + +file mkdir $debugdir/[file dirname $buildid] +file copy $libfile $debugdir/${buildid} + +remote_exec build "ln -s /lib ${debugdir}/" +remote_exec build "ln -s /lib64 ${debugdir}/" +# /usr is not needed, all the libs are in /lib64: libm.so.6 libc.so.6 ld-linux-x86-64.so.2 + +gdb_test_no_output "set solib-absolute-prefix $debugdir" \ + "set solib-absolute-prefix" + +gdb_test_no_output "set debug-file-directory $debugdir" "set debug-file-directory" + +gdb_test "core ${gcorefile}" "Core was generated by .*" "re-load generated corefile" + +gdb_test "frame" "#0 \[^\r\n\]* lib .*" "library got loaded" + +gdb_test "bt" +gdb_test "info shared" diff --git a/gdb/testsuite/gdb.base/gdbinit-history.exp b/gdb/testsuite/gdb.base/gdbinit-history.exp --- a/gdb/testsuite/gdb.base/gdbinit-history.exp +++ b/gdb/testsuite/gdb.base/gdbinit-history.exp @@ -179,7 +179,8 @@ proc test_empty_history_filename { } { global env global gdb_prompt - set common_history [list "set height 0" "set width 0"] + set common_history [list "set height 0" "set width 0" \ + "set rpm-suggestion enabled off"] set test_dir [standard_output_file history_test] remote_exec host "mkdir -p $test_dir" diff --git a/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp b/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp new file mode 100644 --- /dev/null +++ b/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp @@ -0,0 +1,60 @@ +# Copyright (C) 2014 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 . + +# Create a core file, then hide the executable. Restart GDB and load +# the core file. Check GDB gives a message suggesting a 'dnf' command +# to try and install the executable based on its build-id. + +standard_testfile "normal.c" + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +# Get the build-id of the file. +set build_id_debug_file [build_id_debug_filename_get $binfile] +regsub -all ".debug$" $build_id_debug_file "" build_id_without_debug + +# Run to main. +if { ![runto_main] } { + return -1 +} + +# We first need to generate a corefile. +set corefilename "[standard_output_file gcore.test]" +if { ![gdb_gcore_cmd "$corefilename" "save corefile"] } { + untested "could not generate a corefile" + return -1 +} + +# Move the binfile to a temporary name. +remote_exec build "mv $binfile ${binfile}.old" + +# Reinitialize GDB and see if we get a dnf suggestion. +clean_restart + +gdb_test "set rpm-suggestion enabled on" "" \ + "turn on rpm-suggestion feature" + +# GDB only makes build-id based RPM suggestions if /usr/lib is in +# the debug-file-directory list, the reason being that system RPMs +# will always install under this location. If GDB is not looking +# here then there's no point making suggestions. +gdb_test "set debug-file-directory /usr/lib/" "" \ + "set debug-file-directory" + +gdb_test "core-file [standard_output_file gcore.test]" \ + "Missing file\\(s\\), try: dnf --enablerepo='\\*debug\\*' install [string_to_regexp /usr/lib/$build_id_without_debug] [string_to_regexp /usr/lib/debug/$build_id_debug_file]" \ + "test first yum/dnf warning" 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 @@ -19,6 +19,13 @@ from enum import Enum import gdb from gdb.missing_debug import MissingDebugHandler +# 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 = [] @@ -118,4 +125,7 @@ def register(name, locus=None): rhandler = exception_handler() handler_obj = handler() +# Discard the rpm-suggestion handler. +gdb.missing_file_handlers = [] + print("Success") diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.py b/gdb/testsuite/gdb.python/py-missing-objfile.py --- a/gdb/testsuite/gdb.python/py-missing-objfile.py +++ b/gdb/testsuite/gdb.python/py-missing-objfile.py @@ -164,4 +164,7 @@ def register(name, locus=None): rhandler = exception_handler() handler_obj = handler() +# Discard the rpm-suggestion handler. +gdb.missing_file_handlers = [] + print("Success") diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -255,7 +255,8 @@ if ![info exists INTERNAL_GDBFLAGS] { "-nx" \ "-q" \ {-iex "set height 0"} \ - {-iex "set width 0"}]] + {-iex "set width 0"} \ + {-iex "set rpm-suggestion enabled off"}]] # If DEBUGINFOD_URLS is set, gdb will try to download sources and # debug info for f.i. system libraries. Prevent this. @@ -2610,6 +2611,18 @@ proc default_gdb_start { } { } } + # Turn off the missing debug info messages as the testsuite does + # not expect them. + send_gdb "set rpm-suggestion enabled off\n" + gdb_expect 10 { + -re "$gdb_prompt $" { + verbose "Disabled the missing debug info messages." 2 + } + timeout { + warning "Could not disable the missing debug info messages." + } + } + gdb_debug_init return 0 } diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp --- a/gdb/testsuite/lib/mi-support.exp +++ b/gdb/testsuite/lib/mi-support.exp @@ -321,6 +321,17 @@ proc default_mi_gdb_start { { flags {} } } { warning "Couldn't set the width to 0." } } + # Turn off the missing debug info messages as the testsuite does + # not expect them. + send_gdb "190-gdb-set rpm-suggestion enabled off\n" + gdb_expect 10 { + -re ".*190-gdb-set rpm-suggestion enabled off\r\n190\\\^done\r\n$mi_gdb_prompt$" { + verbose "Disabled the missing debug info messages." 2 + } + timeout { + warning "Could not disable the missing debug info messages." + } + } if { $separate_inferior_pty } { mi_create_inferior_pty