* Python API
+ ** gdb.ThreadExitedEvent added. Emits a ThreadEvent.
+
** The gdb.unwinder.Unwinder.name attribute is now read-only.
** The name argument passed to gdb.unwinder.Unwinder.__init__ must
The new thread.
@end defvar
+@item events.thread_exited
+This is emitted when @value{GDBN} notices a thread has exited. The event
+is of type @code{gdb.ThreadExitedEvent} which extends @code{gdb.ThreadEvent}.
+This has a single attribute:
+
+@defvar ThreadExitedEvent.inferior_thread
+The exiting thread.
+@end defvar
+
@item events.gdb_exiting
This is emitted when @value{GDBN} exits. This event is not emitted if
@value{GDBN} exits as a result of an internal error, or after an
(@pxref{python_breakpoint_thread,,The Breakpoint.thread attribute}).
@end defvar
+@anchor{inferior_thread_ptid}
@defvar InferiorThread.ptid
ID of the thread, as assigned by the operating system. This attribute is a
tuple containing three integers. The first is the Process ID (PID); the second
GDB_PY_DEFINE_EVENT(new_inferior)
GDB_PY_DEFINE_EVENT(inferior_deleted)
GDB_PY_DEFINE_EVENT(new_thread)
+GDB_PY_DEFINE_EVENT(thread_exited)
GDB_PY_DEFINE_EVENT(inferior_call)
GDB_PY_DEFINE_EVENT(memory_changed)
GDB_PY_DEFINE_EVENT(register_changed)
"GDB new thread event object",
thread_event_object_type);
+GDB_PY_DEFINE_EVENT_TYPE (thread_exited,
+ "ThreadExitedEvent",
+ "GDB thread exited event object",
+ event_object_type);
+
GDB_PY_DEFINE_EVENT_TYPE (new_inferior,
"NewInferiorEvent",
"GDB new inferior event object",
extern int evpy_emit_event (PyObject *event,
eventregistry_object *registry);
+/* Emits a thread exit event for THREAD */
+extern int emit_thread_exit_event (thread_info * thread);
+
extern gdbpy_ref<> create_event_object (PyTypeObject *py_type);
/* thread events can either be thread specific or process wide. If gdb is
if (inf_obj == NULL)
return;
+ if (emit_thread_exit_event (tp) < 0)
+ gdbpy_print_stack ();
+
auto it = inf_obj->threads->find (tp);
if (it != inf_obj->threads->end ())
{
return thread_event_obj;
}
+
+/* Emits a thread exit event for THREAD */
+
+int
+emit_thread_exit_event (thread_info * thread)
+{
+ if (evregpy_no_listeners_p (gdb_py_events.thread_exited))
+ return 0;
+
+ auto py_thr = thread_to_thread_object (thread);
+
+ if (py_thr == nullptr)
+ return -1;
+
+ auto inf_thr = create_thread_event_object (&thread_exited_event_object_type,
+ py_thr.get ());
+ if (inf_thr == nullptr)
+ return -1;
+
+ return evpy_emit_event (inf_thr.get (), gdb_py_events.thread_exited);
+}
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2022-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 <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <signal.h>
+pthread_t thread2_id;
+pthread_t thread3_id;
+
+void* do_thread (void* d)
+{
+ return NULL;
+}
+
+int main (void)
+{
+ pthread_create (&thread2_id, NULL, do_thread, NULL);
+ pthread_join (thread2_id, NULL);
+ pthread_create (&thread3_id, NULL, do_thread, NULL);
+ pthread_join (thread3_id, NULL);
+ return 12;
+}
--- /dev/null
+# Copyright (C) 2022-202 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 <http://www.gnu.org/licenses/>.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ return -1
+}
+
+save_vars { GDBFLAGS } {
+ clean_restart $testfile
+}
+
+set pyfile [gdb_remote_download host ${srcdir}/${subdir}/py-thread-exited.py]
+gdb_test_no_output "source ${pyfile}" "load python file"
+
+gdb_test "test-events" "Event testers registered."
+
+if ![runto_main] {
+ return -1
+}
+
+gdb_breakpoint 37 "last of main"
+
+gdb_continue_to_breakpoint "continue to breakpoint"
+
+gdb_test "python print(threadOneExit)" \
+ ".*event type: thread-exited. global num: 2.*"
+gdb_test "python print(threadTwoExit)" \
+ ".*event type: thread-exited. global num: 3.*"
--- /dev/null
+# Copyright 2022-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 <http://www.gnu.org/licenses/>.
+
+import gdb
+
+threadOneExit = ""
+threadTwoExit = ""
+# we don't want to overwrite the 2nd thread's exit event, thus
+# store it here. we don't care about it though.
+mainThreadExit = ""
+
+def thread_exited_handler(event):
+ global threadOneExit, threadTwoExit, mainThreadExit
+ print("{}".format(event))
+ assert isinstance(event, gdb.ThreadExitedEvent)
+ if threadOneExit == "":
+ threadOneExit = "event type: thread-exited. global num: {}".format(event.inferior_thread.global_num)
+ else:
+ if threadTwoExit == "":
+ threadTwoExit = "event type: thread-exited. global num: {}".format(event.inferior_thread.global_num)
+ else:
+ mainThreadExit = "event type: thread-exited. global num: {}".format(event.inferior_thread.global_num)
+
+class test_events(gdb.Command):
+ """Test events."""
+
+ def __init__(self):
+ gdb.Command.__init__(self, "test-events", gdb.COMMAND_STACK)
+
+ def invoke(self, arg, from_tty):
+ gdb.events.thread_exited.connect(thread_exited_handler)
+ print("Event testers registered.")
+
+test_events()