From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id QUIlJrSXymn4ij8AWB0awg (envelope-from ) for ; Mon, 30 Mar 2026 11:33:08 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=X+v+ST32; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 9471F1E0BC; Mon, 30 Mar 2026 11:33:08 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.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,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.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 692CC1E04F for ; Mon, 30 Mar 2026 11:33:07 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id D3E5B4B920A3 for ; Mon, 30 Mar 2026 15:33:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D3E5B4B920A3 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=X+v+ST32 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id D53114BA2E39 for ; Mon, 30 Mar 2026 15:31:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D53114BA2E39 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine 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 D53114BA2E39 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=1774884669; cv=none; b=uvRWe4jvWXU6fLNcjrhmPujNWY3SXAl1jE7LujTRLisPOLZ86/6LvqG6C5OZ3mKC8I/SHVHNoN1A1zlKFgpDhGqGnOGVo04VlKUBGO7+6/RU3l2nwalnk3ggIpc85bM3pWXvQdSKQ+SUBuquKRxXG1VeT+/sGx0ThWdng2Zkcgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774884669; c=relaxed/simple; bh=R8a9VdkFfzOZeR8hZopNmUSaIld3aTLDCpbsGcDenfk=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=hkqmf1vp63KXETnBK7wXaf8WA+6tMl8SKH95375CGXdhiBVMW19J4ODSJOtiN7JAkcwsXl4fxmkoTVr5z+aTd/snCXdHCqk1o+htCDzLibW2TjQyx4kqAx6S1z0nW/zer+jZa5yFbmmNIfrsv3Uta+54gYa4IjkSMaj4V6YeFy8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D53114BA2E39 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774884668; 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=NqJ4zVXTf60UwDAkrVVP8kYCJ18FXdapxa6+ui6JzpA=; b=X+v+ST32Y1x8q8hZwAX9iynKh2qnpueD5eEyv92vLrmNhfB/Ys1UcYHo1BUozyjvl4WGJk bVoKS6VsXb9fLnBOA3YvVT06gQBQoJL0OWAfefOuRZH95sAgunjass4sL4ap0hj/SbYQXK AzJHVo5/jmGszRA0mFbgcW2FQspce80= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-352-PKkF1BrxNVWQYeLUofz8fg-1; Mon, 30 Mar 2026 11:31:06 -0400 X-MC-Unique: PKkF1BrxNVWQYeLUofz8fg-1 X-Mimecast-MFC-AGG-ID: PKkF1BrxNVWQYeLUofz8fg_1774884665 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-485397788b3so48648075e9.2 for ; Mon, 30 Mar 2026 08:31:06 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774884665; x=1775489465; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=NqJ4zVXTf60UwDAkrVVP8kYCJ18FXdapxa6+ui6JzpA=; b=CPaZP2Dq5Mj+HAGAV0EWOWhmIAh+cHpIluz0kdHQUEJYB9gyMXiwT7Wlid0KDTvVJF jo9IP95cRKJnoMvT9Qr5KgRwkarz78l/GMhfwdzFgwK4L9UfMaXogpTpdKKTOBI1pv+f OqTiBl5rgTZ+lpbbm0aIN1vaO9P3R11eGWdnsjGC3M1mk0w/ClP8kvNmOdOyhhgQjzpe L+Vy9rludTgm01tFEaEHy1AvL1/Puxwp8P+VrOiWLnKPcAZA69FwFLIjLctVRHL6VWFv Pec+70VrGmdYp4ZtxOzRV+3PCCOmaEfuk2sVF3jJ5tl2LiHgTGA44ayQbw+QPqypjGMn ZKSw== X-Gm-Message-State: AOJu0YxOVHBDeyaPd1M5sk9muJNmXwEr33xmqnuT6zaMKOl4gwVebv3f nQw1zqW86jBJDcvMjhK3uePPcsapLMsjF4XA8XT/6ZKpSuOVjHbKn3VZZia01bYU+lJWEMm+ivK zioSUk48cREJdGAZmwmRH88q3HumOYTwIYTWzCY8O8oU6Sw/3mP36Njx3C1Ys5ZY5+E8BnafV1x 8uIBJo8pg6c/qS1EO4QzvPVZ55QAVOFYHw+pueIrIee3kGkS8= X-Gm-Gg: ATEYQzxz9C3PSx4whhn6upPDrXb3JpXAMxKDrGcdUJGQWg8EXCuk38gG6ksfQgmaDj3 LTE1uU6UbMzWWeOLcxp7d6Cd2WdooWksK8AzlfBygob9HWbXb5V6gOlGkUO6O8O5y6cX883BS9R 0na4XiDyjNxzsASwdG4H6Z4CFfUBbP5KeN6G+0x5mfgJKvcegmr5VvSC+wf3EViIP+LH+bKj+ug hX7s99M5vNnM6VBqIX+cOMqJKXtmsG2wfHBUcofuPaCK7yNXDjf0us2G7C9ViW1Zrb3bVXpqYH6 Zvsl5+wnMAq4ARfRW7AeOAJ/dgOf4rQwT2HXhdoJrBH/wPyBPoA+wk7Lm/URfRb3mioIlxlS/5C OXRmPiEBkEO5BYkO6 X-Received: by 2002:a05:600c:3505:b0:485:4135:5c92 with SMTP id 5b1f17b1804b1-48727c7c6e0mr191528485e9.0.1774884664522; Mon, 30 Mar 2026 08:31:04 -0700 (PDT) X-Received: by 2002:a05:600c:3505:b0:485:4135:5c92 with SMTP id 5b1f17b1804b1-48727c7c6e0mr191527475e9.0.1774884663724; Mon, 30 Mar 2026 08:31:03 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48722c95041sm266116525e9.6.2026.03.30.08.31.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Mar 2026 08:31:02 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 3/3] gdb/python: new events.corefile_changed event Date: Mon, 30 Mar 2026 16:30:53 +0100 Message-Id: <7e010b61a2a9d995765011de53f3036c0b1b2b60.1774884529.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-MFC-PROC-ID: eGyZw9pBRuFnAhvhQEooNEIK3-3UkVhhfpnVpwYS2wc_1774884665 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true 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 Add a new Python event registry, events.corefile_changed. This event is emitted each time the corefile within an inferior changes. The event object has a single 'inferior' attribute which is the gdb.Inferior object for which the core file changed. The user can then inspect Inferior.corefile to see details about the new core file, or this will be None if the core file was removed from the inferior. I've updated the existing test to cover this new event. The new test covers both the corefile_changed event, but also monitors the exited event. This ties in to the work done in the previous commit where we use whether the inferior has exited or not as a guard for whether core_target::exit_core_file_inferior should be called. Unloading a core file should result in a single corefile_changed event and a single exited event. --- gdb/NEWS | 5 + gdb/doc/python.texi | 11 ++ gdb/python/py-all-events.def | 1 + gdb/python/py-corefile.c | 36 ++++++ gdb/python/py-event-types.def | 5 + gdb/testsuite/gdb.python/py-corefile.exp | 140 ++++++++++++++++++++--- gdb/testsuite/gdb.python/py-corefile.py | 117 +++++++++++++++++++ 7 files changed, 296 insertions(+), 19 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 4cf91053c95..225ce15df16 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -238,6 +238,11 @@ qExecAndArgs a tuple of pairs each representing a single range. Contiguous blocks have only one range. + ** New event registry gdb.events.corefile_changed, which emits a + CorefileChangedEvent whenever the core file associated with an + inferior changes. The event has an 'inferior' attribute which is + the gdb.Inferior in which the core file has changed. + * Guile API ** Procedures 'memory-port-read-buffer-size', diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index e1e983726e8..6c0b3310c7f 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -4147,6 +4147,17 @@ Events In Python filename will have changed, but the symbol filename might still hold its previous value. +@item events.corefile_changed +Emits @code{gdb.CorefileChangedEvent} which indicates that the core +file associated with a @code{gdb.Inferior} has changed, either a new +core file has been loaded, or the existing core file has been +unloaded (@pxref{Core Files In Python}). + +@defvar CorefileChangedEvent.inferior +The @code{gdb.Inferior} in which the corefile has changed +(@pxref{Inferiors In Python}). +@end defvar + @item events.new_progspace This is emitted when @value{GDBN} adds a new program space (@pxref{Progspaces In Python,,Program Spaces In Python}). The event diff --git a/gdb/python/py-all-events.def b/gdb/python/py-all-events.def index 24724038562..3711cf29287 100644 --- a/gdb/python/py-all-events.def +++ b/gdb/python/py-all-events.def @@ -47,3 +47,4 @@ GDB_PY_DEFINE_EVENT(new_progspace) GDB_PY_DEFINE_EVENT(free_progspace) GDB_PY_DEFINE_EVENT(tui_enabled) GDB_PY_DEFINE_EVENT(selected_context) +GDB_PY_DEFINE_EVENT(corefile_changed) diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c index d35838c7523..4da70d631a4 100644 --- a/gdb/python/py-corefile.c +++ b/gdb/python/py-corefile.c @@ -23,6 +23,7 @@ #include "inferior.h" #include "gdbcore.h" #include "gdbsupport/rsp-low.h" +#include "py-event.h" /* A gdb.Corefile object. */ @@ -320,13 +321,48 @@ cfpy_mapped_files (PyObject *self, PyObject *args) return obj->mapped_files; } +/* Emit a CorefileChangedEvent event to REGISTRY. Return 0 on success, + or a negative value on error. INF is the inferior in which the core + file changed. */ + +static int +emit_corefile_changed_event (eventregistry_object *registry, inferior *inf) +{ + /* If there are no listeners then we are done. */ + if (evregpy_no_listeners_p (gdb_py_events.corefile_changed)) + return 0; + + gdbpy_ref<> event_obj + = create_event_object (&corefile_changed_event_object_type); + if (event_obj == nullptr) + return -1; + + gdbpy_ref inf_obj = inferior_to_inferior_object (inf); + if (inf_obj == nullptr + || evpy_add_attribute (event_obj.get (), "inferior", + inf_obj.get ()) < 0) + return -1; + + return evpy_emit_event (event_obj.get (), registry); +} + /* Callback from gdb::observers::core_file_changed. The core file in PSPACE has been changed. */ static void cfpy_corefile_changed (inferior *inf) { + /* It's safe to do this even if Python is not initialized, but there + should be nothing to clear in that case. */ cfpy_inferior_corefile_data_key.clear (inf); + + if (!gdb_python_initialized) + return; + + gdbpy_enter enter_py; + + if (emit_corefile_changed_event (gdb_py_events.corefile_changed, inf) < 0) + gdbpy_print_stack (); } /* Called when a gdb.Corefile is destroyed. */ diff --git a/gdb/python/py-event-types.def b/gdb/python/py-event-types.def index fe3e0978a55..7dddcc156ec 100644 --- a/gdb/python/py-event-types.def +++ b/gdb/python/py-event-types.def @@ -150,3 +150,8 @@ GDB_PY_DEFINE_EVENT_TYPE (selected_context, "SelectedContextEvent", "GDB user selected context event object", event_object_type); + +GDB_PY_DEFINE_EVENT_TYPE (corefile_changed, + "CorefileChangedEvent", + "GDB corefile changed event", + event_object_type); diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp index ecbff30cf7c..1b7a6458133 100644 --- a/gdb/testsuite/gdb.python/py-corefile.exp +++ b/gdb/testsuite/gdb.python/py-corefile.exp @@ -17,6 +17,7 @@ # support in Python. require isnative +require {!is_remote host} load_lib gdb-python.exp @@ -28,17 +29,93 @@ if {[build_executable "build executable" $testfile $srcfile] == -1} { return } +set remote_python_file \ + [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + set corefile [core_find $binfile] if {$corefile == ""} { unsupported "couldn't create or find corefile" return } +# Helper proc to run the 'core-file' command. Takes optional arguments: +# +# -corefile FILENAME : Load FILENAME as the new core file. If this +# argument is not given then the current core +# file will be unloaded. +# +# -inferior NUM : The inferior in which the corefile is being changed. +# This is used to match the corefile_changed events +# that will be emitted. +# +# -prefix STRING : A test prefix, to make test names unique. +proc core_file_cmd { args } { + parse_some_args { + {corefile ""} + {inferior 1} + {prefix ""} + } + + if { $prefix eq "" } { + if { $corefile eq "" } { + set prefix "unload corefile" + } else { + set prefix "load corefile" + } + } + + with_test_prefix $prefix { + gdb_test "events corefile_changed check" \ + "^No corefile_changed event has been seen\\." \ + "no corefile event has been seen" + + gdb_test "events exited check" \ + "^No exited event has been seen\\." \ + "no exited event has been seen" + + if { $corefile eq "" } { + gdb_test "core-file" "^No core file now\\." "unload current core file" + + gdb_test "events corefile_changed check" \ + "Event 1/1, Inferior $inferior, Corefile None" \ + "expected corefile event has been seen" + + gdb_test "events exited check" \ + "Event 1/1, Inferior $inferior, Exit Code None" \ + "expected exited event has been seen" + } else { + gdb_test "core-file $corefile" ".*" \ + "load core file" + + gdb_test "events corefile_changed check" \ + "Event 1/1, Inferior $inferior, Corefile [string_to_regexp $corefile]" \ + "expected corefile event has been seen" + + gdb_test "events exited check" \ + "^No exited event has been seen\\." \ + "no exited event was emitted" + } + } + + gdb_test_no_output -nopass "events corefile_changed reset" + gdb_test_no_output -nopass "events exited reset" +} + +# A helper proc runs clean_restart passing through ARGS, and then loads the +# test's Python script. +proc clean_restart_and_load_py_script { args } { + clean_restart {*}$args + + # Load the Python script into GDB. + gdb_test "source $::remote_python_file" "^Success" \ + "source python script" +} + # Create a copy of the corefile. set other_corefile [standard_output_file ${testfile}-other.core] remote_exec build "cp $corefile $other_corefile" -clean_restart +clean_restart_and_load_py_script gdb_test_no_output "python inf = gdb.selected_inferior()" \ "capture current inferior" @@ -46,8 +123,7 @@ gdb_test_no_output "python inf = gdb.selected_inferior()" \ gdb_test "python print(inf.corefile)" "^None" \ "Inferior.corefile is None before loading a core file" -gdb_test "core-file $corefile" ".*" \ - "load core file" +core_file_cmd -corefile $corefile set file_re [string_to_regexp $corefile] gdb_test "python print(inf.corefile)" "^" \ @@ -73,7 +149,7 @@ gdb_test "python print(core1.filename)" "^$file_re" \ gdb_test "python print(core1.is_valid())" "^True" \ "Corefile.is_valid() is True while corefile is loaded" -gdb_test "core-file" "^No core file now\\." "unload current core file" +core_file_cmd gdb_test "python print(core1.is_valid())" "^False" \ "Corefile.is_valid() is False after corefile is unloaded" @@ -101,8 +177,7 @@ gdb_test "add-inferior" gdb_test "inferior 2" with_test_prefix "in second inferior" { - gdb_test "core-file $corefile" ".*" \ - "load core file" + core_file_cmd -corefile $corefile -inferior 2 gdb_test "python print(inf.corefile)" "^None" \ "first inferior still has no core file" @@ -128,8 +203,8 @@ gdb_test "python print(core2.filename)" "^$file_re" \ "Corefile.filename attribute works from different progspace" # Load the other corefile into the first inferior. -gdb_test "core $other_corefile" ".*" \ - "load other corefile into inferior 1" +core_file_cmd -corefile $other_corefile \ + -prefix "load other corefile into inferior 1" # Delete the second inferior. We need to switch to the second # inferior and unload its corefile before we can do that. Then, @@ -152,7 +227,7 @@ with_test_prefix "remove second inferior" { "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \ "try to read attribute that doesn't exist" - gdb_test "core-file" + core_file_cmd -inferior 2 gdb_test "python print(core2.filename)" \ [multi_line \ @@ -182,18 +257,10 @@ with_test_prefix "remove second inferior" { # mapped_files API. The output from the built-in command, and the # Python command should be identical. with_test_prefix "test mapped files data" { - clean_restart - - set remote_python_file \ - [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] - - # Load the Python script into GDB. - gdb_test "source $remote_python_file" "^Success" \ - "source python script" + clean_restart_and_load_py_script # Load the core file. - gdb_test "core-file $corefile" ".*" \ - "load core file" + core_file_cmd -corefile $corefile # Two files to write the output to. set out_1 [standard_output_file ${gdb_test_file_name}-out-1.txt] @@ -279,3 +346,38 @@ with_test_prefix "test mapped files data" { gdb_test "python regions\[0\] = None" \ "'tuple' object does not support item assignment" } + +# Load a core file. GDB should figure out which file is being debugged. +# Then use 'start' to run this executable, this will replace the core file +# target. At least on Linux, this replacement is done without calling +# target_detach. This test checks that the expected core file changed and +# inferior exited events are still seen. +with_test_prefix "start from corefile" { + if { [gdb_protocol_is_native] } { + clean_restart_and_load_py_script + + # Load the core file. + core_file_cmd -corefile $corefile + + # Check GDB figured out the executable. + gdb_test "info inferiors 1" \ + [multi_line \ + "\[^\r\n\]+[string_to_regexp $binfile]\\s*" \ + "\[^\r\n\]+[string_to_regexp $corefile]\\s*"] \ + "check executable was detected correctly" + + gdb_test "start" \ + "Temporary breakpoint $::decimal, main \\(\\).*" \ + + gdb_test "events corefile_changed check" \ + "Event 1/1, Inferior 1, Corefile None" \ + "expected corefile event has been seen" + + gdb_test "events exited check" \ + "Event 1/1, Inferior 1, Exit Code None" \ + "expected exited event has been seen" + + gdb_test_no_output -nopass "events corefile_changed reset" + gdb_test_no_output -nopass "events exited reset" + } +} diff --git a/gdb/testsuite/gdb.python/py-corefile.py b/gdb/testsuite/gdb.python/py-corefile.py index 43b64085117..1aaf15093ac 100644 --- a/gdb/testsuite/gdb.python/py-corefile.py +++ b/gdb/testsuite/gdb.python/py-corefile.py @@ -196,4 +196,121 @@ class CheckMainExec(gdb.Command): CheckMainExec() +# An 'events' prefix command. +class events_cmd(gdb.Command): + """Information about recent Python events.""" + + def __init__(self): + gdb.Command.__init__(self, "events", gdb.COMMAND_USER, prefix=True) + + +# An 'events corefile_changed' sub-command. +class events_corefile_changed_cmd(gdb.Command): + """Check recent corefile_changed events. + + Requires a single argument either 'check' or 'reset'. With + 'check', print details of every recent corefile_changed event. + With 'reset' clear the list of recent corefile_changed events.""" + + def __init__(self): + gdb.Command.__init__(self, "events corefile_changed", gdb.COMMAND_USER) + self._events = [] + gdb.events.corefile_changed.connect(lambda e: self._corefile_changed_handler(e)) + + def _corefile_changed_handler(self, event): + assert isinstance(event, gdb.CorefileChangedEvent) + inf = event.inferior + assert isinstance(inf, gdb.Inferior) + + corefile = inf.corefile + if corefile is not None: + assert corefile.is_valid() + corefile = corefile.filename + + obj = {"inferior": inf.num, "corefile": corefile} + self._events.append(obj) + + def invoke(self, args, from_tty): + if args == "check": + if len(self._events) == 0: + print("No corefile_changed event has been seen.") + else: + total = len(self._events) + for idx, obj in enumerate(self._events, start=1): + inf_num = obj["inferior"] + corefile = obj["corefile"] + + if corefile is None: + msg = "None" + else: + msg = corefile + + print( + "Event {}/{}, Inferior {}, Corefile {}".format( + idx, total, inf_num, msg + ) + ) + elif args == "reset": + self._events = [] + else: + raise gdb.GdbError("Unknown command args: {}".format(args)) + + +# An 'events exited' sub-command. +class events_exited_cmd(gdb.Command): + """Check recent exited events. + + Requires a single argument either 'check' or 'reset'. With + 'check', print details of every recent corefile_changed event. + With 'reset' clear the list of recent corefile_changed events.""" + + def __init__(self): + gdb.Command.__init__(self, "events exited", gdb.COMMAND_USER) + self._events = [] + gdb.events.exited.connect(lambda e: self._exited_handler(e)) + + def _exited_handler(self, event): + assert isinstance(event, gdb.ExitedEvent) + inf = event.inferior + assert isinstance(inf, gdb.Inferior) + + if hasattr(event, "exit_code"): + assert isinstance(event.exit_code, int) + exit_code = event.exit_code + else: + exit_code = None + + obj = {"inferior": inf.num, "exit_code": exit_code} + self._events.append(obj) + + def invoke(self, args, from_tty): + if args == "check": + if len(self._events) == 0: + print("No exited event has been seen.") + else: + total = len(self._events) + for idx, obj in enumerate(self._events, start=1): + inf_num = obj["inferior"] + exit_code = obj["exit_code"] + + if exit_code is None: + msg = "None" + else: + msg = exit_code + + print( + "Event {}/{}, Inferior {}, Exit Code {}".format( + idx, total, inf_num, msg + ) + ) + elif args == "reset": + self._events = [] + else: + raise gdb.GdbError("Unknown command args: {}".format(args)) + + +events_cmd() +events_corefile_changed_cmd() +events_exited_cmd() + print("Success") -- 2.25.4