python/py-bpevent.c \
python/py-breakpoint.c \
python/py-cmd.c \
+ python/py-connection.c \
python/py-continueevent.c \
python/py-event.c \
python/py-evtregistry.c \
** New function gdb.Architecture.integer_type(), which returns an
integer type given a size and a signed-ness.
+ ** New gdb.TargetConnection object type that represents a connection
+ (as displayed by the 'info connections' command).
+
+ ** The gdb.Inferior type now has a 'connection' property which is an
+ instance of gdb.TargetConnection, the connection used by this
+ inferior. This can be None if the inferior has no connection.
+
+ ** New 'gdb.events.connection_removed' event registry, which emits a
+ 'gdb.ConnectionEvent' when a connection is removed from GDB.
+ This event has a 'connection' property, a gdb.TargetConnection
+ object for the connection being removed.
+
+ ** New gdb.connections() function that returns a list of all
+ currently active connections.
+
* New features in the GDB remote stub, GDBserver
** GDBserver is now supported on OpenRISC GNU/Linux.
* Lazy Strings In Python:: Python representation of lazy strings.
* Architectures In Python:: Python representation of architectures.
* Registers In Python:: Python representation of registers.
+* Connections In Python:: Python representation of connections.
* TUI Windows In Python:: Implementing new TUI windows.
@end menu
(@pxref{gdbpy_architecture_name,,Architecture.name}).
@end defun
+@anchor{gdbpy_connections}
+@defun gdb.connections
+Return a list of @code{gdb.TargetConnection} objects, one for each
+currently active connection (@pxref{Connections In Python}). The
+connection objects are in no particular order in the returned list.
+@end defun
+
@node Exception Handling
@subsubsection Exception Handling
@cindex python exceptions
ID of inferior, as assigned by GDB.
@end defvar
+@anchor{gdbpy_inferior_connection}
+@defvar Inferior.connection
+The @code{gdb.TargetConnection} for this inferior (@pxref{Connections
+In Python}), or @code{None} if this inferior has no connection.
+@end defvar
+
@defvar Inferior.connection_num
ID of inferior's connection as assigned by @value{GDBN}, or None if
-the inferior is not connected to a target.
-@xref{Inferiors Connections and Programs}.
+the inferior is not connected to a target. @xref{Inferiors Connections
+and Programs}. This is equivalent to
+@code{gdb.Inferior.connection.num} in the case where
+@code{gdb.Inferior.connection} is not @code{None}.
@end defvar
@defvar Inferior.pid
An integer, the value of the exit code @value{GDBN} will return.
@end defvar
+@item events.connection_removed
+This is emitted when @value{GDBN} removes a connection
+(@pxref{Connections In Python}). The event is of type
+@code{gdb.ConnectionEvent}. This has a single read-only attribute:
+
+@defvar ConnectionEvent.connection
+The @code{gdb.TargetConnection} that is being removed.
+@end defvar
+
@end table
@node Threads In Python
A string that is the name of this register group.
@end defvar
+@node Connections In Python
+@subsubsection Connections In Python
+@cindex connections in python
+@value{GDBN} lets you run and debug multiple programs in a single
+session. Each program being debugged has a connection, the connection
+describes how @value{GDBN} controls the program being debugged.
+Examples of different connection types are @samp{native} and
+@samp{remote}. @xref{Inferiors Connections and Programs}.
+
+@value{GDBN} uses the @code{gdb.TargetConnection} object type to
+represent a connection in Python code. To get a list of all
+connections use @code{gdb.connections}
+(@pxref{gdbpy_connections,,gdb.connections}).
+
+To get the connection for a single @code{gdb.Inferior} read its
+@code{gdb.Inferior.connection} attribute
+(@pxref{gdbpy_inferior_connection,,gdb.Inferior.connection}).
+
+A @code{gdb.TargetConnection} has the following method:
+
+@defun TargetConnection.is_valid ()
+Return @code{True} if the @code{gdb.TargetConnection} object is valid,
+@code{False} if not. A @code{gdb.TargetConnection} will become
+invalid if the connection no longer exists within @value{GDBN}, this
+might happen when no inferiors are using the connection, but could be
+delayed until the user replaces the current target.
+
+Reading any of the @code{gdb.TargetConnection} properties will throw
+an exception if the connection is invalid.
+@end defun
+
+A @code{gdb.TargetConnection} has the following read-only properties:
+
+@defvar TargetConnection.num
+An integer assigned by @value{GDBN} to uniquely identify this
+connection. This is the same value as displayed in the @samp{Num}
+column of the @code{info connections} command output (@pxref{Inferiors
+Connections and Programs,,info connections}).
+@end defvar
+
+@defvar TargetConnection.type
+A string that describes what type of connection this is. This string
+will be one of the valid names that can be passed to the @code{target}
+command (@pxref{Target Commands,,target command}).
+@end defvar
+
+@defvar TargetConnection.description
+A string that gives a short description of this target type. This is
+the same string that is displayed in the @samp{Description} column of
+the @code{info connection} command output (@pxref{Inferiors
+Connections and Programs,,info connections}).
+@end defvar
+
+@defvar TargetConnection.details
+An optional string that gives additional information about this
+connection. This attribute can be @code{None} if there are no
+additional details for this connection.
+
+An example of a connection type that might have additional details is
+the @samp{remote} connection, in this case the details string can
+contain the @samp{@var{hostname}:@var{port}} that was used to connect
+to the remote target.
+@end defvar
+
@node TUI Windows In Python
@subsubsection Implementing new TUI windows
@cindex Python TUI Windows
DEFINE_OBSERVABLE (styling_changed);
DEFINE_OBSERVABLE (current_source_symtab_and_line_changed);
DEFINE_OBSERVABLE (gdb_exiting);
+DEFINE_OBSERVABLE (connection_removed);
} /* namespace observers */
} /* namespace gdb */
/* Called when GDB is about to exit. */
extern observable<int> gdb_exiting;
+/* When a connection is removed. */
+extern observable<process_stratum_target */* target */> connection_removed;
+
} /* namespace observers */
} /* namespace gdb */
GDB_PY_DEFINE_EVENT(breakpoint_modified)
GDB_PY_DEFINE_EVENT(before_prompt)
GDB_PY_DEFINE_EVENT(gdb_exiting)
+GDB_PY_DEFINE_EVENT(connection_removed)
--- /dev/null
+/* Python interface to inferiors.
+
+ Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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 "defs.h"
+#include "python-internal.h"
+#include "process-stratum-target.h"
+#include "inferior.h"
+#include "observable.h"
+#include "target-connection.h"
+#include "py-events.h"
+#include "py-event.h"
+#include "arch-utils.h"
+
+#include <map>
+
+/* The Python object that represents a connection. */
+
+struct connection_object
+{
+ PyObject_HEAD
+
+ /* The process target that represents this connection. When a
+ connection_object is created this field will always point at a valid
+ target. Later, if GDB stops using this target (the target is popped
+ from all target stacks) then this field is set to nullptr, which
+ indicates that this Python object is now in the invalid state (see
+ the is_valid() method below). */
+ struct process_stratum_target *target;
+};
+
+extern PyTypeObject connection_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("connection_object");
+
+/* Require that CONNECTION be valid. */
+#define CONNPY_REQUIRE_VALID(connection) \
+ do { \
+ if (connection->target == nullptr) \
+ { \
+ PyErr_SetString (PyExc_RuntimeError, \
+ _("Connection no longer exists.")); \
+ return nullptr; \
+ } \
+ } while (0)
+
+/* A map between process_stratum targets and the Python object representing
+ them. We actually hold a gdbpy_ref around the Python object so that
+ reference counts are handled correctly when entries are deleted. */
+static std::map<process_stratum_target *,
+ gdbpy_ref<connection_object>> all_connection_objects;
+
+/* Return a reference to a gdb.TargetConnection object for TARGET. If
+ TARGET is nullptr then a reference to None is returned.
+
+ Previously created gdb.TargetConnection objects are cached, and
+ additional references to the same connection object can be returned with
+ later calls to this function. */
+
+gdbpy_ref<>
+target_to_connection_object (process_stratum_target *target)
+{
+ if (target == nullptr)
+ return gdbpy_ref<>::new_reference (Py_None);
+
+ gdbpy_ref <connection_object> conn_obj;
+ auto conn_obj_iter = all_connection_objects.find (target);
+ if (conn_obj_iter == all_connection_objects.end ())
+ {
+ conn_obj.reset (PyObject_New (connection_object,
+ &connection_object_type));
+ if (conn_obj == nullptr)
+ return nullptr;
+ conn_obj->target = target;
+ all_connection_objects.emplace (target, conn_obj);
+ }
+ else
+ conn_obj = conn_obj_iter->second;
+
+ gdb_assert (conn_obj != nullptr);
+
+ /* Repackage the result as a PyObject reference. */
+ return gdbpy_ref<> ((PyObject *) conn_obj.release ());
+}
+
+/* Return a list of gdb.TargetConnection objects, one for each currently
+ active connection. The returned list is in no particular order. */
+
+PyObject *
+gdbpy_connections (PyObject *self, PyObject *args)
+{
+ gdbpy_ref<> list (PyList_New (0));
+ if (list == nullptr)
+ return nullptr;
+
+ for (process_stratum_target *target : all_non_exited_process_targets ())
+ {
+ gdb_assert (target != nullptr);
+
+ gdbpy_ref<> conn = target_to_connection_object (target);
+ if (conn == nullptr)
+ return nullptr;
+ gdb_assert (conn.get () != Py_None);
+
+ if (PyList_Append (list.get (), conn.get ()) < 0)
+ return nullptr;
+ }
+
+ return list.release ();
+}
+
+/* Emit a connection event for TARGET to REGISTRY. Return 0 on success, or
+ a negative value on error. */
+
+static int
+emit_connection_event (process_stratum_target *target,
+ eventregistry_object *registry)
+{
+ gdbpy_ref<> event_obj
+ = create_event_object (&connection_event_object_type);
+ if (event_obj == nullptr)
+ return -1;
+
+ gdbpy_ref<> conn = target_to_connection_object (target);
+ if (evpy_add_attribute (event_obj.get (), "connection", conn.get ()) < 0)
+ return -1;
+
+ return evpy_emit_event (event_obj.get (), registry);
+}
+
+/* Callback for the connection_removed observer. */
+
+static void
+connpy_connection_removed (process_stratum_target *target)
+{
+ if (!gdb_python_initialized)
+ return;
+
+ gdbpy_enter enter_py (get_current_arch (), current_language);
+
+ if (!evregpy_no_listeners_p (gdb_py_events.connection_removed))
+ if (emit_connection_event (target, gdb_py_events.connection_removed) < 0)
+ gdbpy_print_stack ();
+
+ auto conn_obj_iter = all_connection_objects.find (target);
+ if (conn_obj_iter != all_connection_objects.end ())
+ {
+ gdbpy_ref <connection_object> conn_obj = conn_obj_iter->second;
+ conn_obj->target = nullptr;
+ all_connection_objects.erase (target);
+ }
+}
+
+/* Called when a gdb.TargetConnection object is deallocated. */
+
+static void
+connpy_connection_dealloc (PyObject *obj)
+{
+ connection_object *conn_obj = (connection_object *) obj;
+
+ /* As the all_connection_objects map holds a reference to each connection
+ object we can only enter the dealloc function when the reference in
+ all_connection_objects has been erased.
+
+ As we always set the target pointer back to nullptr before we erase
+ items from all_connection_objects then, when we get here, the target
+ pointer must be nullptr. */
+ gdb_assert (conn_obj->target == nullptr);
+
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* Implement repr() for gdb.TargetConnection. */
+
+static PyObject *
+connpy_repr (PyObject *obj)
+{
+ connection_object *self = (connection_object *) obj;
+ process_stratum_target *target = self->target;
+
+ if (target == nullptr)
+ return PyString_FromFormat ("<%s (invalid)>", Py_TYPE (obj)->tp_name);
+
+ return PyString_FromFormat ("<%s num=%d, what=\"%s\">",
+ Py_TYPE (obj)->tp_name,
+ target->connection_number,
+ make_target_connection_string (target).c_str ());
+}
+
+/* Implementation of gdb.TargetConnection.is_valid() -> Boolean. Returns
+ True if this connection object is still associated with a
+ process_stratum_target, otherwise, returns False. */
+
+static PyObject *
+connpy_is_valid (PyObject *self, PyObject *args)
+{
+ connection_object *conn = (connection_object *) self;
+
+ if (conn->target == nullptr)
+ Py_RETURN_FALSE;
+
+ Py_RETURN_TRUE;
+}
+
+/* Return the id number of this connection. */
+
+static PyObject *
+connpy_get_connection_num (PyObject *self, void *closure)
+{
+ connection_object *conn = (connection_object *) self;
+
+ CONNPY_REQUIRE_VALID (conn);
+
+ auto num = conn->target->connection_number;
+ return gdb_py_object_from_longest (num).release ();
+}
+
+/* Return a string that gives the short name for this connection type. */
+
+static PyObject *
+connpy_get_connection_type (PyObject *self, void *closure)
+{
+ connection_object *conn = (connection_object *) self;
+
+ CONNPY_REQUIRE_VALID (conn);
+
+ const char *shortname = conn->target->shortname ();
+ return host_string_to_python_string (shortname).release ();
+}
+
+/* Return a string that gives a longer description of this connection type. */
+
+static PyObject *
+connpy_get_description (PyObject *self, void *closure)
+{
+ connection_object *conn = (connection_object *) self;
+
+ CONNPY_REQUIRE_VALID (conn);
+
+ const char *longname = conn->target->longname ();
+ return host_string_to_python_string (longname).release ();
+}
+
+/* Return a string that gives additional details about this connection, or
+ None, if there are no additional details for this connection type. */
+
+static PyObject *
+connpy_get_connection_details (PyObject *self, void *closure)
+{
+ connection_object *conn = (connection_object *) self;
+
+ CONNPY_REQUIRE_VALID (conn);
+
+ const char *details = conn->target->connection_string ();
+ if (details != nullptr)
+ return host_string_to_python_string (details).release ();
+ else
+ Py_RETURN_NONE;
+}
+
+/* Python specific initialization for this file. */
+
+int
+gdbpy_initialize_connection (void)
+{
+ if (PyType_Ready (&connection_object_type) < 0)
+ return -1;
+
+ if (gdb_pymodule_addobject (gdb_module, "TargetConnection",
+ (PyObject *) &connection_object_type) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Global initialization for this file. */
+
+void _initialize_py_connection ();
+void
+_initialize_py_connection ()
+{
+ gdb::observers::connection_removed.attach (connpy_connection_removed,
+ "py-connection");
+}
+
+/* Methods for the gdb.TargetConnection object type. */
+
+static PyMethodDef connection_object_methods[] =
+{
+ { "is_valid", connpy_is_valid, METH_NOARGS,
+ "is_valid () -> Boolean.\n\
+Return true if this TargetConnection is valid, false if not." },
+ { NULL }
+};
+
+/* Attributes for the gdb.TargetConnection object type. */
+
+static gdb_PyGetSetDef connection_object_getset[] =
+{
+ { "num", connpy_get_connection_num, NULL,
+ "ID number of this connection, as assigned by GDB.", NULL },
+ { "type", connpy_get_connection_type, NULL,
+ "A short string that is the name for this connection type.", NULL },
+ { "description", connpy_get_description, NULL,
+ "A longer string describing this connection type.", NULL },
+ { "details", connpy_get_connection_details, NULL,
+ "A string containing additional connection details.", NULL },
+ { NULL }
+};
+
+/* Define the gdb.TargetConnection object type. */
+
+PyTypeObject connection_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.TargetConnection", /* tp_name */
+ sizeof (connection_object), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ connpy_connection_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ connpy_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "GDB target connection object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ connection_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ connection_object_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0 /* tp_alloc */
+};
"GdbExitingEvent",
"GDB is about to exit",
event_object_type);
+
+GDB_PY_DEFINE_EVENT_TYPE (connection,
+ "ConnectionEvent",
+ "GDB connection added or removed object",
+ event_object_type);
return gdb_py_object_from_longest (inf->inferior->num).release ();
}
+/* Return the gdb.TargetConnection object for this inferior, or None if a
+ connection does not exist. */
+
+static PyObject *
+infpy_get_connection (PyObject *self, void *closure)
+{
+ inferior_object *inf = (inferior_object *) self;
+
+ INFPY_REQUIRE_VALID (inf);
+
+ process_stratum_target *target = inf->inferior->process_target ();
+ return target_to_connection_object (target).release ();
+}
+
/* Return the connection number of the given inferior, or None if a
connection does not exist. */
static gdb_PyGetSetDef inferior_object_getset[] =
{
{ "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
+ { "connection", infpy_get_connection, NULL,
+ "The gdb.TargetConnection for this inferior.", NULL },
{ "connection_num", infpy_get_connection_num, NULL,
"ID of inferior's connection, as assigned by GDB.", NULL },
{ "pid", infpy_get_pid, NULL, "PID of inferior, as assigned by the OS.",
PyObject *gdbpy_buffer_to_membuf (gdb::unique_xmalloc_ptr<gdb_byte> buffer,
CORE_ADDR address, ULONGEST length);
+struct process_stratum_target;
+gdbpy_ref<> target_to_connection_object (process_stratum_target *target);
+PyObject *gdbpy_connections (PyObject *self, PyObject *args);
+
const struct block *block_object_to_block (PyObject *obj);
struct symbol *symbol_object_to_symbol (PyObject *obj);
struct value *value_object_to_value (PyObject *self);
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
int gdbpy_initialize_membuf ()
CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_connection ()
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
/* A wrapper for PyErr_Fetch that handles reference counting for the
caller. */
|| gdbpy_initialize_xmethods () < 0
|| gdbpy_initialize_unwind () < 0
|| gdbpy_initialize_membuf () < 0
+ || gdbpy_initialize_connection () < 0
|| gdbpy_initialize_tui () < 0)
return false;
"architecture_names () -> List.\n\
Return a list of all the architecture names GDB understands." },
+ { "connections", gdbpy_connections, METH_NOARGS,
+ "connections () -> List.\n\
+Return a list of gdb.TargetConnection objects." },
+
{NULL, NULL, 0, NULL}
};
#include "inferior.h"
#include "target.h"
+#include "observable.h"
/* A map between connection number and representative process_stratum
target. */
void
connection_list_remove (process_stratum_target *t)
{
+ /* Notify about the connection being removed before we reset the
+ connection number to zero. */
+ gdb::observers::connection_removed.notify (t);
process_targets.erase (t->connection_number);
t->connection_number = 0;
}
# Test "info inferiors" and "info connections" with multiple targets.
+load_lib gdb-python.exp
+
source $srcdir/$subdir/multi-target.exp.tcl
if {![multi_target_prepare]} {
return
}
+# Cache the result of calling skip_python_tests into a local variable.
+set run_python_tests [expr ! [skip_python_tests]]
+
# Test "info inferiors" and "info connections". MULTI_PROCESS
# indicates whether the multi-process feature of remote targets is
# turned off or on.
proc test_info_inferiors {multi_process} {
setup "off"
+ if { $::run_python_tests } {
+ gdb_test_no_output "source ${::remote_python_file}" "load python file"
+ }
+
gdb_test_no_output \
"set remote multiprocess-feature-packet $multi_process"
"[connection_num 5 $inf]${ws}core${ws}Local core dump file${ws}" \
]
+ if { $::run_python_tests } {
+ gdb_test "python info_connections()" \
+ [multi_line \
+ "Num${ws}What${ws}Description" \
+ "[connection_num 1 $inf]${ws}native${ws}Native process" \
+ "[connection_num 2 $inf]${ws}extended-remote localhost:$decimal${ws}Extended remote serial target in gdb-specific protocol" \
+ "[connection_num 3 $inf]${ws}core${ws}Local core dump file" \
+ "[connection_num 4 $inf]${ws}extended-remote localhost:$decimal${ws}Extended remote serial target in gdb-specific protocol" \
+ "[connection_num 5 $inf]${ws}core${ws}Local core dump file" \
+ ]
+ }
+
gdb_test "info inferiors" \
[multi_line \
"Num${ws}Description${ws}Connection${ws}Executable${ws}" \
"[inf_desc 5 $inf]4 \\(extended-remote localhost:$decimal\\)${ws}${binfile}${ws}" \
"[inf_desc 6 $inf]5 \\(core\\)${ws}${binfile}${ws}" \
]
+
+ if { $::run_python_tests } {
+ gdb_test "python info_inferiors()" \
+ [multi_line \
+ "Inferior 1, Connection #1: native" \
+ "Inferior 2, Connection #2: extended-remote localhost:$decimal" \
+ "Inferior 3, Connection #3: core" \
+ "Inferior 4, Connection #1: native" \
+ "Inferior 5, Connection #4: extended-remote localhost:$decimal" \
+ "Inferior 6, Connection #5: core" \
+ ]
+ }
}
}
}
+if { $run_python_tests } {
+ set remote_python_file [gdb_remote_download host \
+ ${srcdir}/${subdir}/${testfile}.py]
+}
+
# Test "info inferiors" and "info connections" commands.
with_test_prefix "info-inferiors" {
foreach_with_prefix multi_process {"on" "off"} {
--- /dev/null
+# Copyright (C) 2021 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
+
+# Take a gdb.TargetConnection and return the connection number.
+def conn_num(c):
+ return c.num
+
+# Takes a gdb.TargetConnection and return a string that is either the
+# type, or the type and details (if the details are not None).
+def make_target_connection_string(c):
+ if c.details is None:
+ return c.type
+ else:
+ return "%s %s" % (c.type, c.details)
+
+# A Python implementation of 'info connections'. Produce output that
+# is identical to the output of 'info connections' so we can check
+# that aspects of gdb.TargetConnection work correctly.
+def info_connections():
+ all_connections = sorted(gdb.connections(), key=conn_num)
+ current_conn = gdb.selected_inferior().connection
+ what_width = 0
+ for c in all_connections:
+ s = make_target_connection_string(c)
+ if len(s) > what_width:
+ what_width = len(s)
+
+ fmt = " Num %%-%ds Description" % what_width
+ print(fmt % "What")
+ fmt = "%%s%%-3d %%-%ds %%s" % what_width
+ for c in all_connections:
+ if c == current_conn:
+ prefix = "* "
+ else:
+ prefix = " "
+
+ print(fmt % (prefix, c.num, make_target_connection_string(c),
+ c.description))
+
+def inf_num(i):
+ return i.num
+
+# Print information about each inferior, and the connection it is
+# using.
+def info_inferiors():
+ all_inferiors = sorted(gdb.inferiors(), key=inf_num)
+ for i in gdb.inferiors():
+ print("Inferior %d, Connection #%d: %s" %
+ (i.num, i.connection_num,
+ make_target_connection_string(i.connection)))
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021 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/>. */
+
+int
+main (void)
+{
+ return 0;
+}
--- /dev/null
+# Copyright (C) 2021 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/>.
+
+# This file is for testing the gdb.TargetConnection API. This API is
+# already tested in gdb.multi/multi-target-info-inferiors.exp and
+# gdb.python/py-inferior.exp, this file just covers some edge cases
+# that are not tested in other places.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+ return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+ return 0
+}
+
+# Create a gdb.TargetConnection object and check it is initially
+# valid.
+gdb_test_no_output "python conn = gdb.selected_inferior().connection"
+gdb_test "python print(conn)" \
+ "<gdb.TargetConnection num=1, what=\"\[^\"\]+\">" \
+ "print gdb.TargetConnection while it is still valid"
+gdb_test "python print(conn.is_valid())" "True" "is_valid returns True"
+
+# Get the connection again, and ensure we get the exact same object.
+gdb_test_no_output "python conn2 = gdb.selected_inferior().connection"
+gdb_test "python print('Same object: %s' % (conn is conn2))" "True"
+
+# Now invalidate the connection, and ensure that the is_valid method
+# starts to return False.
+gdb_test "info connections" "\r\n\\* 1 .*" \
+ "info connections while the connection is still around"
+gdb_test "disconnect" "" "kill the inferior" \
+ "A program is being debugged already\\. Kill it\\? .*y or n. $" "y"
+gdb_test "info connections" "No connections\\." \
+ "info connections now all the connections have gone"
+gdb_test "python print(conn)" "<gdb.TargetConnection \\(invalid\\)>" \
+ "print gdb.TargetConnection now its invalid"
+gdb_test "python print(conn.is_valid())" "False" "is_valid returns False"
+
+# Now check that accessing properties of the invalid connection cases
+# an error.
+gdb_test "python print(conn.num)" \
+ "RuntimeError: Connection no longer exists\\.\r\n.*"
+gdb_test "python print(conn.type)" \
+ "RuntimeError: Connection no longer exists\\.\r\n.*"
+gdb_test "python print(conn.description)" \
+ "RuntimeError: Connection no longer exists\\.\r\n.*"
+gdb_test "python print(conn.details)" \
+ "RuntimeError: Connection no longer exists\\.\r\n.*"
gdb_test "python print ('result = %s' % (i0 == inferiors\[0\]))" " = True" "test equality comparison (true)"
gdb_test "python print ('result = %s' % i0.num)" " = \[0-9\]+" "test Inferior.num"
gdb_test "python print ('result = %s' % i0.connection_num)" " = \[0-9\]+" "test Inferior.connection_num"
+gdb_test "python print ('result = %s' % (i0.connection_num == i0.connection.num))" " = True" \
+ "Inferior.connection_num equals Inferior.connection.num"
gdb_test "python print ('result = %s' % i0.pid)" " = \[0-9\]+" "test Inferior.pid"
gdb_test "python print ('result = %s' % i0.was_attached)" " = False" "test Inferior.was_attached"
gdb_test "python print (i0.threads ())" "\\(<gdb.InferiorThread object at 0x\[\[:xdigit:\]\]+>,\\)" "test Inferior.threads"
"RuntimeError: Inferior no longer exists.*"
gdb_test "python print (inf_list\[1\].connection_num)" \
"RuntimeError: Inferior no longer exists.*"
+ gdb_test "python print (inf_list\[1\].connection)" \
+ "RuntimeError: Inferior no longer exists.*"
gdb_test "python print (inf_list\[1\].pid)" \
"RuntimeError: Inferior no longer exists.*"
gdb_test "python print (inf_list\[1\].was_attached)" \
gdb_test "inferior 1" ".*" "switch to first inferior"
gdb_test "py print (gdb.selected_inferior().num)" "1" "first inferior selected"
gdb_test "py print (gdb.selected_inferior().connection_num)" "1" \
- "first inferior's connection"
+ "first inferior's connection number"
+ gdb_test "py print (gdb.selected_inferior().connection.num)" "1" \
+ "first inferior's connection number, though connection object"
# Figure out if inf 1 has a native target.
set inf_1_is_native [gdb_is_target_native]
gdb_test "inferior 3" ".*" "switch to third inferior"
gdb_test "py print (gdb.selected_inferior().num)" "3" "third inferior selected"
gdb_test "py print (gdb.selected_inferior().connection_num)" "None" \
+ "third inferior's None connection number"
+ gdb_test "py print (gdb.selected_inferior().connection)" "None" \
"third inferior's None connection"
gdb_test "target native" "Done. Use the \"run\" command to start a process." \
"target for the third inferior"
}
gdb_test "py print (gdb.selected_inferior().connection_num)" \
"$expected_connection_num" \
- "third inferior's native connection"
+ "third inferior's native connection number"
+ gdb_test "py print (gdb.selected_inferior().connection.num)" \
+ "$expected_connection_num" \
+ "third inferior's native connection number, though connection object"
+
+ # Test printing of gdb.TargetConnection object.
+ gdb_test "py print (gdb.selected_inferior().connection)" \
+ "<gdb.TargetConnection num=${expected_connection_num}, what=\"\[^\"\]+\">" \
+ "print a connection object"
gdb_test "inferior 1" ".*" "switch back to first inferior"
gdb_test_no_output "remove-inferiors 3" "remove second inferior"