From bd6b66da870a0ce0f34cfe384b2eec4996f14a49 Mon Sep 17 00:00:00 2001 From: Kevin Cox Date: Tue, 17 May 2016 19:20:48 -0400 Subject: [PATCH] Make using separate debugging info easier. The previous iteration of debug info required that all of the packages were "installed". This is suboptimal because "installing" all of the programs you want to debug as well as the libraries that they use. This method enables debug symbols for all loaded files as long as those symbols are present in the nix store, as well as a command to download those symbols from binary caches for all loaded object files. This is implemented by adding a new section to the executable witch contains the path to the debug symbols. An extension that is auto-loaded by GDB then uses this information to automatically load these symbols whenever the object file is loaded. --- .../git-and-tools/git/default.nix | 1 + .../setup-hooks/separate-debug-info.sh | 29 +++++--- pkgs/development/libraries/glibc/common.nix | 1 + pkgs/development/tools/misc/gdb/default.nix | 8 ++- pkgs/development/tools/misc/gdb/nix.py | 67 +++++++++++++++++++ pkgs/tools/compression/xz/default.nix | 2 + 6 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 pkgs/development/tools/misc/gdb/nix.py diff --git a/pkgs/applications/version-management/git-and-tools/git/default.nix b/pkgs/applications/version-management/git-and-tools/git/default.nix index 84af1a2fcbb23..aa0cb613bccb2 100644 --- a/pkgs/applications/version-management/git-and-tools/git/default.nix +++ b/pkgs/applications/version-management/git-and-tools/git/default.nix @@ -153,6 +153,7 @@ stdenv.mkDerivation { ''); enableParallelBuilding = true; + separateDebugInfo = true; meta = { homepage = http://git-scm.com/; diff --git a/pkgs/build-support/setup-hooks/separate-debug-info.sh b/pkgs/build-support/setup-hooks/separate-debug-info.sh index 518be96473342..d3a1ca9ed2854 100644 --- a/pkgs/build-support/setup-hooks/separate-debug-info.sh +++ b/pkgs/build-support/setup-hooks/separate-debug-info.sh @@ -1,19 +1,19 @@ export NIX_SET_BUILD_ID=1 -export NIX_LDFLAGS+=" --compress-debug-sections=zlib" -export NIX_CFLAGS_COMPILE+=" -ggdb -Wa,--compress-debug-sections" +export NIX_CFLAGS_COMPILE+=' -ggdb' dontStrip=1 fixupOutputHooks+=(_separateDebugInfo) _separateDebugInfo() { - local dst="${debug:-$out}" - if [ "$prefix" = "$dst" ]; then return; fi + local debugout="${debug:-$out}" + if [ "$prefix" = "$debugout" ]; then return; fi - dst="$dst/lib/debug/.build-id" + local hashandname="$(basename "$debugout")" + local debugpath="$(dirname "$debugout")/${hashandname:0:2} ${hashandname:2}" # Find executables and dynamic libraries. local i magic - while IFS= read -r -d $'\0' i; do + while read -r -d $'\0' i; do if ! isELF "$i"; then continue; fi # Extract the Build ID. FIXME: there's probably a cleaner way. @@ -25,11 +25,18 @@ _separateDebugInfo() { # Extract the debug info. header "separating debug info from $i (build ID $id)" - mkdir -p "$dst/${id:0:2}" - objcopy --only-keep-debug "$i" "$dst/${id:0:2}/${id:2}.debug" - strip --strip-debug "$i" - + local relpath="lib/debug/.build-id/${id:0:2}/${id:2}.debug" + local debugfile="$debugout/$relpath" + mkdir -p $(dirname "$debugfile") + objcopy --only-keep-debug --compress-debug-sections "$i" "$debugfile" # Also a create a symlink .debug. - ln -sfn ".build-id/${id:0:2}/${id:2}.debug" "$dst/../$(basename "$i")" + ln -sfn ".build-id/${id:0:2}/${id:2}.debug" "$debugout/lib/debug/$(basename "$i").debug" + + if ! objdump -sj .nix_debug "$i" &> /dev/null; then + # The space is to prevent a reference. + objcopy --add-section=.nix_debug=<(echo "$debugpath/$relpath") "$i" + fi + strip $commonStripFlags "$i" + done < <(find "$prefix" -type f -print0) } diff --git a/pkgs/development/libraries/glibc/common.nix b/pkgs/development/libraries/glibc/common.nix index a189edb9832df..a299d5e09e516 100644 --- a/pkgs/development/libraries/glibc/common.nix +++ b/pkgs/development/libraries/glibc/common.nix @@ -26,6 +26,7 @@ stdenv.mkDerivation ({ inherit (stdenv) is64bit; enableParallelBuilding = true; + separateDebugInfo = true; patches = [ /* Have rpcgen(1) look for cpp(1) in $PATH. */ diff --git a/pkgs/development/tools/misc/gdb/default.nix b/pkgs/development/tools/misc/gdb/default.nix index 472bb4de58b9e..c0b6486767232 100644 --- a/pkgs/development/tools/misc/gdb/default.nix +++ b/pkgs/development/tools/misc/gdb/default.nix @@ -1,5 +1,5 @@ { fetchurl, stdenv, ncurses, readline, gmp, mpfr, expat, texinfo, zlib -, dejagnu, perl, pkgconfig +, dejagnu, perl, pkgconfig, binutils, nix , python ? null , guile ? null , target ? null @@ -21,6 +21,8 @@ let stdenv.system == "i686-gnu" || (stdenv ? cross && stdenv.cross.config == "i586-pc-gnu"); + nixExtension = ./nix.py; + in assert isGNU -> mig != null && hurd != null; @@ -42,6 +44,7 @@ stdenv.mkDerivation rec { ++ stdenv.lib.optional doCheck dejagnu; enableParallelBuilding = true; + separateDebugInfo = true; configureFlags = with stdenv.lib; [ "--with-gmp=${gmp}" "--with-mpfr=${mpfr}" "--with-system-readline" @@ -64,6 +67,9 @@ stdenv.mkDerivation rec { postInstall = '' # Remove Info files already provided by Binutils and other packages. + sed '-es|"objcopy"|"${binutils}/bin/objcopy"|' \ + '-es|"nix-store"|"${nix.out}/bin/nix-store"|' \ + '${nixExtension}' > "$out/share/gdb/python/gdb/command/nix.py" rm -v $out/share/info/bfd.info ''; diff --git a/pkgs/development/tools/misc/gdb/nix.py b/pkgs/development/tools/misc/gdb/nix.py new file mode 100644 index 0000000000000..eb8530c03b3f3 --- /dev/null +++ b/pkgs/development/tools/misc/gdb/nix.py @@ -0,0 +1,67 @@ +import os +import subprocess + +import gdb + +_objects_without_symbols_loaded = set() + +def _new_objfile(event): + _load(event.new_objfile) + +gdb.events.new_objfile.connect(_new_objfile) + +def _clear_objfiles(event): + _objects_without_symbols_loaded.clear() + +gdb.events.clear_objfiles.connect(_clear_objfiles) + +def _get_debug_file_path(obj): + try: + out = subprocess.check_output([ + "objcopy", + "--dump-section=.nix_debug=/dev/stdout", + obj.filename, + "/dev/null", + ], stderr=subprocess.STDOUT) + if b"File in wrong format" in out: + # objdump isn't exiting with failure in this case. + return None + return str(out).replace(" ", "", 1).rstrip() + except: + return None + +def _load(obj): + debugfile = _get_debug_file_path(obj) + if not debugfile: + return + + try: + obj.add_separate_debug_file(debugfile) + gdb.write("Loaded symbols for {}.\n".format(obj.username)) + _objects_without_symbols_loaded.discard(obj) + except Exception as e: + _objects_without_symbols_loaded.add(obj) + gdb.write("Error loading debug symbols: {}\n".format(e)) + gdb.write("From: {}\n".format(debugfile, e)) + gdb.write("For: {}\n".format(obj.username)) + if not os.path.exists(debugfile): + gdb.write("The debug file doesn't exists.\n") + gdb.write("You can download missing debug files with 'nix-download-debug'.\n") + +def _download(objs): + debugfiles = [_get_debug_file_path(obj) for obj in objs] + subprocess.check_call(["nix-store", "-rk"] + debugfiles) + +class CommandNixDownloadDebug(gdb.Command): + def __init__(self): + super(CommandNixDownloadDebug, self).__init__("nix-download-debug", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + assert not arg + try: + _download(_objects_without_symbols_loaded) + finally: + for obj in list(_objects_without_symbols_loaded): + _load(obj) + +CommandNixDownloadDebug() diff --git a/pkgs/tools/compression/xz/default.nix b/pkgs/tools/compression/xz/default.nix index 5d6a8634b1baa..d3d54c76d2a6a 100644 --- a/pkgs/tools/compression/xz/default.nix +++ b/pkgs/tools/compression/xz/default.nix @@ -10,6 +10,8 @@ stdenv.mkDerivation rec { outputs = [ "dev" "out" "bin" "man" "doc" ]; + enableParallelBuilding = true; + separateDebugInfo = true; doCheck = true; # In stdenv-linux, prevent a dependency on bootstrap-tools.