From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id +azQNOh2fGqsliAAWB0awg (envelope-from ) for ; Wed, 12 Aug 2026 09:36:40 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=il+pPX52; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id D24811E166; Wed, 12 Aug 2026 09:36:40 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-6.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id D099D1E033 for ; Wed, 12 Aug 2026 09:36:39 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 711144BAE7D9 for ; Wed, 12 Aug 2026 13:36:39 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 711144BAE7D9 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=il+pPX52 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) by sourceware.org (Postfix) with ESMTPS id 1C9A94BB3B94 for ; Wed, 12 Aug 2026 13:29:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1C9A94BB3B94 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 1C9A94BB3B94 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=192.198.163.9 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1786541367; cv=none; b=YuoLRHRo84qNK1HHSzpJcHNsHC5aZfSk+WoLmhZQAhHH8ixVEJhGT6nAFxODV8O7HaKzzWoOCpXWg0C74Y7DAIZ8zQsc/RLgm4O15YiX7eHGUiRIVpVhMiBz6lC2PhLZ4+OwyDGXUo2jvdCqWv5GxFGb/1ohf6+CSheJFDLc8UI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1786541367; c=relaxed/simple; bh=utJ1E93RkWAU26fD+YWdWfz59azdhkPJXeg8VuL5OJo=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=BU/akAZitsKzpYrcQd8/mCLiZg/nwRCE2D33VBSE+dGW2p8gu4e9m477pJcv198vxcNbv/z+9w377Lknhrlknc7IvsJ76GjHpNlxZU46rxApBdrnU+LasJhM60I4o6NutwjYrlkHJZ5m223P66jCJX8uZE9yE+QRQvdBRHSUkrQ= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=il+pPX52 DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1C9A94BB3B94 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1786541367; x=1818077367; h=from:to:subject:date:message-id:in-reply-to:references: mime-version:content-transfer-encoding; bh=utJ1E93RkWAU26fD+YWdWfz59azdhkPJXeg8VuL5OJo=; b=il+pPX52NXEQIpwhcSVAgMtbJw1rpReTNcOXYS3QNS2wutKO2nCL2d89 780QuNTeza5lrHpjoKydXRiD2IUyhOYfvQFDQ/jKtbp5D5Yzy4G7hzLRL RYslnN+hpBmWHyu05DKj0vPiK/J+vsNbrFSdCggUago91xW4Ob78GbrpJ iVqjtGEGu/QRJPq7W6Ny90ugFTgYXexpl6DZHOp7/3qZDRqRinVeBq6bR JusOxsHfGYlKUoKmRcp+oEclMlR6EIxJc1mEG7lZ99//HFDg/6KpIn9n/ 8w6EEosWYohJ/K355Uz2JCJ67IkhaGBgW4bgDEc826OQ/fypeE7Cx5ZF3 A==; X-CSE-ConnectionGUID: OurvKi79Tqa1aUlaqRUDzA== X-CSE-MsgGUID: g651bqlNRFypTdOma7HhkQ== X-IronPort-AV: E=McAfee;i="6800,10657,11872"; a="97757878" X-IronPort-AV: E=Sophos;i="6.25,219,1779174000"; d="scan'208";a="97757878" Received: from fmviesa010.fm.intel.com ([10.60.135.150]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Aug 2026 06:29:27 -0700 X-CSE-ConnectionGUID: KSaSALdYS3m6Avl9fp/GmQ== X-CSE-MsgGUID: V8RH8ipsQVWX1NUFbTYoQw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.25,219,1779174000"; d="scan'208";a="259817935" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by fmviesa010-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 12 Aug 2026 06:29:24 -0700 From: Markus Metzger To: gdb-patches@sourceware.org Subject: [PATCH v4 25/44] gdb: allow continuing an inferior without threads Date: Wed, 12 Aug 2026 15:27:45 +0200 Message-ID: <20260812132805.380163-26-markus.t.metzger@intel.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260812132805.380163-1-markus.t.metzger@intel.com> References: <20260812132805.380163-1-markus.t.metzger@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 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~public-inbox=simark.ca@sourceware.org When debugging an Intel GPU device, we may end up with a GPU inferior but no threads, e.g. if we interrupted the device inferior and there was no work dispatched onto that device. We want to be able to continue the inferior in all-stop mode and go back to waiting for events. With schedule-multiple, we would not only continue the current inferior, but other inferiors that we also stopped, as well. When debugging the host process, one might argue that the user should switch to a host thread before continuing. It's a matter of convenience to not require this additional switch. When debugging only GPU devices, however, there is no host thread to switch to. To support that use-case, we have to allow continuing the device inferior even though no thread is selected. There may not be any threads at all at the moment. --- gdb/infcmd.c | 21 +++++-- gdb/infrun.c | 159 +++++++++++++++++++++++++++++---------------------- 2 files changed, 107 insertions(+), 73 deletions(-) diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 3da5a1366aa..43126c5ff35 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -722,6 +722,9 @@ error_is_running (void) static void ensure_not_running (void) { + if (inferior_ptid == null_ptid) + return; + if (inferior_thread ()->state () == THREAD_RUNNING) error_is_running (); } @@ -787,7 +790,9 @@ continue_1 (bool all_threads_p) } else { - ensure_valid_thread (); + /* Allow continuing inferiors without threads. */ + if (inferior_ptid != null_ptid) + ensure_valid_thread (); ensure_not_running (); clear_proceed_status (0); proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT); @@ -836,7 +841,7 @@ continue_command (const char *args, int from_tty) struct thread_info *tp; if (non_stop) - tp = inferior_thread (); + tp = (inferior_ptid == null_ptid ? nullptr : inferior_thread ()); else { process_stratum_target *last_target; @@ -870,13 +875,21 @@ continue_command (const char *args, int from_tty) ensure_not_tfind_mode (); + /* Switch away from an exited thread if that is the last thread in the + inferior to allow resuming that inferior. */ + inferior* inf = current_inferior (); + if (any_live_thread_of_inferior (inf) == nullptr) + switch_to_inferior_no_thread (inf); + if (!non_stop || !all_threads_p) { - ensure_valid_thread (); + /* Allow continuing inferiors without threads. */ + if (inferior_ptid != null_ptid) + ensure_valid_thread (); ensure_not_running (); } - prepare_execution_command (current_inferior ()->top_target (), async_exec); + prepare_execution_command (inf->top_target (), async_exec); if (from_tty) gdb_printf (_("Continuing.\n")); diff --git a/gdb/infrun.c b/gdb/infrun.c index ce7c376af14..d181d20ba83 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3249,6 +3249,9 @@ thread_still_needs_step_over (struct thread_info *tp) static bool schedlock_applies (struct thread_info *tp) { + if (tp == nullptr) + return (scheduler_mode == schedlock_on); + return (scheduler_mode == schedlock_on || (scheduler_mode == schedlock_step && tp->control.stepping_command) @@ -3645,15 +3648,12 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) { INFRUN_SCOPED_DEBUG_ENTER_EXIT; - struct gdbarch *gdbarch; - CORE_ADDR pc; - /* If we're stopped at a fork/vfork, switch to either the parent or child thread as defined by the "set follow-fork-mode" command, or, if both the parent and child are controlled by GDB, and schedule-multiple is on, follow the child. If none of the above apply then we just proceed resuming the current thread. */ - if (!follow_fork ()) + if ((inferior_ptid != null_ptid) && !follow_fork ()) { /* The target for some reason decided not to resume. */ normal_stop (); @@ -3665,56 +3665,69 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) /* We'll update this if & when we switch to a new thread. */ update_previous_thread (); - thread_info *cur_thr = inferior_thread (); - infrun_debug_printf ("cur_thr = %s", cur_thr->ptid.to_string ().c_str ()); + thread_info *cur_thr = nullptr; + regcache *regcache = nullptr; + gdbarch *gdbarch = nullptr; + CORE_ADDR pc = 0; + int step = 0; - regcache *regcache = get_thread_regcache (cur_thr); - gdbarch = regcache->arch (); - pc = regcache_read_pc_protected (regcache); + if (inferior_ptid != null_ptid) + { + cur_thr = inferior_thread (); + infrun_debug_printf ("cur_thr = %s", + cur_thr->ptid.to_string ().c_str ()); - /* Fill in with reasonable starting values. */ - init_thread_stepping_state (cur_thr); + regcache = get_thread_regcache (cur_thr); + gdbarch = regcache->arch (); + pc = regcache_read_pc_protected (regcache); - gdb_assert (!thread_is_in_step_over_chain (cur_thr)); + /* Fill in with reasonable starting values. */ + init_thread_stepping_state (cur_thr); + step = cur_thr->control.stepping_command; + + gdb_assert (!thread_is_in_step_over_chain (cur_thr)); + } - ptid_t resume_ptid - = user_visible_resume_ptid (cur_thr->control.stepping_command); + ptid_t resume_ptid = user_visible_resume_ptid (step); process_stratum_target *resume_target = user_visible_resume_target (resume_ptid); check_multi_target_resumption (resume_target); - if (addr == (CORE_ADDR) -1) - { - const address_space *aspace = cur_thr->inf->aspace.get (); - - if (cur_thr->stop_pc_p () - && pc == cur_thr->stop_pc () - && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here - && execution_direction != EXEC_REVERSE) - /* There is a breakpoint at the address we will resume at, - step one instruction before inserting breakpoints so that - we do not stop right away (and report a second hit at this - breakpoint). - - Note, we don't do this in reverse, because we won't - actually be executing the breakpoint insn anyway. - We'll be (un-)executing the previous instruction. */ - cur_thr->stepping_over_breakpoint = 1; - else if (gdbarch_single_step_through_delay_p (gdbarch) - && gdbarch_single_step_through_delay (gdbarch, - get_current_frame ())) - /* We stepped onto an instruction that needs to be stepped - again before re-inserting the breakpoint, do so. */ - cur_thr->stepping_over_breakpoint = 1; - } - else + if (inferior_ptid != null_ptid) { - regcache_write_pc (regcache, addr); - } + if (addr == (CORE_ADDR) -1) + { + const address_space *aspace = cur_thr->inf->aspace.get (); + + if (cur_thr->stop_pc_p () + && pc == cur_thr->stop_pc () + && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here + && execution_direction != EXEC_REVERSE) + /* There is a breakpoint at the address we will resume at, + step one instruction before inserting breakpoints so that + we do not stop right away (and report a second hit at this + breakpoint). + + Note, we don't do this in reverse, because we won't + actually be executing the breakpoint insn anyway. + We'll be (un-)executing the previous instruction. */ + cur_thr->stepping_over_breakpoint = 1; + else if (gdbarch_single_step_through_delay_p (gdbarch) + && gdbarch_single_step_through_delay (gdbarch, + get_current_frame ())) + /* We stepped onto an instruction that needs to be stepped + again before re-inserting the breakpoint, do so. */ + cur_thr->stepping_over_breakpoint = 1; + } + else + { + regcache_write_pc (regcache, addr); + } - if (siggnal != GDB_SIGNAL_DEFAULT) - cur_thr->set_stop_signal (siggnal); + if (siggnal != GDB_SIGNAL_DEFAULT) + cur_thr->set_stop_signal (siggnal); + } /* If an exception is thrown from this point on, make sure to propagate GDB's knowledge of the executing state to the @@ -3724,11 +3737,15 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) /* Even if RESUME_PTID is a wildcard, and we end up resuming fewer threads (e.g., we might need to set threads stepping over breakpoints first), from the user/frontend's point of view, all - threads in RESUME_PTID are now running. */ - set_state (resume_target, resume_ptid, THREAD_RUNNING); + threads in RESUME_PTID are now running. + + Note that RESUME_PTID may be NULL_PTID when resuming an inferior that + (currently) has no threads. */ + if (resume_ptid != null_ptid) + set_state (resume_target, resume_ptid, THREAD_RUNNING); infrun_debug_printf ("addr=%s, signal=%s, resume_ptid=%s", - paddress (gdbarch, addr), + hex_string (addr), gdb_signal_to_symbol_string (siggnal), resume_ptid.to_string ().c_str ()); @@ -3744,6 +3761,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) Ctrl-C from within target_pass_ctrlc). */ target_terminal::inferior (); + /* We don't want the user to see a different selected thread. */ + scoped_restore_current_thread restore_thread; + /* In a multi-threaded task we may select another thread and then continue or step. @@ -3757,8 +3777,10 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) /* If scheduler locking applies, we can avoid iterating over all threads. */ - if (!non_stop && !schedlock_applies (cur_thr)) + if (resume_ptid != null_ptid && !non_stop && !schedlock_applies (cur_thr)) { + scoped_restore_current_thread restore; + for (thread_info &tp : all_non_exited_threads (resume_target, resume_ptid)) { @@ -3779,21 +3801,22 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) global_thread_step_over_chain_enqueue (&tp); } - - switch_to_thread (cur_thr); } - /* Enqueue the current thread last, so that we move all other - threads over their breakpoints first. */ - if (cur_thr->stepping_over_breakpoint) - global_thread_step_over_chain_enqueue (cur_thr); + if (cur_thr != nullptr) + { + /* Enqueue the current thread last, so that we move all other + threads over their breakpoints first. */ + if (cur_thr->stepping_over_breakpoint) + global_thread_step_over_chain_enqueue (cur_thr); - /* If the thread isn't started, we'll still need to set its prev_pc, - so that switch_back_to_stepped_thread knows the thread hasn't - advanced. Must do this before resuming any thread, as in - all-stop/remote, once we resume we can't send any other packet - until the target stops again. */ - cur_thr->prev_pc = regcache_read_pc_protected (regcache); + /* If the thread isn't started, we'll still need to set its prev_pc, + so that switch_back_to_stepped_thread knows the thread hasn't + advanced. Must do this before resuming any thread, as in + all-stop/remote, once we resume we can't send any other packet + until the target stops again. */ + cur_thr->prev_pc = regcache_read_pc_protected (regcache); + } { scoped_disable_commit_resumed disable_commit_resumed ("proceeding"); @@ -3817,14 +3840,17 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) /* In all-stop, but the target is always in non-stop mode. Start all other threads that are implicitly resumed too. */ - for (thread_info &tp : all_non_exited_threads (resume_target, - resume_ptid)) + if (resume_ptid != null_ptid) { - switch_to_thread_no_regs (&tp); - proceed_resume_thread_checked (&tp); + for (thread_info &tp : all_non_exited_threads (resume_target, + resume_ptid)) + { + switch_to_thread_no_regs (&tp); + proceed_resume_thread_checked (&tp); + } } } - else + else if (cur_thr != nullptr) proceed_resume_thread_checked (cur_thr); disable_commit_resumed.reset_and_commit (); @@ -3832,11 +3858,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) finish_state.release (); - /* If we've switched threads above, switch back to the previously - current thread. We don't want the user to see a different - selected thread. */ - switch_to_thread (cur_thr); - /* Tell the event loop to wait for it to stop. If the target supports asynchronous execution, it'll do this from within target_resume. */ -- 2.43.0 ________________________________________ Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 (89) 99143-0 www.intel.de Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman Chairperson of the Supervisory Board: Sonja Pierer Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928 This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.