From patchwork Wed Oct 18 10:53:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 78082 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CBD023875DC0 for ; Wed, 18 Oct 2023 10:53:59 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 96A6D386180D for ; Wed, 18 Oct 2023 10:53:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 96A6D386180D Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 96A6D386180D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626413; cv=none; b=pVTfdNXYVnpcZqaDjEurRfEHikZpZP7MhSjSw1XSz2f/tUWXSiCG/uciFrEOg7G0tluExe11n53ADPOwbuN9pjAXGTakJ1UbI15zPt8h+ojSz2ZbuBdW7/GrCfL679ZjLC+G6/avpeYCQpNA8yT+bDpqaYJSvqMYpRK5UojqB34= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626413; c=relaxed/simple; bh=7POUHQMn6LCt8iRQ6wnHGq5V4K1dVWhR8fy8LBBQ+Kg=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=x3Arob3uGhjQlzxhqWkB8VlqPM+mfiDO/6FrI3mP+dtfkMQNm4A+ccEnhzSmZdJ9p1TcUcDS3ODqASKO9Ux8TCSeL7hwbHSHpZTHfQFzg8yjkPy0DuunMXiVqFM6ZceyJ+432XqfX2kOSUmeofYPsDlNTLgqx4W0z3tOMl66tLc= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697626412; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=oMGRMu0+4zG2p6Xk98LrIZCj7iNkPX0RUcdH9DOXMzM=; b=d+gnCCIb73Wbk3vShczZQovPp/jYtErhOzMZPiXXqBf162tH+hD8HIOgpvZyDX5kBHE77h ldLWDy9fId3RsOWMUg4x8Ce4Umkh79kGr2rzGNELGGnTq7RCetrJuFQaJbJ9BEYVtxmSen 14avwryQSrMDvsLOl9023Q9aQP3rAzQ= Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-441-tNgcRaDAMzKCMWrj3rEtmg-1; Wed, 18 Oct 2023 06:53:31 -0400 X-MC-Unique: tNgcRaDAMzKCMWrj3rEtmg-1 Received: by mail-qv1-f70.google.com with SMTP id 6a1803df08f44-66d75988385so13422256d6.2 for ; Wed, 18 Oct 2023 03:53:31 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697626410; x=1698231210; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=oMGRMu0+4zG2p6Xk98LrIZCj7iNkPX0RUcdH9DOXMzM=; b=GGJvZjmsfTlWIUZLtoAyhtl2rlWIgxN1cEqilnr0bT3iuCT0BVK6x282JnyxkAHZAp WvyCPITYIMa1KWUl5Y0pnSNTqWHbyJSv8qCK6B9kmHhIMSmxWqKvNJSBWfHrdWDo6INF 21R3XRVgu6zil9/yyDt4igmx46ZrhBQ/7ZQoTSE2ef4lpnuDjSCuXGdbSDk2QGFoHSCr TWvQ5Nj7Q0LtYMTRClJ1BJaP1oPFwHzV75lGdIIyadhj6zgBbm4vXjrNfM+4J3VX/bPU K4ryJXscFTuWtw6RuOD5tCg9lW7KG9N4R7YBJNiVy3sDw+YZFK5xEX+3lae147vXPaub V42Q== X-Gm-Message-State: AOJu0Yy0C0LneH4SMVz3CE3lXS3lLSdAlMVragYkRmMxAhFF3RMco0Lw dXCUFB6N+HGm5xf2szR4N5BBkePuTqt5UpRWcLP0gn+/upW++fZcsXCnkNLmPd2StEbh3+8cD26 kj8Tk34je0Fvyudj/tZJY41pOkGcf/f/UguLHEHXTfdZBSt2H1Vdtfiy9fSGYZgL0PCxUNquC0m Hy6UBlow== X-Received: by 2002:a05:6214:e4f:b0:668:e6f7:3d48 with SMTP id o15-20020a0562140e4f00b00668e6f73d48mr5577266qvc.19.1697626410504; Wed, 18 Oct 2023 03:53:30 -0700 (PDT) X-Google-Smtp-Source: AGHT+IH2FTLzs2PPeHp4BOrgOp1jw36IoxBof2Klr/iHNcmWFMQdoU9Oce2suM5UgWcgRn7sW//04w== X-Received: by 2002:a05:6214:e4f:b0:668:e6f7:3d48 with SMTP id o15-20020a0562140e4f00b00668e6f73d48mr5577245qvc.19.1697626410140; Wed, 18 Oct 2023 03:53:30 -0700 (PDT) Received: from localhost ([31.111.84.209]) by smtp.gmail.com with ESMTPSA id o11-20020a056214108b00b0066d15e2b73fsm1243719qvr.145.2023.10.18.03.53.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 03:53:29 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 1/5] gdb/coffread: bring separate debug file logic into line with elfread.c Date: Wed, 18 Oct 2023 11:53:19 +0100 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org In this commit: commit 8a92335bfca80cc9b4cd217505ea0dcbfdefbf07 Date: Fri Feb 1 19:39:04 2013 +0000 the logic for when we try to load a separate debug file in elfread.c was extended. The new code checks that the objfile doesn't already have a separate debug objfile linked to it, and that the objfile isn't itself a separate debug objfile for some other objfile. The coffread code wasn't extended at the same time. I don't know if it's possible for the coffread code to get into the same state where these checks are needed, but I don't see why having these checks would be a problem. In a later commit I plan to merge this part of the elfread and coffread code, so bringing these two pieces of code into line first makes that job easier. I haven't tested this, I don't have a coff target to hand. --- gdb/coffread.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gdb/coffread.c b/gdb/coffread.c index 4a6a83b15ad..e1415d6b258 100644 --- a/gdb/coffread.c +++ b/gdb/coffread.c @@ -725,7 +725,9 @@ coff_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags) } /* Try to add separate debug file if no symbols table found. */ - if (!objfile->has_partial_symbols ()) + else if (!objfile->has_partial_symbols () + && objfile->separate_debug_objfile == NULL + && objfile->separate_debug_objfile_backlink == NULL) { deferred_warnings warnings; std::string debugfile From patchwork Wed Oct 18 10:53:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 78083 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 75CD8387545E for ; Wed, 18 Oct 2023 10:54:00 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id AC95F3870C35 for ; Wed, 18 Oct 2023 10:53:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org AC95F3870C35 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org AC95F3870C35 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626426; cv=none; b=GtwMQxQ3PQdUR07OqRbeCBxvTBzNS0TWVWzwHrgZ5MYw/w33fWK73DjzE3tGXQIaZyY1mIrG/LATPUN+em8ByGckntlMO+EnydNxGl861gc4Gifv9mUe3Nuxd9JB7XDNN6VqAkWe3d0zWI4gXDlSy5Yp0fhfTbjRCuW+c6Wo8Pw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626426; c=relaxed/simple; bh=7THTah0Dz9/lxzU4Pop07MkMjWt55gHH2yboe6SDs2g=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=KL8gwZbDgOKFK/dTWcEPIOzOWP13tbjuzSHjRQEDSWMAtaUWxsir+oiJp5+Z/Lq6KQaPf7LReDq3pNAu15pYrUuOgOatz/U0JgQMu0XyVXIKFrdkYv8ar0bsp6TLqggFmDD8LPZlg2ghHBy6KYoNSZJzqtvkt4GQzvzP4PsmY4w= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697626424; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=OomHNEc3YdhSiuttr6A9SCDWgkRJv4MmHFSzvqAWfQQ=; b=PlNZXvncH1yjOmniXFkJPYbyDcVSeMFZnu/Kq/sg5vOgFd7sdCkmLOJh1MMHSwBCT2aYIB MPx9JrDBRSDMONFav70Q2AN8KpNOdpqhEEhqsBST2a/0l2/MkuK6yUxwCvD0bOk42vGE3E zbMWv6FigkZFJjekb/yG8ni2QYG2WAM= Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-108-QTPynY7-M0CyMR6CMpYaig-1; Wed, 18 Oct 2023 06:53:33 -0400 X-MC-Unique: QTPynY7-M0CyMR6CMpYaig-1 Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-417fd6a8575so75421941cf.2 for ; Wed, 18 Oct 2023 03:53:33 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697626412; x=1698231212; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NzyeQdCxpajvfNa7brjBPNkgbKMePeAolljkTq2yEBI=; b=E38Ju3lpxfvJZo2mf1OjhkWlKhV3INB8KJgFpqXKkq8+sshgRLYV0Qn90aSNlaAdda yDWVCev96KxbbI+DSsiBRqs06ePdxxJ1d1uHEej0DRiT8w7ftlIOhG9bDvqcBLR1gkio TkkBUHougNb3W6BylW7mbJI3TjdXMVZl5S3hiqg3+Rbl3vdvWBhPw/aFWkQmloWJYQnx /j8Pj4DlBGuym3Fw9SvAvQSkRftppyLc15QlCtJDHntklQH/RMzjZ3Bu9UcJu9Epc7Hh yGNsuS7XJ69+VX+obUQW545bzB88URqPWylhT5ylZViVcIFCkUPoRpIA17rvXq1QMeSg If6Q== X-Gm-Message-State: AOJu0YxE4Q8cUXYnC/lKP2I+b3cK73yrJkX2iZF+5l/dZy1TeaNo+ftN IwSHrA5dhH8/tCbpwwBvjvbJMqkQOfIT+bE6n5a4e/FENXz1bitqvng7E4MVDrDE0b293pSvDCj 14o/5VcxIYnvjosEtiohn2UTdi87/kMTN663Z3nQpIJPLKY4X7A1/3LA6TCt88I4dY6MVOdeKCb B33o5Ygg== X-Received: by 2002:ad4:5e8e:0:b0:66d:9d6:633a with SMTP id jl14-20020ad45e8e000000b0066d09d6633amr5323473qvb.33.1697626412569; Wed, 18 Oct 2023 03:53:32 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFbKVuIwTmQW7h7X2WIxvEIFzikrRjN6KthIYl7pOtCp4kn1/5Pvk2gHAYcrijoXPjm6gtt0g== X-Received: by 2002:ad4:5e8e:0:b0:66d:9d6:633a with SMTP id jl14-20020ad45e8e000000b0066d09d6633amr5323457qvb.33.1697626412129; Wed, 18 Oct 2023 03:53:32 -0700 (PDT) Received: from localhost ([31.111.84.209]) by smtp.gmail.com with ESMTPSA id d14-20020a0cfe8e000000b006616fbcc077sm1259377qvs.129.2023.10.18.03.53.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 03:53:31 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 2/5] gdb: merge debug symbol file lookup code from coffread & elfread paths Date: Wed, 18 Oct 2023 11:53:20 +0100 Message-Id: <07d050dec424c75095135c8a4836f24e3d713a32.1697626088.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org This commit merges the code that looks for and loads the separate debug symbol files from coffread.c and elfread.c. The factored out code is moved into a new objfile::find_and_add_separate_symbol_file() method. For the elfread.c path there should be no user visible changes after this commit. For the coffread.c path GDB will now attempt to perform a debuginfod lookup for the missing debug information, assuming that GDB can find a build-id in the COFF file. I don't know if COFF files can include a build-id, but I the existing coffread.c code already includes a call to find_separate_debug_file_by_build-id, so I know that it is at least OK for GDB to ask a COFF file for a build-id. If the COFF file doesn't include a build-id then the debuginfod lookup code will not trigger and the new code is harmless. If the COFF file does include a build-id, then we're going to end up asking debuginfod for the debug file. As build-ids should be unique, this should be harmless, even if debuginfod doesn't contain any suitable debug data, it just costs us one debuginfod lookup, so I'm not too worried about this for now. I don't have access to a COFF target right now, so beyond compiling it, the coffread.c changes are completely untested. --- gdb/coffread.c | 24 ++--------------- gdb/elfread.c | 57 +++------------------------------------ gdb/objfiles.h | 10 +++++++ gdb/symfile-debug.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 76 deletions(-) diff --git a/gdb/coffread.c b/gdb/coffread.c index e1415d6b258..5898b3a8e08 100644 --- a/gdb/coffread.c +++ b/gdb/coffread.c @@ -40,8 +40,6 @@ #include "coff-pe-read.h" -#include "build-id.h" - /* The objfile we are currently reading. */ static struct objfile *coffread_objfile; @@ -729,26 +727,8 @@ coff_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags) && objfile->separate_debug_objfile == NULL && objfile->separate_debug_objfile_backlink == NULL) { - deferred_warnings warnings; - std::string debugfile - = find_separate_debug_file_by_buildid (objfile, &warnings); - - if (debugfile.empty ()) - debugfile - = find_separate_debug_file_by_debuglink (objfile, &warnings); - - if (!debugfile.empty ()) - { - gdb_bfd_ref_ptr debug_bfd (symfile_bfd_open (debugfile.c_str ())); - - symbol_file_add_separate (debug_bfd, debugfile.c_str (), - symfile_flags, objfile); - } - /* If all the methods to collect the debuginfo failed, print any - warnings that were collected, this is a no-op if there are no - warnings. */ - if (debugfile.empty ()) - warnings.emit (); + if (objfile->find_and_add_separate_symbol_file (symfile_flags)) + gdb_assert (objfile->separate_debug_objfile != nullptr); } } diff --git a/gdb/elfread.c b/gdb/elfread.c index 7900dfbc388..86e7f61586e 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -41,14 +41,12 @@ #include "regcache.h" #include "bcache.h" #include "gdb_bfd.h" -#include "build-id.h" #include "location.h" #include "auxv.h" #include "mdebugread.h" #include "ctfread.h" #include "gdbsupport/gdb_string_view.h" #include "gdbsupport/scoped_fd.h" -#include "debuginfod-support.h" #include "dwarf2/public.h" #include "cli/cli-cmds.h" @@ -1218,59 +1216,10 @@ elf_symfile_read_dwarf2 (struct objfile *objfile, && objfile->separate_debug_objfile == NULL && objfile->separate_debug_objfile_backlink == NULL) { - deferred_warnings warnings; - - std::string debugfile - = find_separate_debug_file_by_buildid (objfile, &warnings); - - if (debugfile.empty ()) - debugfile = find_separate_debug_file_by_debuglink (objfile, &warnings); - - if (!debugfile.empty ()) - { - gdb_bfd_ref_ptr debug_bfd - (symfile_bfd_open_no_error (debugfile.c_str ())); - - if (debug_bfd != nullptr) - symbol_file_add_separate (debug_bfd, debugfile.c_str (), - symfile_flags, objfile); - } + if (objfile->find_and_add_separate_symbol_file (symfile_flags)) + gdb_assert (objfile->separate_debug_objfile != nullptr); else - { - has_dwarf2 = false; - const struct bfd_build_id *build_id - = build_id_bfd_get (objfile->obfd.get ()); - const char *filename = bfd_get_filename (objfile->obfd.get ()); - - if (build_id != nullptr) - { - gdb::unique_xmalloc_ptr symfile_path; - scoped_fd fd (debuginfod_debuginfo_query (build_id->data, - build_id->size, - filename, - &symfile_path)); - - if (fd.get () >= 0) - { - /* File successfully retrieved from server. */ - gdb_bfd_ref_ptr debug_bfd - (symfile_bfd_open_no_error (symfile_path.get ())); - - if (debug_bfd != nullptr - && build_id_verify (debug_bfd.get (), build_id->size, - build_id->data)) - { - symbol_file_add_separate (debug_bfd, symfile_path.get (), - symfile_flags, objfile); - has_dwarf2 = true; - } - } - } - } - /* If all the methods to collect the debuginfo failed, print the - warnings, this is a no-op if there are no warnings. */ - if (debugfile.empty () && !has_dwarf2) - warnings.emit (); + has_dwarf2 = false; } return has_dwarf2; diff --git a/gdb/objfiles.h b/gdb/objfiles.h index 4b8aa9bfcec..ec9d354e4a7 100644 --- a/gdb/objfiles.h +++ b/gdb/objfiles.h @@ -513,6 +513,16 @@ struct objfile bool has_partial_symbols (); + /* Look for a separate debug symbol file for this objfile, make use of + build-id, debug-link, and debuginfod as necessary. If a suitable + separate debug symbol file is found then it is loaded using a call to + symbol_file_add_separate (SYMFILE_FLAGS is passed through unmodified + to this call) and this function returns true. If no suitable separate + debug symbol file is found and loaded then this function returns + false. */ + + bool find_and_add_separate_symbol_file (symfile_add_flags symfile_flags); + /* Return true if this objfile has any unexpanded symbols. A return value of false indicates either, that this objfile has all its symbols fully expanded (i.e. fully read in), or that this objfile has diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c index 850da4147a3..961ae2327f7 100644 --- a/gdb/symfile-debug.c +++ b/gdb/symfile-debug.c @@ -35,6 +35,8 @@ #include "block.h" #include "filenames.h" #include "cli/cli-style.h" +#include "build-id.h" +#include "debuginfod-support.h" /* We need to save a pointer to the real symbol functions. Plus, the debug versions are malloc'd because we have to NULL out the @@ -558,6 +560,70 @@ objfile::require_partial_symbols (bool verbose) } } +/* See objfiles.h. */ + +bool +objfile::find_and_add_separate_symbol_file (symfile_add_flags symfile_flags) +{ + bool has_dwarf2 = true; + + deferred_warnings warnings; + + std::string debugfile + = find_separate_debug_file_by_buildid (this, &warnings); + + if (debugfile.empty ()) + debugfile = find_separate_debug_file_by_debuglink (this, &warnings); + + if (!debugfile.empty ()) + { + gdb_bfd_ref_ptr debug_bfd + (symfile_bfd_open_no_error (debugfile.c_str ())); + + if (debug_bfd != nullptr) + symbol_file_add_separate (debug_bfd, debugfile.c_str (), + symfile_flags, this); + } + else + { + has_dwarf2 = false; + const struct bfd_build_id *build_id + = build_id_bfd_get (this->obfd.get ()); + const char *filename = bfd_get_filename (this->obfd.get ()); + + if (build_id != nullptr) + { + gdb::unique_xmalloc_ptr symfile_path; + scoped_fd fd (debuginfod_debuginfo_query (build_id->data, + build_id->size, + filename, + &symfile_path)); + + if (fd.get () >= 0) + { + /* File successfully retrieved from server. */ + gdb_bfd_ref_ptr debug_bfd + (symfile_bfd_open_no_error (symfile_path.get ())); + + if (debug_bfd != nullptr + && build_id_verify (debug_bfd.get (), build_id->size, + build_id->data)) + { + symbol_file_add_separate (debug_bfd, symfile_path.get (), + symfile_flags, this); + has_dwarf2 = true; + } + } + } + } + /* If all the methods to collect the debuginfo failed, print the + warnings, this is a no-op if there are no warnings. */ + if (debugfile.empty () && !has_dwarf2) + warnings.emit (); + + return has_dwarf2; +} + /* Debugging version of struct sym_probe_fns. */ From patchwork Wed Oct 18 10:53:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 78084 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4BA3D386D618 for ; Wed, 18 Oct 2023 10:54:16 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 76CD13865C2A for ; Wed, 18 Oct 2023 10:53:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 76CD13865C2A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 76CD13865C2A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626427; cv=none; b=Kr7XWtFwcL52fHtQEqeRs3ybhA2gqrLcNfyJNp3EvOho/O5JCrl/txmI/hm7wusxuAqJLEaJr6o21JK+tN8qJ7lEgWG2eqeLnLaXEXA3h77IuAaZQPVMmgiVYhWKZGh6fkmM/DeXqDe2Q609odm2fjt/WdI/I0dqE0nHZIY1FWg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626427; c=relaxed/simple; bh=AsPNaC7DR+vKmGo7w4l2kzK/FUB0k68zciQvo7BqqYo=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=R6jMbqCqc9SNC7yvsdXt+pQyk9lXSSbPZW7YtWA2Sh/fDPqyhXDLjpWu0mpHunDlH8Fi/37Hc9/FzvUlkzKP6Z5VS34YTtHK96xrAb0EmqRCOklJH4O3DX9wcKLxhETZuTR1xIj8P2yIkojVyyplSDzeVnaFEk12IaqvcCiaD0g= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697626426; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=XEbz6kK670MIaDXtMFSMzBNaLL64DTWbYGClrFH2SYQ=; b=adAUT2L+eoEtbnpzDGbn1w4Wjz6+qxntFirMIO57jMbAqRitsRnVE3rGlKNS7Dm7/5g6Qy 00fNY+BlYVrvKpxtSQRwYLhhaompFKC77HIYqziHvVt6t06Z1v2tyEKKbANMNlOBgQk976 ljgMwOH5LX5D87geSixlXxofnvTGUxw= Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-140-buRrqzB5P96DgiRUGXyj0w-1; Wed, 18 Oct 2023 06:53:35 -0400 X-MC-Unique: buRrqzB5P96DgiRUGXyj0w-1 Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-418099ca1c2so75663891cf.0 for ; Wed, 18 Oct 2023 03:53:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697626414; x=1698231214; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XEbz6kK670MIaDXtMFSMzBNaLL64DTWbYGClrFH2SYQ=; b=WMbhmU8XAQ5jekK0gVu4BkAMDagS+hHNhhoP4SrdJaSlVaAKqguY7drF2H2M6snEul g7Iu5ySwWWDo2/zdwhmskjMSiyP/sZJky9R+FZq29j0c0hihltiNIT5Z5NtxM4udL/jC GQEf1m6znq6FH050ZAuP2A9kxRZRN8Gtg11nrdqZkCs84+6yaGsAMZMCRPZahZN0plA/ B0mf2QdM9TXLiS3qdc/z/F4HS262Qwf4MWX4qbcrt3kfvVeIvqeO2S/RX7ABDdnFPtgt n4Jric4+XDMw4JkyUSl+JpO2Ar4pEJmRZRtPLh849++BE8bW4lEtQclZgnltixYPEO66 rHeQ== X-Gm-Message-State: AOJu0Ywn9UR+VkNfn0hoME43rj5NvQiFRbkIRh/lL4LmOsvCDceYJDBG BUXNgNNfJsFraS3VvNikhCRPtX4fYRQjpgf9xMhHa5hl9P1UApB3G9LeZnjd0Xz2F5b8PxuzB0W gbzco0160vBGFDqxQo5DgZht9fulpU+yFjTb+vhFNbmCmgoRn8R0Pl0WExlW3j+pbDtTJZGHkU7 +1dwlCog== X-Received: by 2002:a05:622a:282:b0:403:fcd9:963 with SMTP id z2-20020a05622a028200b00403fcd90963mr5235805qtw.67.1697626414504; Wed, 18 Oct 2023 03:53:34 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHUq53XopbBlpy6tgliv6hPAr/yc+JpWK6TgEuXIGVPF/34lMs+Kb8rkyrwjhuRZLotkGa4NA== X-Received: by 2002:a05:622a:282:b0:403:fcd9:963 with SMTP id z2-20020a05622a028200b00403fcd90963mr5235785qtw.67.1697626414151; Wed, 18 Oct 2023 03:53:34 -0700 (PDT) Received: from localhost ([31.111.84.209]) by smtp.gmail.com with ESMTPSA id r19-20020ac85e93000000b0041cbb7139a9sm482828qtx.65.2023.10.18.03.53.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 03:53:33 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 3/5] gdb: refactor objfile::find_and_add_separate_symbol_file Date: Wed, 18 Oct 2023 11:53:21 +0100 Message-Id: <108c51107d11eae41fd77d54d73613569875c4c7.1697626088.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org This is purely a refactoring commit. This commit splits objfile::find_and_add_separate_symbol_file into some separate helper functions. My hope is that the steps for looking up separate debug information are now clearer. In a later commit I'm going to extend objfile::find_and_add_separate_symbol_file, with some additional logic, so starting with a simpler function will make the following changes easier. There should be no user visible changes after this commit. --- gdb/symfile-debug.c | 127 ++++++++++++++++++++++++++++---------------- 1 file changed, 82 insertions(+), 45 deletions(-) diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c index 961ae2327f7..0a4499320c7 100644 --- a/gdb/symfile-debug.c +++ b/gdb/symfile-debug.c @@ -560,65 +560,102 @@ objfile::require_partial_symbols (bool verbose) } } +/* Call LOOKUP_FUNC to find the filename of a file containing the separate + debug information matching OBJFILE. If LOOKUP_FUNC does return a + filename then open this file and return a std::pair containing the + gdb_bfd_ref_ptr of the open file and the filename returned by + LOOKUP_FUNC, otherwise this function returns an empty pair; the first + item will be nullptr, and the second will be an empty string. + + Any warnings generated by this function, or by calling LOOKUP_FUNC are + placed into WARNINGS, these warnings are only displayed to the user if + GDB is unable to find the separate debug information via any route. */ +static std::pair +simple_find_and_open_separate_symbol_file + (struct objfile *objfile, + std::string (*lookup_func) (struct objfile *, deferred_warnings *), + deferred_warnings *warnings) +{ + std::string filename = lookup_func (objfile, warnings); + + if (!filename.empty ()) + { + gdb_bfd_ref_ptr symfile_bfd + = symfile_bfd_open_no_error (filename.c_str ()); + if (symfile_bfd != nullptr) + return { symfile_bfd, filename }; + } + + return {}; +} + +/* Lookup separate debug information for OBJFILE via debuginfod. If + successful the debug information will be have been downloaded into the + debuginfod cache and this function will return a std::pair containing a + gdb_bfd_ref_ptr of the open debug information file and the filename for + the file within the debuginfod cache. If no debug information could be + found then this function returns an empty pair; the first item will be + nullptr, and the second will be an empty string. */ + +static std::pair +debuginfod_find_and_open_separate_symbol_file (struct objfile * objfile) +{ + const struct bfd_build_id *build_id + = build_id_bfd_get (objfile->obfd.get ()); + const char *filename = bfd_get_filename (objfile->obfd.get ()); + + if (build_id != nullptr) + { + gdb::unique_xmalloc_ptr symfile_path; + scoped_fd fd (debuginfod_debuginfo_query (build_id->data, build_id->size, + filename, &symfile_path)); + + if (fd.get () >= 0) + { + /* File successfully retrieved from server. */ + gdb_bfd_ref_ptr debug_bfd + (symfile_bfd_open_no_error (symfile_path.get ())); + + if (debug_bfd != nullptr + && build_id_verify (debug_bfd.get (), + build_id->size, build_id->data)) + return { debug_bfd, std::string (symfile_path.get ()) }; + } + } + + return {}; +} + /* See objfiles.h. */ bool objfile::find_and_add_separate_symbol_file (symfile_add_flags symfile_flags) { - bool has_dwarf2 = true; - + bool has_dwarf2 = false; deferred_warnings warnings; - std::string debugfile - = find_separate_debug_file_by_buildid (this, &warnings); + std::pair result; - if (debugfile.empty ()) - debugfile = find_separate_debug_file_by_debuglink (this, &warnings); - - if (!debugfile.empty ()) - { - gdb_bfd_ref_ptr debug_bfd - (symfile_bfd_open_no_error (debugfile.c_str ())); + result = simple_find_and_open_separate_symbol_file + (this, find_separate_debug_file_by_buildid, &warnings); - if (debug_bfd != nullptr) - symbol_file_add_separate (debug_bfd, debugfile.c_str (), - symfile_flags, this); - } - else - { - has_dwarf2 = false; - const struct bfd_build_id *build_id - = build_id_bfd_get (this->obfd.get ()); - const char *filename = bfd_get_filename (this->obfd.get ()); + if (result.first == nullptr) + result = simple_find_and_open_separate_symbol_file + (this, find_separate_debug_file_by_debuglink, &warnings); - if (build_id != nullptr) - { - gdb::unique_xmalloc_ptr symfile_path; - scoped_fd fd (debuginfod_debuginfo_query (build_id->data, - build_id->size, - filename, - &symfile_path)); + if (result.first == nullptr) + result = debuginfod_find_and_open_separate_symbol_file (this); - if (fd.get () >= 0) - { - /* File successfully retrieved from server. */ - gdb_bfd_ref_ptr debug_bfd - (symfile_bfd_open_no_error (symfile_path.get ())); - - if (debug_bfd != nullptr - && build_id_verify (debug_bfd.get (), build_id->size, - build_id->data)) - { - symbol_file_add_separate (debug_bfd, symfile_path.get (), - symfile_flags, this); - has_dwarf2 = true; - } - } - } + if (result.first != nullptr) + { + symbol_file_add_separate (result.first, result.second.c_str (), + symfile_flags, this); + has_dwarf2 = true; } + /* If all the methods to collect the debuginfo failed, print the warnings, this is a no-op if there are no warnings. */ - if (debugfile.empty () && !has_dwarf2) + if (!has_dwarf2) warnings.emit (); return has_dwarf2; From patchwork Wed Oct 18 10:53:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 78085 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8B95B3875424 for ; Wed, 18 Oct 2023 10:54:16 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 57255386D616 for ; Wed, 18 Oct 2023 10:53:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 57255386D616 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 57255386D616 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626421; cv=none; b=bY7zyLRL6PaGQAbkwxZpcXpGoVs3IsY0iAyp/NS4ffc0TlbaVyhNSTS4DOUhuWl5gBrecmuzxHLfyG7UZL4J0wOW+2nAuCr+ydvXYDppbFwX9wzdof2Z9gVKPdk3DCcP8DutXtWh6flwxOHFJOzymfkpKWJlvrA944c8DjeBhuE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626421; c=relaxed/simple; bh=1Ha0zD5HLxUoLNKTMf1yVv1ETn2AtvTjD519bIR7uXE=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=XBervoVm8asZgXf3OSVi0kqBeD0Oj97r3y+AI4/ySZvfYYnxl4wWGucK9RUCvOeY3H4NBwqF79Z4XGKIQtQ6Zxx7R54An0a1pbp/kde7hQMxaeWwdIYQiJFlHL3pl5COct4KF997ovNSe4NtE6xwSFJjkpxTMNb8h1one8+v+mA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697626419; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=FauNp/vpMXwlxSNYPAPzBST28qVbIRykvHFSKcngcgU=; b=LilTwF0RDAaJ29WGslrZJF9V9WMrw0fALfkUN9BNYm0Khh7Ua8Nn781ILdH8Na7+DMAaso /e3LWW5SINdL02fytlNOMD0JCxqOybtCTDrCSo8BGgb+D+odBdGo4rSe5XEwjG4VKzJ6Zd 7VM4XWaX+TnzuKPyomMg94p9RAqqqTM= Received: from mail-vk1-f199.google.com (mail-vk1-f199.google.com [209.85.221.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-3-vtkczJ5rNFaZDDCBKA-xwA-1; Wed, 18 Oct 2023 06:53:37 -0400 X-MC-Unique: vtkczJ5rNFaZDDCBKA-xwA-1 Received: by mail-vk1-f199.google.com with SMTP id 71dfb90a1353d-49aa40b39adso2036866e0c.3 for ; Wed, 18 Oct 2023 03:53:37 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697626417; x=1698231217; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=FauNp/vpMXwlxSNYPAPzBST28qVbIRykvHFSKcngcgU=; b=wGS2/Sg6eSbOkXuQq+rwuaYYVCz4BpKgl2YuzOcQWn2lzI68HfOQ1Ggrlc9dU+/Haq UnTCrb1kZo6oUh5r/r5Zid/oRej3B8e8hR5ZbISSJZj0DbYiwb6xKBg1OW4t5XlsHhTF HnuYkK6ny3md8XUV+1A1XQ0WXjtkbUOLs95cE/HGRWgGA9mr1jGZQFkwh0NBWigiMH0/ 3PDvFnThV4QxOGJByu8ugdY6tTxwaGcwClC7lWubc7Kp588DVi/DaQQLOcQ9+90hAytr lc7u/+QmvCzEd/B7JcjRiN579ybuhaeMk4P8GxGcQiD+9g/iEQyRTws9paChP7+UOQQS GbEg== X-Gm-Message-State: AOJu0YxL0UNFJY0Ahqs70oBdT1tDMp53kpn5siH264FgR6zySarHX5mR x9fdqLsGx9c0Cw/s/1n/BUoKVcYjQfW8oUkgGjCJezqSnf/kVPSXsEmTt6PPETAGJZpPfrTJhOa qKj8sLLfXMhjQ7NKDQwIKxeWeJN+ot4XNzzscxa8SsI/CTbL+fRUnyo8UABvhn42PJfNGQ8uEQq k02MATbw== X-Received: by 2002:a1f:244e:0:b0:495:cac2:253b with SMTP id k75-20020a1f244e000000b00495cac2253bmr4141135vkk.0.1697626416877; Wed, 18 Oct 2023 03:53:36 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFJ65QQroZJ/4WPDFU6/dTnNI+Sao8JwwRHuj5cVaU5KNGTVxOjnyqRxP0k6kiKYw/ZR3007g== X-Received: by 2002:a1f:244e:0:b0:495:cac2:253b with SMTP id k75-20020a1f244e000000b00495cac2253bmr4141121vkk.0.1697626416402; Wed, 18 Oct 2023 03:53:36 -0700 (PDT) Received: from localhost ([31.111.84.209]) by smtp.gmail.com with ESMTPSA id k14-20020a05621414ee00b006564afc5908sm1253318qvw.111.2023.10.18.03.53.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 03:53:36 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 4/5] gdb: add an extension language hook for missing debug info Date: Wed, 18 Oct 2023 11:53:22 +0100 Message-Id: <95e0ba3218667b9e65968d83e98281c0984a1009.1697626088.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org This commit adds a new extension_language_ops hook which allows an extension to handle the case where GDB can't find a separate debug information file for a particular objfile. This commit doesn't actually implement the hook for any of GDB's extension languages, the next commit will do that. This commit just adds support for the hook to extension-priv.h and extension.[ch], and then reworks symfile-debug.c to call the hook. Right now the hook will always return its default value, which means GDB should do nothing different. As such, there should be no user visible changes after this commit. I'll give a brief description of what the hook does here so that we can understand the changes in symfile-debug.c. The next commit adds a Python implementation for this new hook, and gives a fuller description of the new functionality. Currently, when looking for separate debug information GDB tries three things, in this order: 1. Use the build-id to find the required debug information, 2. Check for .gnu_debuglink section and use that to look up the required debug information, 3. Check with debuginfod to see if it can supply the required information. The new extension_language_ops::handle_missing_debuginfo hook is called if all three steps fail to find any debug information. The hook has three possible return values: a. Nothing, no debug information is found, GDB continues without the debug information for this objfile. This matches the current behaviour of GDB, and is the default if nothing is implementing this new hook, b. Install debug information into a location that step #1 or #2 above would normally check, and then request that GDB repeats steps #1 and #2 in the hope that GDB will now find the debug information. If the debug information is still not found then GDB carries on without the debug information. If the debug information is found the GDB loads it and carries on, c. Return a filename for a file containing the required debug information. GDB loads the contents of this file and carries on. The changes in this commit mostly involve placing the core of objfile::find_and_add_separate_symbol_file into a loop which allows for steps #1 and #2 to be repeated. We take care to ensure that debuginfod is only queried once, the first time through. The assumption is that no extension is going to be able to control the replies from debuginfod, so there's no point making a second request -- and as these requests go over the network, they could potentially be slow. The warnings that find_and_add_separate_symbol_file collects are displayed only once assuming that no debug information is found. If debug information is found, even after the extension has operated, then the warnings are not shown; remember, these are warnings from GDB about failure to find any suitable debug information, so it makes sense to hide these if debug information is found. --- gdb/extension-priv.h | 7 ++++ gdb/extension.c | 19 ++++++++++ gdb/extension.h | 62 ++++++++++++++++++++++++++++++ gdb/symfile-debug.c | 89 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 158 insertions(+), 19 deletions(-) diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h index 3442302a0be..e71eac20d4e 100644 --- a/gdb/extension-priv.h +++ b/gdb/extension-priv.h @@ -279,6 +279,13 @@ struct extension_language_ops gdb::optional (*print_insn) (struct gdbarch *gdbarch, CORE_ADDR address, struct disassemble_info *info); + + /* Give extension languages a chance to deal with missing debug + information. OBJFILE is the file for which GDB was unable to find + any debug information. */ + ext_lang_missing_debuginfo_result + (*handle_missing_debuginfo) (const struct extension_language_defn *, + struct objfile *objfile); }; /* State necessary to restore a signal handler to its previous value. */ diff --git a/gdb/extension.c b/gdb/extension.c index 65f3bab32a7..9cb393e1d50 100644 --- a/gdb/extension.c +++ b/gdb/extension.c @@ -997,6 +997,25 @@ ext_lang_print_insn (struct gdbarch *gdbarch, CORE_ADDR address, return {}; } +/* See extension.h. */ + +ext_lang_missing_debuginfo_result +ext_lang_handle_missing_debuginfo (struct objfile *objfile) +{ + for (const struct extension_language_defn *extlang : extension_languages) + { + if (extlang->ops == nullptr + || extlang->ops->handle_missing_debuginfo == nullptr) + continue; + ext_lang_missing_debuginfo_result result + = extlang->ops->handle_missing_debuginfo (extlang, objfile); + if (!result.filename ().empty () || result.try_again ()) + return result; + } + + return {}; +} + /* Called via an observer before gdb prints its prompt. Iterate over the extension languages giving them a chance to change the prompt. The first one to change the prompt wins, diff --git a/gdb/extension.h b/gdb/extension.h index 28f9e3bc028..282d591be43 100644 --- a/gdb/extension.h +++ b/gdb/extension.h @@ -337,6 +337,68 @@ extern gdb::optional ext_lang_colorize_disasm extern gdb::optional ext_lang_print_insn (struct gdbarch *gdbarch, CORE_ADDR address, struct disassemble_info *info); +/* When GDB calls into an extension language because an objfile was + discovered for which GDB couldn't find any debug information, this + structure holds the result that the extension language returns. + + There are three possible actions that might be returned by an extension; + first an extension can return a filename, this is the path to the file + containing the required debug information. The second possibility is + to return a flag indicating that GDB should check again for the missing + debug information, this would imply that the extension has installed + the debug information into a location where GDB can be expected to find + it. And the third option is for the extension to just return a null + result, indication there is nothing the extension can do to provide the + missing debug information. */ +struct ext_lang_missing_debuginfo_result +{ + /* Default result. The extension was unable to provide the missing debug + info. */ + ext_lang_missing_debuginfo_result () + { /* Nothing. */ } + + /* When TRY_AGAIN is true GDB should try searching again, the extension + may have installed the missing debug info into a suitable location. + When TRY_AGAIN is false this is equivalent to the default, no + argument, constructor. */ + ext_lang_missing_debuginfo_result (bool try_again) + : m_try_again (try_again) + { /* Nothing. */ } + + /* Look in FILENAME for the missing debug info. */ + ext_lang_missing_debuginfo_result (std::string &&filename) + : m_filename (std::move (filename)) + { /* Nothing. */ } + + /* The filename where GDB can find the missing debuginfo. This is empty + if the extension didn't suggest a file that can be used. */ + const std::string & + filename () const + { + return m_filename; + } + + /* Returns true if GDB should look again for the debug information. */ + const bool + try_again () const + { + return m_try_again; + } + +private: + /* The filename where the missing debuginfo can now be found. */ + std::string m_filename; + + /* When true GDB will search again for the debuginfo using its standard + techniques. When false GDB will not search again. */ + bool m_try_again = false; +}; + +/* Called when GDB failed to find any debug information for OBJFILE. */ + +extern ext_lang_missing_debuginfo_result ext_lang_handle_missing_debuginfo + (struct objfile *objfile); + #if GDB_SELF_TEST namespace selftests { extern void (*hook_set_active_ext_lang) (); diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c index 0a4499320c7..fd9e55dffad 100644 --- a/gdb/symfile-debug.c +++ b/gdb/symfile-debug.c @@ -632,31 +632,82 @@ bool objfile::find_and_add_separate_symbol_file (symfile_add_flags symfile_flags) { bool has_dwarf2 = false; - deferred_warnings warnings; - std::pair result; + /* Usually we only make a single pass when looking for separate debug + information. However, it is possible for an extension language hook + to request that GDB make a second pass, in which case max_attempts + will be updated, and the loop restarted. */ + for (unsigned attempt = 0, max_attempts = 1; + attempt < max_attempts && !has_dwarf2; + ++attempt) + { + gdb_assert (max_attempts <= 2); - result = simple_find_and_open_separate_symbol_file - (this, find_separate_debug_file_by_buildid, &warnings); + deferred_warnings warnings; + std::pair result; - if (result.first == nullptr) - result = simple_find_and_open_separate_symbol_file - (this, find_separate_debug_file_by_debuglink, &warnings); + result = simple_find_and_open_separate_symbol_file + (this, find_separate_debug_file_by_buildid, &warnings); - if (result.first == nullptr) - result = debuginfod_find_and_open_separate_symbol_file (this); + if (result.first == nullptr) + result = simple_find_and_open_separate_symbol_file + (this, find_separate_debug_file_by_debuglink, &warnings); - if (result.first != nullptr) - { - symbol_file_add_separate (result.first, result.second.c_str (), - symfile_flags, this); - has_dwarf2 = true; - } + /* Only try debuginfod on the first attempt. Sure, we could imagine + an extension that somehow adds the required debug info to the + debuginfod server but, at least for now, we don't support this + scenario. Better for the extension to return new debug info + directly to GDB. Plus, going to the debuginfod server might be + slow, so that's a good argument for only doing this once. */ + if (result.first == nullptr && attempt == 0) + result + = debuginfod_find_and_open_separate_symbol_file (this); + + if (result.first != nullptr) + { + /* We found a separate debug info symbol file. If this is our + first attempt then setting HAS_DWARF2 will cause us to break + from the attempt loop. */ + symbol_file_add_separate (result.first, result.second.c_str (), + symfile_flags, this); + has_dwarf2 = true; + } + else if (attempt == 0) + { + /* Failed to find a separate debug info symbol file. Call out to + the extension languages. The user might have registered an + extension that can find the debug info for us, or maybe give + the user a system specific message that guides them to finding + the missing debug info. */ + + ext_lang_missing_debuginfo_result ext_result + = ext_lang_handle_missing_debuginfo (this); + if (!ext_result.filename ().empty ()) + { + /* Extension found a suitable debug file for us. */ + gdb_bfd_ref_ptr debug_bfd + = symfile_bfd_open_no_error (ext_result.filename ().c_str ()); - /* If all the methods to collect the debuginfo failed, print the - warnings, this is a no-op if there are no warnings. */ - if (!has_dwarf2) - warnings.emit (); + if (debug_bfd != nullptr) + { + symbol_file_add_separate (debug_bfd, + ext_result.filename ().c_str (), + symfile_flags, this); + has_dwarf2 = true; + } + } + else if (ext_result.try_again ()) + { + max_attempts = 2; + continue; + } + } + + /* If we still have not got a separate debug symbol file, then + emit any warnings we've collected so far. */ + if (!has_dwarf2) + warnings.emit (); + } return has_dwarf2; } From patchwork Wed Oct 18 10:53:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 78086 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 56F773870894 for ; Wed, 18 Oct 2023 10:54:37 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 442FC3870C09 for ; Wed, 18 Oct 2023 10:53:54 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 442FC3870C09 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 442FC3870C09 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626439; cv=none; b=RRZ6avgQy8rKses/J85s6zcdpQQy7RfWxXY6XyeW83soyDiRhqAjCHMo5Y6Y8Sy5Xjcjse8Alq/tCI2LynCM5QeGItA9EZjXiUWEn6CW/yaDSK79EmmwRZfUhdJYiaqEM8C3GVrXeYUjQiEMEyeMf0KJ9zBQVRtLzu3GkPgOB/k= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697626439; c=relaxed/simple; bh=7zL7UKKj/8P4+8WncLQR9Uu+59vqke1eES95qM03akI=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=RPU8ki9VZ8wzgC0O6cr+F+Qszhjua8EAu+AWsbZMzbTd+k+XhRtHeCjAfeGs800lXbvVoUiPotjmB9auR857OlG14hLmRRXlCvANGB9PIvfIZAFMICdopif2IQ1WtGw4pOYXvROWa4vWrbSjef5GRe7Njk/gUJ92RTRMpiuxcfY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1697626433; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BnyG9IV0FiL/w8KiM45XabyAU4dmocHQESO8FQR/87A=; b=QJ6LCA/Yo5PX4dgk1fqFPLSMF8YYQFETbDWYrBRPX3lm0LVsDc9XP1QJlrXsWzttomytbJ gzGr7A0e7CaEI5BzPE+1iLoIxhzkbn+ujF8/mVMn+1LyFS4hCRbY4z+8+ku086UknmH2qx cmG9akxEMCg/ZJMivDjkE2vupzbiSyA= Received: from mail-qk1-f197.google.com (mail-qk1-f197.google.com [209.85.222.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-19-xlreMlsENUuOIo_em2bB0g-1; Wed, 18 Oct 2023 06:53:42 -0400 X-MC-Unique: xlreMlsENUuOIo_em2bB0g-1 Received: by mail-qk1-f197.google.com with SMTP id af79cd13be357-778999c5f1dso173285a.2 for ; Wed, 18 Oct 2023 03:53:42 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697626422; x=1698231222; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=BnyG9IV0FiL/w8KiM45XabyAU4dmocHQESO8FQR/87A=; b=EIJQ5YZe6/Q0SHEQN7gq9l8PfR7kAEJG4If0qJeFQrGAZUByILCrNva6xYCQFgYfhy nnciavxxy4owUMBhD2/o9LKAXzOK9xDeQSZQCXHRJjvqUVeU2l0vA4YyhxdpPsX5Uk9h 66MWVw0ElBzolvfWIK3CMLbGSE+Gez8gfnx9OdtsTab+c18TPijFjw1TNQsirV/I15Sj XNoFk0tyj84ElHJAmmbZ5xkzb4Q8IbssffZokHIOc4G0h8tS5Ar6wuk2VCg6kPnJzYYI oqOqQscxdCJ9U2a0hKudgEbEjaPyJZ6hLKlk2Q1LDeA7dWP8kcYFHUsqczHB/1bp4tH/ a7wA== X-Gm-Message-State: AOJu0YzilYjAKv1kfNVWzd5yQ/QtpYLZSFaUWGp+0qsuRAy/BzWsACLp W/juUq9bACE///P6mQMMPMv7hpLf5CC9VQ/z7z3PiW040aTbHUT027vR8HLG71J8HbpEHWHn4Z4 kO5TG2WEGV4PhUMSpJnYUnpPSXdIKmjLButXiI0RQUZDGBdsU90YFXPsq6tjktiiapksk0ADd91 vo0uFohw== X-Received: by 2002:a05:620a:850f:b0:76d:7431:2fc2 with SMTP id pe15-20020a05620a850f00b0076d74312fc2mr4171065qkn.73.1697626420279; Wed, 18 Oct 2023 03:53:40 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEeIM63W5ootWKrGgX4Vdly9czwcgD82PIwLm+3kHwqruzZhH5LuMFehdaPHOqu4AY42fFFCQ== X-Received: by 2002:a05:620a:850f:b0:76d:7431:2fc2 with SMTP id pe15-20020a05620a850f00b0076d74312fc2mr4171020qkn.73.1697626418730; Wed, 18 Oct 2023 03:53:38 -0700 (PDT) Received: from localhost ([31.111.84.209]) by smtp.gmail.com with ESMTPSA id pw9-20020a05620a63c900b007756e75b91bsm1369360qkn.78.2023.10.18.03.53.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 03:53:38 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 5/5] gdb: implement missing debug handler hook for Python Date: Wed, 18 Oct 2023 11:53:23 +0100 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org This commit builds on the previous commit, and implements the extension_language_ops::handle_missing_debuginfo function for Python. This hook will give user supplied Python code a chance to help find missing debug information. The implementation of the new hook is pretty minimal within GDB's C++ code; most of the work is out-sourced to a Python implementation which is modelled heavily on how GDB's Python frame unwinders are implemented. The following new commands are added as commands implemented in Python, this is similar to how the Python unwinder commands are implemented: info missing-debug-handlers enable missing-debug-handler LOCUS HANDLER disable missing-debug-handler LOCUS HANDLER To make use of this extension hook a user will create missing debug information handler objects, and registers these handlers with GDB. When GDB encounters an objfile that is missing debug information, each handler is called in turn until one is able to help. Here is a minimal handler that does nothing useful: import gdb import gdb.missing_debug class MyFirstHandler(gdb.missing_debug.MissingDebugHandler): def __init__(self): super().__init__("my_first_handler") def __call__(self, objfile): # This handler does nothing useful. return None gdb.missing_debug.register_handler(None, MyFirstHandler()) Returning None from the __call__ method tells GDB that this handler was unable to find the missing debug information, and GDB should ask any other registered handlers. By extending the __call__ method it is possible for the Python extension to locate the debug information for objfile and return a value that tells GDB how to use the information that has been located. Possible return values from a handler: - None: This means the handler couldn't help. GDB will call other registered handlers to see if they can help instead. - False: The handler has done all it can, but the debug information for the objfile still couldn't be found. GDB will not call any other handlers, and will continue without the debug information for objfile. - True: The handler has installed the debug information into a location where GDB would normally expect to find it. GDB should look again for the debug information. - A string: The handler can return a filename, which is the file containing the missing debug information. GDB will load this file. When a handler returns True, GDB will look again for the debug information, but only using the standard built-in build-id and .gnu_debuglink based lookup strategies. It is not possible for an extension to trigger another debuginfod lookup; the assumption is that the debuginfod server is remote, and out of the control of extensions running within GDB. Handlers can be registered globally, or per program space. GDB checks the handlers for the current program space first, and then all of the global handles. The first handler that returns a value that is not None, has "handled" the objfile, at which point GDB continues. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 26 + gdb/data-directory/Makefile.in | 2 + gdb/doc/python.texi | 136 +++++ gdb/python/lib/gdb/__init__.py | 41 ++ gdb/python/lib/gdb/command/missing_debug.py | 226 +++++++++ gdb/python/lib/gdb/missing_debug.py | 169 +++++++ gdb/python/py-progspace.c | 51 ++ gdb/python/python.c | 83 ++- gdb/testsuite/gdb.python/py-missing-debug.c | 22 + gdb/testsuite/gdb.python/py-missing-debug.exp | 473 ++++++++++++++++++ gdb/testsuite/gdb.python/py-missing-debug.py | 120 +++++ 11 files changed, 1348 insertions(+), 1 deletion(-) create mode 100644 gdb/python/lib/gdb/command/missing_debug.py create mode 100644 gdb/python/lib/gdb/missing_debug.py create mode 100644 gdb/testsuite/gdb.python/py-missing-debug.c create mode 100644 gdb/testsuite/gdb.python/py-missing-debug.exp create mode 100644 gdb/testsuite/gdb.python/py-missing-debug.py diff --git a/gdb/NEWS b/gdb/NEWS index 81264c0cfb3..509586b9589 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -6,11 +6,37 @@ * GDB index now contains information about the main function. This speeds up startup when it is being used for some large binaries. +* New Commands + +info missing-debug-handler + List all the registered missing debug handlers. + +enable missing-debug-handler LOCUS HANDLER +disable missing-debug-handler LOCUS HANDLER + Enable or disable a missing debug handler with a name matching the + regular expression HANDLER, in LOCUS. + + LOCUS can be 'global' to operate on global missing debug handler, + 'progspace' to operate on handlers within the current program space, + or can be a regular expression which is matched against the filename + of the primary executable in each program space. + * Python API ** New function gdb.notify_mi(NAME, DATA), that emits custom GDB/MI async notification. + ** New module gdb.missing_debug that facilitates dealing with + objfiles that are missing any debug information. + + ** New function gdb.missing_debug.register_handler that can register + an instance of a sub-class of gdb.missing_debug.MissingDebugInfo + as a handler for objfiles that are missing debug information. + + ** New class gdb.missing_debug.MissingDebugInfo which can be + sub-classed to create handlers for objfiles with missing debug + information. + *** Changes in GDB 14 * GDB now supports the AArch64 Scalable Matrix Extension 2 (SME2), which diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 04a8c8eca69..7af6bafb0c9 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -73,6 +73,7 @@ PYTHON_FILE_LIST = \ gdb/FrameDecorator.py \ gdb/FrameIterator.py \ gdb/frames.py \ + gdb/missing_debug.py \ gdb/printing.py \ gdb/prompt.py \ gdb/styling.py \ @@ -82,6 +83,7 @@ PYTHON_FILE_LIST = \ gdb/command/__init__.py \ gdb/command/explore.py \ gdb/command/frame_filters.py \ + gdb/command/missing_debug.py \ gdb/command/pretty_printers.py \ gdb/command/prompt.py \ gdb/command/type_printers.py \ diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 546b4d4b962..5aa1062699a 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -230,6 +230,7 @@ * Connections In Python:: Python representation of connections. * TUI Windows In Python:: Implementing new TUI windows. * Disassembly In Python:: Instruction Disassembly In Python +* Missing Debug Info In Python:: Handle missing debug info from Python. @end menu @node Basic Python @@ -5236,6 +5237,12 @@ objects. @xref{Frame Filter API}, for more information. @end defvar +@defvar Progspace.missing_debug_handlers +The @code{missing_debug_handlers} attribute is a list of the missing +debug handler objects for this program space. @xref{Missing Debug +Info In Python}, for more information. +@end defvar + A program space has the following methods: @defun Progspace.block_for_pc (pc) @@ -7803,6 +7810,135 @@ gdb.disassembler.register_disassembler(NibbleSwapDisassembler()) @end smallexample +@node Missing Debug Info In Python +@subsubsection Missing Debug Info In Python +@cindex python, handle missing debug information + +When @value{GDBN} encounters a new objfile (@pxref{Objfiles In +Python}), e.g.@: the primary executable, or any shared libraries used +by the inferior, @value{GDBN} will attempt to load the corresponding +debug information for that objfile. The debug information might be +found within the objfile itself, or within a separate objfile which +@value{GDBN} will automatically locate and load. + +Sometimes though, @value{GDBN} might not find any debug information +for an objfile, in this case the debugging experience will be +restricted. + +If @value{GDBN} fails to locate any debug information for a particular +objfile, there is an opportunity for a Python extension to step in. A +Python extension can potentially locate the missing debug information +using some platform, or project, specific steps, and inform +@value{GDBN} of its location. Or a Python extension might provide +some platform, or project specific advice to the user about how to +obtain the missing debug information. + +A missing debug information Python extension consists of a handler +object which has the @code{name} and @code{enabled} attributes, and +implements the @code{__call__} method. When @value{GDBN} encounters +an objfile for which it is unable to find any debug information the +@code{__call__} method is invoked. Full details of how handlers are +written can be found below. + +@subheading The @code{gdb.missing_debug} Module + +@value{GDBN} comes with a @code{gdb.missing_debug} module which +contains the following class and global function: + +@deftp{class} gdb.missing_debug.MissingDebugHandler + +The @code{MissingDebugHandler} is a base class from which user created +handlers can derive, though it is not required that handlers derive +from this class, so long as any user created handler has the +@code{name} and @code{enabled} attributes, and implements the +@code{__call__} method. + +@defun MissingDebugHandler.__init__ (name) +The @var{name} is a string used to reference this unwinder within some +@value{GDBN} commands. Valid names consist of the characters +@code{[-_a-zA-Z0-9]}, creating a handler with an invalid name raises a +@code{ValueError} exception. +@end defun + +@defun MissingDebugHandler.__call__ (objfile) +Sub-classes must override the @code{__call__} method. The +@var{objfile} argument will be a @code{gdb.Objfile}, this is the +objfile for which @value{GDBN} was unable to find any debug +information. + +The return value from the @code{__call__} method indicates what +@value{GDBN} should do next. The possible return values are: + +@itemize @bullet +@item @code{None} + +This indicates that this handler could not help with @var{objfile}, +@value{GDBN} should call any other registered handlers. + +@item @code{True} + +This indicates that this handler has installed the debug information +into a location where @value{GDBN} would normally expect to find it +when looking for separate debug information files (@pxref{Separate +Debug Files}). @value{GDBN} will repeat the normal lookup process, +which should now find the separate debug file. + +If @value{GDBN} still doesn't find the separate debug information file +after this second attempt, then the Python missing debug information +handlers are not invoked a second time, this prevents a badly behaved +handler causing @value{GDBN} to get stuck in a loop. @value{GDBN} +will continue without any debug information for @var{objfile}. + +@item @code{False} + +This indicates that this handler has done everything that it intends +to do with @var{objfile}, but no separate debug information can be +found. @value{GDBN} will not call any other registered handlers for +@var{objfile}. @value{GDBN} will continue without debugging +information for @var{objfile}. + +@item A string + +The returned string should contain a filename. @value{GDBN} will not +call any further registered handlers, and will instead load the debug +information from the file identified by the returned filename. +@end itemize +@end defun + +@defvar MissingDebugHandler.name +A read-only attribute which is a string, the name of this handler. +@end defvar + +@defvar MissingDebugHandler.enabled +A modifiable attribute containing a boolean; when @code{True}, the +handler is enabled, and will be used by @value{GDBN}. When +@code{False}, the handler has been disabled, and will not be used. +@end defvar +@end deftp + +@defun gdb.missing_debug.register_handler (locus, handler, replace=@code{False}) +Register a new missing debug handler with @value{GDBN}. + +@var{handler} is an instance of a sub-class of +@code{MissingDebugHandler}, or at least an instance of an object that +has the same attributes and methods as @code{MissingDebugHandler}. + +@var{locus} specifies to which handler list to prepend @var{handler}. +It can be either a @code{gdb.Progspace} (@pxref{Progspaces In Python}) +or @code{None}, in which case the handler is registered globally. The +newly registered @var{handler} will be called before any other handler +from the same locus. Two handlers in the same locus cannot have the +same name, an attempt to add a handler with an already existing name +raises an exception unless @var{replace} is @code{True}, in which case +the old handler is deleted and the new handler is prepended to the +selected handler list. + +@value{GDBN} first calls the handlers for the current program space, +and then the globally registered handlers. As soon as a handler +returns a value other than @code{None}, no further handlers are called +for this objfile. +@end defun + @node Python Auto-loading @subsection Python Auto-loading @cindex Python auto-loading diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py index b3124369fe8..93ed50effcb 100644 --- a/gdb/python/lib/gdb/__init__.py +++ b/gdb/python/lib/gdb/__init__.py @@ -84,6 +84,8 @@ xmethods = [] frame_filters = {} # Initial frame unwinders. frame_unwinders = [] +# Initial missing debug handlers. +missing_debug_handlers = [] def _execute_unwinders(pending_frame): @@ -291,3 +293,42 @@ class Thread(threading.Thread): # threads. with blocked_signals(): super().start() + + +def _handle_missing_debuginfo(objfile): + """Internal function called from GDB to execute missing debug + handlers. + + Run each of the currently registered, and enabled missing debug + handler objects for the current program space and then from the + global list. Stop after the first handler that returns a result + other than None. + + Arguments: + objfile: A gdb.Objfile for which GDB could not find any debug + information. + + Returns: + None: No debug information could be found for objfile. + False: A handler has done all it can with objfile, but no + debug information could be found. + True: Debug information might have been installed by a + handler, GDB should check again. + A string: This is the filename of a file containing the + required debug information. + """ + pspace = objfile.progspace + + for handler in pspace.missing_debug_handlers: + if handler.enabled: + result = handler(objfile) + if result is not None: + return result + + for handler in missing_debug_handlers: + if handler.enabled: + result = handler(objfile) + if result is not None: + return result + + return None diff --git a/gdb/python/lib/gdb/command/missing_debug.py b/gdb/python/lib/gdb/command/missing_debug.py new file mode 100644 index 00000000000..4ffd1d59800 --- /dev/null +++ b/gdb/python/lib/gdb/command/missing_debug.py @@ -0,0 +1,226 @@ +# Missing debug related commands. +# +# 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 re + + +def validate_regexp(exp, idstring): + """Compile exp into a compiler regular expression object. + + Arguments: + exp: The string to compile into a re.Pattern object. + idstring: A string, what exp is a regexp for. + + Returns: + A re.Pattern object representing exp. + + Raises: + SyntaxError: If exp is an invalid regexp. + """ + try: + return re.compile(exp) + except SyntaxError: + raise SyntaxError("Invalid %s regexp: %s." % (idstring, exp)) + + +def parse_missing_debug_command_args(arg): + """Internal utility to parse missing debug handler command argv. + + Arguments: + arg: The arguments to the command. The format is: + [locus-regexp [name-regexp]] + + Returns: + A 2-tuple of compiled regular expressions. + + Raises: + SyntaxError: an error processing ARG + """ + argv = gdb.string_to_argv(arg) + argc = len(argv) + if argc > 2: + raise SyntaxError("Too many arguments.") + locus_regexp = "" + name_regexp = "" + if argc >= 1: + locus_regexp = argv[0] + if argc >= 2: + name_regexp = argv[1] + return ( + validate_regexp(locus_regexp, "locus"), + validate_regexp(name_regexp, "handler"), + ) + + +class InfoMissingDebugHanders(gdb.Command): + """GDB command to list missing debug handlers. + + Usage: info missing-debug-handlers [LOCUS-REGEXP [NAME-REGEXP]] + + LOCUS-REGEXP is a regular expression matching the location of the + handler. If it is omitted, all registered handlers from all + loci are listed. A locus can be 'global', 'progspace' to list + the handlers from the current progspace, or a regular expression + matching filenames of progspaces. + + NAME-REGEXP is a regular expression to filter missing debug + handler names. If this omitted for a specified locus, then all + registered handlers in the locus are listed. + """ + + def __init__(self): + super().__init__("info missing-debug-handlers", gdb.COMMAND_FILES) + + def list_handlers(self, title, handlers, name_re): + """Lists the missing debug handlers whose name matches regexp. + + Arguments: + title: The line to print before the list. + handlers: The list of the missing debug handlers. + name_re: handler name filter. + """ + if not handlers: + return + print(title) + for handler in handlers: + if name_re.match(handler.name): + print( + " %s%s" % (handler.name, "" if handler.enabled else " [disabled]") + ) + + def invoke(self, arg, from_tty): + locus_re, name_re = parse_missing_debug_command_args(arg) + + if locus_re.match("progspace") and locus_re.pattern != "": + cp = gdb.current_progspace() + self.list_handlers( + "Progspace %s:" % cp.filename, cp.missing_debug_handlers, name_re + ) + + for progspace in gdb.progspaces(): + filename = progspace.filename or "" + if locus_re.match(filename): + if filename == "": + if progspace == gdb.current_progspace(): + msg = "Current Progspace:" + else: + msg = "Progspace :" + else: + msg = "Progspace %s:" % filename + self.list_handlers( + msg, + progspace.missing_debug_handlers, + name_re, + ) + + # Print global handlers last, as these are invoked last. + if locus_re.match("global"): + self.list_handlers("Global:", gdb.missing_debug_handlers, name_re) + + +def do_enable_handler1(handlers, name_re, flag): + """Enable/disable missing debug handlers whose names match given regex. + + Arguments: + handlers: The list of missing debug handlers. + name_re: Handler name filter. + flag: A boolean indicating if we should enable or disable. + + Returns: + The number of handlers affected. + """ + total = 0 + for handler in handlers: + if name_re.match(handler.name) and handler.enabled != flag: + handler.enabled = flag + total += 1 + return total + + +def do_enable_handler(arg, flag): + """Enable or disable missing debug handlers.""" + (locus_re, name_re) = parse_missing_debug_command_args(arg) + total = 0 + if locus_re.match("global"): + total += do_enable_handler1(gdb.missing_debug_handlers, name_re, flag) + if locus_re.match("progspace") and locus_re.pattern != "": + total += do_enable_handler1( + gdb.current_progspace().missing_debug_handlers, name_re, flag + ) + for progspace in gdb.progspaces(): + filename = progspace.filename or "" + if locus_re.match(filename): + total += do_enable_handler1(progspace.missing_debug_handlers, name_re, flag) + print( + "%d missing debug handler%s %s" + % (total, "" if total == 1 else "s", "enabled" if flag else "disabled") + ) + + +class EnableMissingDebugHandler(gdb.Command): + """GDB command to enable missing debug handlers. + + Usage: enable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]] + + LOCUS-REGEXP is a regular expression specifying the handlers to + enable. It can be 'global', 'progspace' for the current + progspace, or the filename for a file associated with a progspace. + + NAME_REGEXP is a regular expression to filter handler names. If + this omitted for a specified locus, then all registered handlers + in the locus are affected. + """ + + def __init__(self): + super().__init__("enable missing-debug-handler", gdb.COMMAND_FILES) + + def invoke(self, arg, from_tty): + """GDB calls this to perform the command.""" + do_enable_handler(arg, True) + + +class DisableMissingDebugHandler(gdb.Command): + """GDB command to disable missing debug handlers. + + Usage: disable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]] + + LOCUS-REGEXP is a regular expression specifying the handlers to + enable. It can be 'global', 'progspace' for the current + progspace, or the filename for a file associated with a progspace. + + NAME_REGEXP is a regular expression to filter handler names. If + this omitted for a specified locus, then all registered handlers + in the locus are affected. + """ + + def __init__(self): + super().__init__("disable missing-debug-handler", gdb.COMMAND_FILES) + + def invoke(self, arg, from_tty): + """GDB calls this to perform the command.""" + do_enable_handler(arg, False) + + +def register_missing_debug_handler_commands(): + """Installs the missing debug handler commands.""" + InfoMissingDebugHanders() + EnableMissingDebugHandler() + DisableMissingDebugHandler() + + +register_missing_debug_handler_commands() diff --git a/gdb/python/lib/gdb/missing_debug.py b/gdb/python/lib/gdb/missing_debug.py new file mode 100644 index 00000000000..42d69858f96 --- /dev/null +++ b/gdb/python/lib/gdb/missing_debug.py @@ -0,0 +1,169 @@ +# Copyright (C) 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 . + +""" +MissingDebugHandler base class, and register_handler function. +""" + +import gdb + + +def _validate_name(name): + """Validate a missing debug handler name string. + + If name is valid as a missing debug handler name, then this + function does nothing. If name is not valid then an exception is + raised. + + Arguments: + name: A string, the name of a missing debug handler. + + Returns: + Nothing. + + Raises: + ValueError: If name is invalid as a missing debug handler + name. + """ + for ch in name: + if not ch.isascii() or not (ch.isalnum() or ch in "_-"): + raise ValueError("invalid character '%s' in handler name: %s" % (ch, name)) + + +class MissingDebugHandler(object): + """Base class for missing debug handlers written in Python. + + A missing debug handler has a single method __call__ along with + the read/write attribute enabled, and a read-only attribute name. + + Attributes: + name: Read-only attribute, the name of this handler. + enabled: When true this handler is enabled. + """ + + def __init__(self, name): + """Constructor. + + Args: + name: An identifying name for this handler. + + Raises: + TypeError: name is not a string. + ValueError: name contains invalid characters. + """ + + if not isinstance(name, str): + raise TypeError("incorrect type for name: %s" % type(name)) + + _validate_name(name) + + self._name = name + self._enabled = True + + @property + def name(self): + return self._name + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, value): + if not isinstance(value, bool): + raise TypeError("incorrect type for enabled attribute: %s" % type(value)) + self._enabled = value + + def __call__(self, objfile): + """GDB handle missing debug information for an objfile. + + Arguments: + objfile: A gdb.Objfile for which GDB could not find any + debug information. + + Returns: + True: GDB should try again to locate the debug information + for objfile, the handler may have installed the + missing information. + False: GDB should move on without the debug information + for objfile. + A string: GDB should load the file at the given path; it + contains the debug information for objfile. + None: This handler can't help with objfile. GDB should + try any other registered handlers. + """ + raise NotImplementedError("MissingDebugHandler.__call__()") + + +def register_handler(locus, handler, replace=False): + """Register handler in given locus. + + The handler is prepended to the locus's missing debug handlers + list. The name of handler should be unique (or replace must be + True). + + Arguments: + locus: Either a progspace, or None (in which case the unwinder + is registered globally). + handler: An object of a gdb.MissingDebugHandler subclass. + + replace: If True, replaces existing handler with the same name + within locus. Otherwise, raises RuntimeException if + unwinder with the same name already exists. + + Returns: + Nothing. + + Raises: + RuntimeError: The name of handler is not unique. + TypeError: Bad locus type. + AttributeError: Required attributes of handler are missing. + """ + + if locus is None: + if gdb.parameter("verbose"): + gdb.write("Registering global %s handler ...\n" % handler.name) + locus = gdb + elif isinstance(locus, gdb.Progspace): + if gdb.parameter("verbose"): + gdb.write( + "Registering %s handler for %s ...\n" % (handler.name, locus.filename) + ) + else: + raise TypeError("locus should be gdb.Progspace or None") + + # Some sanity checks on HANDLER. Calling getattr will raise an + # exception if the attribute doesn't exist, which is what we want. + # These checks are not exhaustive; we don't check the attributes + # have the correct types, or the method has the correct signature, + # but this should catch some basic mistakes. + getattr(handler, "name") + getattr(handler, "enabled") + call_method = getattr(handler, "__call__") + if not callable(call_method): + raise AttributeError( + "'%s' object's '__call__' attribute is not callable" + % type(handler).__name__ + ) + + i = 0 + for needle in locus.missing_debug_handlers: + if needle.name == handler.name: + if replace: + del locus.missing_debug_handlers[i] + else: + raise RuntimeError("Handler %s already exists." % handler.name) + i += 1 + locus.missing_debug_handlers.insert(0, handler) diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c index f636ffd0460..0797ef1fa6b 100644 --- a/gdb/python/py-progspace.c +++ b/gdb/python/py-progspace.c @@ -55,6 +55,9 @@ struct pspace_object /* The debug method list. */ PyObject *xmethods; + + /* The missing debug handler list. */ + PyObject *missing_debug_handlers; }; extern PyTypeObject pspace_object_type @@ -164,6 +167,7 @@ pspy_dealloc (PyObject *self) Py_XDECREF (ps_self->frame_unwinders); Py_XDECREF (ps_self->type_printers); Py_XDECREF (ps_self->xmethods); + Py_XDECREF (ps_self->missing_debug_handlers); Py_TYPE (self)->tp_free (self); } @@ -199,6 +203,10 @@ pspy_initialize (pspace_object *self) if (self->xmethods == NULL) return 0; + self->missing_debug_handlers = PyList_New (0); + if (self->missing_debug_handlers == nullptr) + return 0; + return 1; } @@ -353,6 +361,47 @@ pspy_get_xmethods (PyObject *o, void *ignore) return self->xmethods; } +/* Return the list of missing debug handlers for this program space. */ + +static PyObject * +pspy_get_missing_debug_handlers (PyObject *o, void *ignore) +{ + pspace_object *self = (pspace_object *) o; + + Py_INCREF (self->missing_debug_handlers); + return self->missing_debug_handlers; +} + +/* Set this program space's list of missing debug handlers to HANDLERS. */ + +static int +pspy_set_missing_debug_handlers (PyObject *o, PyObject *handlers, + void *ignore) +{ + pspace_object *self = (pspace_object *) o; + + if (handlers == nullptr) + { + PyErr_SetString (PyExc_TypeError, + "cannot delete the missing debug handlers list"); + return -1; + } + + if (!PyList_Check (handlers)) + { + PyErr_SetString (PyExc_TypeError, + "the missing debug handlers attribute must be a list"); + return -1; + } + + /* Take care in case the LHS and RHS are related somehow. */ + gdbpy_ref<> tmp (self->missing_debug_handlers); + Py_INCREF (handlers); + self->missing_debug_handlers = handlers; + + return 0; +} + /* Set the 'type_printers' attribute. */ static int @@ -745,6 +794,8 @@ static gdb_PyGetSetDef pspace_getset[] = "Type printers.", NULL }, { "xmethods", pspy_get_xmethods, NULL, "Debug methods.", NULL }, + { "missing_debug_handlers", pspy_get_missing_debug_handlers, + pspy_set_missing_debug_handlers, "Missing debug handlers.", NULL }, { NULL } }; diff --git a/gdb/python/python.c b/gdb/python/python.c index d3dea088c3b..5caad5b227a 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -124,7 +124,9 @@ static enum ext_lang_rc gdbpy_before_prompt_hook static gdb::optional gdbpy_colorize (const std::string &filename, const std::string &contents); static gdb::optional gdbpy_colorize_disasm - (const std::string &content, gdbarch *gdbarch); +(const std::string &content, gdbarch *gdbarch); +static ext_lang_missing_debuginfo_result gdbpy_handle_missing_debuginfo + (const struct extension_language_defn *extlang, struct objfile *objfile); /* The interface between gdb proper and loading of python scripts. */ @@ -170,6 +172,8 @@ static const struct extension_language_ops python_extension_ops = gdbpy_colorize_disasm, gdbpy_print_insn, + + gdbpy_handle_missing_debuginfo }; #endif /* HAVE_PYTHON */ @@ -1685,6 +1689,83 @@ gdbpy_get_current_objfile (PyObject *unused1, PyObject *unused2) return objfile_to_objfile_object (gdbpy_current_objfile).release (); } +/* Implement the 'handle_missing_debuginfo' hook for Python. GDB has + failed to find any debug information for OBJFILE. The extension has a + chance to record this, or even install the required debug information. + See the description of ext_lang_missing_debuginfo_result in + extension-priv.h for details of the return value. */ + +static ext_lang_missing_debuginfo_result +gdbpy_handle_missing_debuginfo (const struct extension_language_defn *extlang, + struct objfile *objfile) +{ + /* Early exit if Python is not initialised. */ + if (!gdb_python_initialized) + return {}; + + struct gdbarch *gdbarch = objfile->arch (); + + gdbpy_enter enter_py (gdbarch); + + /* Convert OBJFILE into the corresponding Python object. */ + gdbpy_ref<> pyo_objfile = objfile_to_objfile_object (objfile); + if (pyo_objfile == nullptr) + { + gdbpy_print_stack (); + return {}; + } + + /* Lookup the helper function within the GDB module. */ + gdbpy_ref<> pyo_handler + (PyObject_GetAttrString (gdb_python_module, "_handle_missing_debuginfo")); + if (pyo_handler == nullptr) + { + gdbpy_print_stack (); + return {}; + } + + /* Call the function, passing in the Python objfile object. */ + gdbpy_ref<> pyo_execute_ret + (PyObject_CallFunctionObjArgs (pyo_handler.get (), pyo_objfile.get (), + nullptr)); + if (pyo_execute_ret == nullptr) + { + /* If the handler is cancelled due to a Ctrl-C, then propagate + the Ctrl-C as a GDB exception instead of swallowing it. */ + gdbpy_print_stack_or_quit (); + return {}; + } + + /* Parse the result, and convert it back to the C++ object. */ + if (pyo_execute_ret == Py_None) + return {}; + + if (PyBool_Check (pyo_execute_ret.get ())) + { + bool try_again = PyObject_IsTrue (pyo_execute_ret.get ()); + return ext_lang_missing_debuginfo_result (try_again); + } + + if (!gdbpy_is_string (pyo_execute_ret.get ())) + { + PyErr_SetString (PyExc_ValueError, + "return value from _handle_missing_debuginfo should " + "be None, a Bool, or a String"); + gdbpy_print_stack (); + return {}; + } + + gdb::unique_xmalloc_ptr filename + = python_string_to_host_string (pyo_execute_ret.get ()); + if (filename == nullptr) + { + gdbpy_print_stack (); + return {}; + } + + return ext_lang_missing_debuginfo_result (std::string (filename.get ())); +} + /* Compute the list of active python type printers and store them in EXT_PRINTERS->py_type_printers. The product of this function is used by gdbpy_apply_type_printers, and freed by gdbpy_free_type_printers. diff --git a/gdb/testsuite/gdb.python/py-missing-debug.c b/gdb/testsuite/gdb.python/py-missing-debug.c new file mode 100644 index 00000000000..8cbea3b6892 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-missing-debug.c @@ -0,0 +1,22 @@ +/* This test program is part of GDB, the GNU debugger. + + 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 . */ + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-missing-debug.exp b/gdb/testsuite/gdb.python/py-missing-debug.exp new file mode 100644 index 00000000000..3e0d70b9d22 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-missing-debug.exp @@ -0,0 +1,473 @@ +# Copyright (C) 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 . + +load_lib gdb-python.exp + +require allow_python_tests + +standard_testfile + +if {[build_executable "failed to prepare" ${testfile} ${srcfile}]} { + return -1 +} + +# Remove debug information from BINFILE and place it into +# BINFILE.debug. +if {[gdb_gnu_strip_debug $binfile]} { + unsupported "cannot produce separate debug info files" + return -1 +} + +set remote_python_file \ + [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + +set debug_filename ${binfile}.debug +set hidden_filename ${binfile}.hidden + +# Start GDB. +clean_restart + +# Some initial sanity checks; initially, we can find the debug information +# (this will use the .gnu_debuglink), then after we move the debug +# information, reload the executable, now the debug can't be found. +with_test_prefix "initial checks" { + # Load BINFILE, we should find the separate debug information. + gdb_file_cmd $binfile + gdb_assert {$gdb_file_cmd_debug_info == "debug"} \ + "debug info is found" + + # Rename the debug information file, re-load BINFILE, GDB should fail + # to find the debug information + remote_exec build "mv $debug_filename $hidden_filename" + gdb_file_cmd $binfile + gdb_assert {$gdb_file_cmd_debug_info == "nodebug"} \ + "debug info no longer found" +} + +# Load the Python script into GDB. +gdb_test "source $remote_python_file" "^Success" \ + "source python script" + +# Setup the separate debug info directory. This isn't actually needed until +# some of the later tests, but might as well get this done now. +set debug_directory [standard_output_file "debug-dir"] +remote_exec build "mkdir -p $debug_directory" +gdb_test_no_output "set debug-file-directory $debug_directory" \ + "set debug-file-directory" + +# Initially the missing debug handler we install is in a mode where it +# returns None, indicating that it can't help locate the debug information. +# Check this works as expected. +with_test_prefix "handler returning None" { + gdb_test_no_output \ + "python gdb.missing_debug.register_handler(None, handler_obj)" \ + "register the initial handler" + + gdb_file_cmd $binfile + gdb_assert {$gdb_file_cmd_debug_info == "nodebug"} \ + "debug info not found" + + # Check the handler was only called once. + gdb_test "python print(handler_obj.call_count)" "^1" \ + "check handler was only called once" +} + +# Now configure the handler to move the debug file back to the +# .gnu_debuglink location and then return True, this will cause GDB to +# recheck, at which point it should find the debug info. +with_test_prefix "handler in gnu_debuglink mode" { + gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_TRUE, \ + \"$hidden_filename\", \ + \"$debug_filename\")" \ + "confirgure handler" + gdb_file_cmd $binfile + gdb_assert {$gdb_file_cmd_debug_info == "debug"} "debug info found" + + # Check the handler was only called once. + gdb_test "python print(handler_obj.call_count)" "^1" \ + "check handler was only called once" +} + +# Setup a directory structure based on the build-id of BINFILE, but don't +# move the debug information into place just yet. +# +# Instead, configure the handler to move the debug info into the build-id +# directory. +# +# Reload BINFILE, at which point the handler will move the debug info into +# the build-id directory and return True, GDB will then recheck for the +# debug information, and should find it. +with_test_prefix "handler in build-id mode" { + # Move the debug file out of the way once more. + remote_exec build "mv $debug_filename $hidden_filename" + + # Create the build-id based directory in which the debug information + # will be placed. + set build_id_filename \ + $debug_directory/[build_id_debug_filename_get $binfile] + remote_exec build "mkdir -p [file dirname $build_id_filename]" + + # Configure the handler to move the debug info into the build-id dir. + gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_TRUE, \ + \"$hidden_filename\", \ + \"$build_id_filename\")" \ + "confirgure handler" + + # Reload the binary and check the debug information is found. + gdb_file_cmd $binfile + gdb_assert {$gdb_file_cmd_debug_info == "debug"} "debug info found" + + # Check the handler was only called once. + gdb_test "python print(handler_obj.call_count)" "^1" \ + "check handler was only called once" +} + +# Move the debug information back to a hidden location and configure the +# handler to return the filename of the hidden debug info location. GDB +# should immediately use this file as the debug information. +with_test_prefix "handler returning a string" { + remote_exec build "mv $build_id_filename $hidden_filename" + + # Configure the handler return a filename string. + gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_STRING, \ + \"$hidden_filename\")" \ + "confirgure handler" + + # Reload the binary and check the debug information is found. + gdb_file_cmd $binfile + gdb_assert {$gdb_file_cmd_debug_info == "debug"} "debug info found" + + # Check the handler was only called once. + gdb_test "python print(handler_obj.call_count)" "^1" \ + "check handler was only called once" +} + +# Register another global handler, this one raises an exception. Reload the +# debug information, the bad handler should be invoked first, which raises +# an excetption, at which point GDB should skip further Python handlers. +with_test_prefix "handler raises an exception" { + gdb_test_no_output \ + "python gdb.missing_debug.register_handler(None, rhandler)" + + foreach_with_prefix exception_type {gdb.GdbError TypeError} { + gdb_test_no_output \ + "python rhandler.exception_type = $exception_type" + + gdb_file_cmd $binfile + gdb_assert {$gdb_file_cmd_debug_info == "nodebug"} \ + "debug info not found" + + set re [string_to_regexp \ + "Python Exception : message"] + gdb_assert {[regexp $re $gdb_file_cmd_msg]} \ + "check for exception in file command output" + + # Our original handler is still registered, but should not have been + # called again (as the exception occurs first). + gdb_test "python print(handler_obj.call_count)" "^1" \ + "check good handler hasn't been called again" + } +} + +gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Global:" \ + " exception_handler" \ + " handler"] \ + "check both handlers are visible" + +# Re-start GDB. +clean_restart + +# Load the Python script into GDB. +gdb_test "source $remote_python_file" "^Success" \ + "source python script for bad handler name checks" + +# Attempt to register a missing-debug-handler with NAME. The expectation is +# that this should fail as NAME contains some invalid characters. +proc check_bad_name {name} { + set name_re [string_to_regexp $name] + set re \ + [multi_line \ + "ValueError: invalid character '.' in handler name: $name_re" \ + "Error while executing Python code\\."] + + gdb_test "python register(\"$name\")" $re \ + "check that '$name' is not accepted" +} + +# We don't attempt to be exhaustive here, just check a few random examples +# of invalid names. +check_bad_name "!! Bad Name" +check_bad_name "Bad Name" +check_bad_name "(Bad Name)" +check_bad_name "Bad \[Name\]" +check_bad_name "Bad,Name" +check_bad_name "Bad;Name" + +# Check that there are no handlers registered. +gdb_test_no_output "info missing-debug-handlers" \ + "check no handlers are registered" + +# Check we can use the enable/disable commands where there are no handlers +# registered. +gdb_test "enable missing-debug-handler foo" \ + "^0 missing debug handlers enabled" +gdb_test "disable missing-debug-handler foo" \ + "^0 missing debug handlers disabled" + +# Grab the current program space object, used for registering handler later. +gdb_test_no_output "python pspace = gdb.selected_inferior().progspace" + +# Now register some handlers. +foreach hspec {{\"Foo\" None} + {\"-bar\" None} + {\"baz-\" pspace} + {\"abc-def\" pspace}} { + lassign $hspec name locus + gdb_test "python register($name, $locus)" +} + +with_test_prefix "all handlers enabled" { + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Current Progspace:" \ + " abc-def" \ + " baz-" \ + "Global:" \ + " -bar" \ + " Foo"] + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {['abc-def', 'baz-', '-bar', 'Foo']}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +with_test_prefix "disable 'baz-'" { + gdb_test "disable missing-debug-handler progspace baz-" \ + "^1 missing debug handler disabled" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " abc-def" \ + " baz- \\\[disabled\\\]" \ + "Global:" \ + " -bar" \ + " Foo"] + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {['abc-def', '-bar', 'Foo']}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +with_test_prefix "disable 'Foo'" { + gdb_test "disable missing-debug-handler .* Foo" \ + "^1 missing debug handler disabled" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " abc-def" \ + " baz- \\\[disabled\\\]" \ + "Global:" \ + " -bar" \ + " Foo \\\[disabled\\\]"] + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {['abc-def', '-bar']}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +with_test_prefix "disable everything" { + gdb_test "disable missing-debug-handler .* .*" \ + "^2 missing debug handlers disabled" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " abc-def \\\[disabled\\\]" \ + " baz- \\\[disabled\\\]" \ + "Global:" \ + " -bar \\\[disabled\\\]" \ + " Foo \\\[disabled\\\]"] + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {[]}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +with_test_prefix "enable 'abc-def'" { + set re [string_to_regexp $binfile] + + gdb_test "enable missing-debug-handler \"$re\" abc-def" \ + "^1 missing debug handler enabled" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " abc-def" \ + " baz- \\\[disabled\\\]" \ + "Global:" \ + " -bar \\\[disabled\\\]" \ + " Foo \\\[disabled\\\]"] + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {['abc-def']}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +with_test_prefix "enable global handlers" { + set re [string_to_regexp $binfile] + + gdb_test "enable missing-debug-handler global" \ + "^2 missing debug handlers enabled" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " abc-def" \ + " baz- \\\[disabled\\\]" \ + "Global:" \ + " -bar" \ + " Foo"] + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {['abc-def', '-bar', 'Foo']}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +# Add handler_obj to the global handler list, and configure it to +# return False. We should call all of the program space specific +# handlers (which return None), and then call handler_obj from the +# global list, which returns False, at which point we shouldn't call +# anyone else. +with_test_prefix "return False handler in progspace list" { + gdb_test "enable missing-debug-handler progspace" \ + "^1 missing debug handler enabled" + + gdb_test_no_output \ + "python gdb.missing_debug.register_handler(None, handler_obj)" \ + "register the initial handler" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " abc-def" \ + " baz-" \ + "Global:" \ + " handler" \ + " -bar" \ + " Foo"] + + gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_FALSE)" \ + "confirgure handler" + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {['abc-def', 'baz-', 'handler']}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +# Now add handler_obj to the current program space's handler list. We +# use the same handler object here, that's fine. We should only see a +# call to the first handler object in the call log. +with_test_prefix "return False handler in global list" { + gdb_test_no_output \ + "python gdb.missing_debug.register_handler(pspace, handler_obj)" \ + "register the initial handler" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " handler" \ + " abc-def" \ + " baz-" \ + "Global:" \ + " handler" \ + " -bar" \ + " Foo"] + + gdb_file_cmd $binfile + gdb_test "python print(handler_call_log)" \ + [string_to_regexp {['handler']}] + gdb_test_no_output "python handler_call_log = \[\]" \ + "reset call log" +} + +with_test_prefix "check handler replacement" { + # First, check we can have the same name appear in both program + # space and global lists without giving an error. + gdb_test_no_output "python register(\"Foo\", pspace)" + + gdb_test "info missing-debug-handlers" \ + [multi_line \ + "Progspace \[^\r\n\]+:" \ + " Foo" \ + " handler" \ + " abc-def" \ + " baz-" \ + "Global:" \ + " handler" \ + " -bar" \ + " Foo"] + + # Now check that we get an error if we try to add a handler with + # the same name. + gdb_test "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"))" \ + [multi_line \ + "RuntimeError: Handler Foo already exists\\." \ + "Error while executing Python code\\."] + + gdb_test "python gdb.missing_debug.register_handler(handler=log_handler(\"Foo\"), locus=pspace)" \ + [multi_line \ + "RuntimeError: Handler Foo already exists\\." \ + "Error while executing Python code\\."] + + # And now try again, but this time with 'replace=True', we + # shouldn't get an error in this case. + gdb_test_no_output \ + "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"), replace=True)" + + gdb_test_no_output \ + "python gdb.missing_debug.register_handler(handler=log_handler(\"Foo\"), locus=None, replace=True)" + + # Now disable a handler and check we still need to use 'replace=True'. + gdb_test "disable missing-debug-handler progspace Foo" \ + "^1 missing debug handler disabled" + + gdb_test "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"))" \ + [multi_line \ + "RuntimeError: Handler Foo already exists\\." \ + "Error while executing Python code\\."] \ + "still get an error when handler is disabled" + + gdb_test_no_output \ + "python gdb.missing_debug.register_handler(pspace, log_handler(\"Foo\"), replace=True)" \ + "can replace a disabled handler" +} diff --git a/gdb/testsuite/gdb.python/py-missing-debug.py b/gdb/testsuite/gdb.python/py-missing-debug.py new file mode 100644 index 00000000000..720648e18f0 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-missing-debug.py @@ -0,0 +1,120 @@ +# Copyright (C) 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 +from gdb.missing_debug import MissingDebugHandler +from enum import Enum +import os + +# A global log that is filled in by instances of the LOG_HANDLER class +# when they are called. +handler_call_log = [] + + +class Mode(Enum): + RETURN_NONE = 0 + RETURN_TRUE = 1 + RETURN_FALSE = 2 + RETURN_STRING = 3 + + +class handler(MissingDebugHandler): + def __init__(self): + super().__init__("handler") + self._call_count = 0 + self._mode = Mode.RETURN_NONE + + def __call__(self, objfile): + global handler_call_log + handler_call_log.append(self.name) + self._call_count += 1 + if self._mode == Mode.RETURN_NONE: + return None + + if self._mode == Mode.RETURN_TRUE: + os.rename(self._src, self._dest) + return True + + if self._mode == Mode.RETURN_FALSE: + return False + + if self._mode == Mode.RETURN_STRING: + return self._dest + + assert False + + @property + def call_count(self): + """Return a count, the number of calls to __call__ since the last + call to set_mode. + """ + return self._call_count + + def set_mode(self, mode, *args): + self._call_count = 0 + self._mode = mode + + if mode == Mode.RETURN_NONE: + assert len(args) == 0 + return + + if mode == Mode.RETURN_TRUE: + assert len(args) == 2 + self._src = args[0] + self._dest = args[1] + return + + if mode == Mode.RETURN_FALSE: + assert len(args) == 0 + return + + if mode == Mode.RETURN_STRING: + assert len(args) == 1 + self._dest = args[0] + return + + assert False + + +class exception_handler(MissingDebugHandler): + def __init__(self): + super().__init__("exception_handler") + self.exception_type = None + + def __call__(self, objfile): + global handler_call_log + handler_call_log.append(self.name) + assert self.exception_type is not None + raise self.exception_type("message") + + +class log_handler(MissingDebugHandler): + def __call__(self, objfile): + global handler_call_log + handler_call_log.append(self.name) + return None + + +# A basic helper function, this keeps lines shorter in the TCL script. +def register(name, locus=None): + gdb.missing_debug.register_handler(locus, log_handler(name)) + + +# Create instances of the handlers, but don't install any. We install +# these as needed from the TCL script. +rhandler = exception_handler() +handler_obj = handler() + +print("Success")