1 /* Python interface to inferior threads. 2 3 Copyright (C) 2009-2023 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 #include "defs.h" 21 #include "gdbthread.h" 22 #include "inferior.h" 23 #include "python-internal.h" 24 25 extern PyTypeObject thread_object_type 26 CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("thread_object"); 27 28 /* Require that INFERIOR be a valid inferior ID. */ 29 #define THPY_REQUIRE_VALID(Thread) \ 30 do { \ 31 if (!Thread->thread) \ 32 { \ 33 PyErr_SetString (PyExc_RuntimeError, \ 34 _("Thread no longer exists.")); \ 35 return NULL; \ 36 } \ 37 } while (0) 38 39 gdbpy_ref<thread_object> 40 create_thread_object (struct thread_info *tp) 41 { 42 gdbpy_ref<thread_object> thread_obj; 43 44 gdbpy_ref<inferior_object> inf_obj = inferior_to_inferior_object (tp->inf); 45 if (inf_obj == NULL) 46 return NULL; 47 48 thread_obj.reset (PyObject_New (thread_object, &thread_object_type)); 49 if (thread_obj == NULL) 50 return NULL; 51 52 thread_obj->thread = tp; 53 thread_obj->inf_obj = (PyObject *) inf_obj.release (); 54 55 return thread_obj; 56 } 57 58 static void 59 thpy_dealloc (PyObject *self) 60 { 61 Py_DECREF (((thread_object *) self)->inf_obj); 62 Py_TYPE (self)->tp_free (self); 63 } 64 65 static PyObject * 66 thpy_get_name (PyObject *self, void *ignore) 67 { 68 thread_object *thread_obj = (thread_object *) self; 69 70 THPY_REQUIRE_VALID (thread_obj); 71 72 const char *name = thread_name (thread_obj->thread); 73 if (name == NULL) 74 Py_RETURN_NONE; 75 76 return PyUnicode_FromString (name); 77 } 78 79 /* Return a string containing target specific additional information about 80 the state of the thread, or None, if there is no such additional 81 information. */ 82 83 static PyObject * 84 thpy_get_details (PyObject *self, void *ignore) 85 { 86 thread_object *thread_obj = (thread_object *) self; 87 88 THPY_REQUIRE_VALID (thread_obj); 89 90 /* GCC can't tell that extra_info will always be assigned after the 91 'catch', so initialize it. */ 92 const char *extra_info = nullptr; 93 try 94 { 95 extra_info = target_extra_thread_info (thread_obj->thread); 96 } 97 catch (const gdb_exception &except) 98 { 99 GDB_PY_HANDLE_EXCEPTION (except); 100 } 101 if (extra_info == nullptr) 102 Py_RETURN_NONE; 103 104 return PyUnicode_FromString (extra_info); 105 } 106 107 static int 108 thpy_set_name (PyObject *self, PyObject *newvalue, void *ignore) 109 { 110 thread_object *thread_obj = (thread_object *) self; 111 gdb::unique_xmalloc_ptr<char> name; 112 113 if (! thread_obj->thread) 114 { 115 PyErr_SetString (PyExc_RuntimeError, _("Thread no longer exists.")); 116 return -1; 117 } 118 119 if (newvalue == NULL) 120 { 121 PyErr_SetString (PyExc_TypeError, 122 _("Cannot delete `name' attribute.")); 123 return -1; 124 } 125 else if (newvalue == Py_None) 126 { 127 /* Nothing. */ 128 } 129 else if (! gdbpy_is_string (newvalue)) 130 { 131 PyErr_SetString (PyExc_TypeError, 132 _("The value of `name' must be a string.")); 133 return -1; 134 } 135 else 136 { 137 name = python_string_to_host_string (newvalue); 138 if (! name) 139 return -1; 140 } 141 142 thread_obj->thread->set_name (std::move (name)); 143 144 return 0; 145 } 146 147 /* Getter for InferiorThread.num. */ 148 149 static PyObject * 150 thpy_get_num (PyObject *self, void *closure) 151 { 152 thread_object *thread_obj = (thread_object *) self; 153 154 THPY_REQUIRE_VALID (thread_obj); 155 156 gdbpy_ref<> result 157 = gdb_py_object_from_longest (thread_obj->thread->per_inf_num); 158 return result.release (); 159 } 160 161 /* Getter for InferiorThread.global_num. */ 162 163 static PyObject * 164 thpy_get_global_num (PyObject *self, void *closure) 165 { 166 thread_object *thread_obj = (thread_object *) self; 167 168 THPY_REQUIRE_VALID (thread_obj); 169 170 gdbpy_ref<> result 171 = gdb_py_object_from_longest (thread_obj->thread->global_num); 172 return result.release (); 173 } 174 175 /* Getter for InferiorThread.ptid -> (pid, lwp, tid). 176 Returns a tuple with the thread's ptid components. */ 177 178 static PyObject * 179 thpy_get_ptid (PyObject *self, void *closure) 180 { 181 thread_object *thread_obj = (thread_object *) self; 182 183 THPY_REQUIRE_VALID (thread_obj); 184 185 return gdbpy_create_ptid_object (thread_obj->thread->ptid); 186 } 187 188 /* Getter for InferiorThread.inferior -> Inferior. */ 189 190 static PyObject * 191 thpy_get_inferior (PyObject *self, void *ignore) 192 { 193 thread_object *thread_obj = (thread_object *) self; 194 195 THPY_REQUIRE_VALID (thread_obj); 196 Py_INCREF (thread_obj->inf_obj); 197 198 return thread_obj->inf_obj; 199 } 200 201 /* Implementation of InferiorThread.switch (). 202 Makes this the GDB selected thread. */ 203 204 static PyObject * 205 thpy_switch (PyObject *self, PyObject *args) 206 { 207 thread_object *thread_obj = (thread_object *) self; 208 209 THPY_REQUIRE_VALID (thread_obj); 210 211 try 212 { 213 switch_to_thread (thread_obj->thread); 214 } 215 catch (const gdb_exception &except) 216 { 217 GDB_PY_HANDLE_EXCEPTION (except); 218 } 219 220 Py_RETURN_NONE; 221 } 222 223 /* Implementation of InferiorThread.is_stopped () -> Boolean. 224 Return whether the thread is stopped. */ 225 226 static PyObject * 227 thpy_is_stopped (PyObject *self, PyObject *args) 228 { 229 thread_object *thread_obj = (thread_object *) self; 230 231 THPY_REQUIRE_VALID (thread_obj); 232 233 if (thread_obj->thread->state == THREAD_STOPPED) 234 Py_RETURN_TRUE; 235 236 Py_RETURN_FALSE; 237 } 238 239 /* Implementation of InferiorThread.is_running () -> Boolean. 240 Return whether the thread is running. */ 241 242 static PyObject * 243 thpy_is_running (PyObject *self, PyObject *args) 244 { 245 thread_object *thread_obj = (thread_object *) self; 246 247 THPY_REQUIRE_VALID (thread_obj); 248 249 if (thread_obj->thread->state == THREAD_RUNNING) 250 Py_RETURN_TRUE; 251 252 Py_RETURN_FALSE; 253 } 254 255 /* Implementation of InferiorThread.is_exited () -> Boolean. 256 Return whether the thread is exited. */ 257 258 static PyObject * 259 thpy_is_exited (PyObject *self, PyObject *args) 260 { 261 thread_object *thread_obj = (thread_object *) self; 262 263 THPY_REQUIRE_VALID (thread_obj); 264 265 if (thread_obj->thread->state == THREAD_EXITED) 266 Py_RETURN_TRUE; 267 268 Py_RETURN_FALSE; 269 } 270 271 /* Implementation of gdb.InfThread.is_valid (self) -> Boolean. 272 Returns True if this inferior Thread object still exists 273 in GDB. */ 274 275 static PyObject * 276 thpy_is_valid (PyObject *self, PyObject *args) 277 { 278 thread_object *thread_obj = (thread_object *) self; 279 280 if (! thread_obj->thread) 281 Py_RETURN_FALSE; 282 283 Py_RETURN_TRUE; 284 } 285 286 /* Implementation of gdb.InferiorThread.handle (self) -> handle. */ 287 288 static PyObject * 289 thpy_thread_handle (PyObject *self, PyObject *args) 290 { 291 thread_object *thread_obj = (thread_object *) self; 292 THPY_REQUIRE_VALID (thread_obj); 293 294 gdb::byte_vector hv; 295 296 try 297 { 298 hv = target_thread_info_to_thread_handle (thread_obj->thread); 299 } 300 catch (const gdb_exception &except) 301 { 302 GDB_PY_HANDLE_EXCEPTION (except); 303 } 304 305 if (hv.size () == 0) 306 { 307 PyErr_SetString (PyExc_RuntimeError, _("Thread handle not found.")); 308 return NULL; 309 } 310 311 PyObject *object = PyBytes_FromStringAndSize ((const char *) hv.data (), 312 hv.size()); 313 return object; 314 } 315 316 /* Return a reference to a new Python object representing a ptid_t. 317 The object is a tuple containing (pid, lwp, tid). */ 318 PyObject * 319 gdbpy_create_ptid_object (ptid_t ptid) 320 { 321 int pid; 322 long lwp; 323 ULONGEST tid; 324 PyObject *ret; 325 326 ret = PyTuple_New (3); 327 if (!ret) 328 return NULL; 329 330 pid = ptid.pid (); 331 lwp = ptid.lwp (); 332 tid = ptid.tid (); 333 334 gdbpy_ref<> pid_obj = gdb_py_object_from_longest (pid); 335 if (pid_obj == nullptr) 336 return nullptr; 337 gdbpy_ref<> lwp_obj = gdb_py_object_from_longest (lwp); 338 if (lwp_obj == nullptr) 339 return nullptr; 340 gdbpy_ref<> tid_obj = gdb_py_object_from_ulongest (tid); 341 if (tid_obj == nullptr) 342 return nullptr; 343 344 /* Note that these steal references, hence the use of 'release'. */ 345 PyTuple_SET_ITEM (ret, 0, pid_obj.release ()); 346 PyTuple_SET_ITEM (ret, 1, lwp_obj.release ()); 347 PyTuple_SET_ITEM (ret, 2, tid_obj.release ()); 348 349 return ret; 350 } 351 352 /* Implementation of gdb.selected_thread () -> gdb.InferiorThread. 353 Returns the selected thread object. */ 354 355 PyObject * 356 gdbpy_selected_thread (PyObject *self, PyObject *args) 357 { 358 if (inferior_ptid != null_ptid) 359 return thread_to_thread_object (inferior_thread ()).release (); 360 361 Py_RETURN_NONE; 362 } 363 364 int 365 gdbpy_initialize_thread (void) 366 { 367 if (PyType_Ready (&thread_object_type) < 0) 368 return -1; 369 370 return gdb_pymodule_addobject (gdb_module, "InferiorThread", 371 (PyObject *) &thread_object_type); 372 } 373 374 static gdb_PyGetSetDef thread_object_getset[] = 375 { 376 { "name", thpy_get_name, thpy_set_name, 377 "The name of the thread, as set by the user or the OS.", NULL }, 378 { "details", thpy_get_details, NULL, 379 "A target specific string containing extra thread state details.", 380 NULL }, 381 { "num", thpy_get_num, NULL, 382 "Per-inferior number of the thread, as assigned by GDB.", NULL }, 383 { "global_num", thpy_get_global_num, NULL, 384 "Global number of the thread, as assigned by GDB.", NULL }, 385 { "ptid", thpy_get_ptid, NULL, "ID of the thread, as assigned by the OS.", 386 NULL }, 387 { "inferior", thpy_get_inferior, NULL, 388 "The Inferior object this thread belongs to.", NULL }, 389 390 { NULL } 391 }; 392 393 static PyMethodDef thread_object_methods[] = 394 { 395 { "is_valid", thpy_is_valid, METH_NOARGS, 396 "is_valid () -> Boolean.\n\ 397 Return true if this inferior thread is valid, false if not." }, 398 { "switch", thpy_switch, METH_NOARGS, 399 "switch ()\n\ 400 Makes this the GDB selected thread." }, 401 { "is_stopped", thpy_is_stopped, METH_NOARGS, 402 "is_stopped () -> Boolean\n\ 403 Return whether the thread is stopped." }, 404 { "is_running", thpy_is_running, METH_NOARGS, 405 "is_running () -> Boolean\n\ 406 Return whether the thread is running." }, 407 { "is_exited", thpy_is_exited, METH_NOARGS, 408 "is_exited () -> Boolean\n\ 409 Return whether the thread is exited." }, 410 { "handle", thpy_thread_handle, METH_NOARGS, 411 "handle () -> handle\n\ 412 Return thread library specific handle for thread." }, 413 414 { NULL } 415 }; 416 417 PyTypeObject thread_object_type = 418 { 419 PyVarObject_HEAD_INIT (NULL, 0) 420 "gdb.InferiorThread", /*tp_name*/ 421 sizeof (thread_object), /*tp_basicsize*/ 422 0, /*tp_itemsize*/ 423 thpy_dealloc, /*tp_dealloc*/ 424 0, /*tp_print*/ 425 0, /*tp_getattr*/ 426 0, /*tp_setattr*/ 427 0, /*tp_compare*/ 428 0, /*tp_repr*/ 429 0, /*tp_as_number*/ 430 0, /*tp_as_sequence*/ 431 0, /*tp_as_mapping*/ 432 0, /*tp_hash */ 433 0, /*tp_call*/ 434 0, /*tp_str*/ 435 0, /*tp_getattro*/ 436 0, /*tp_setattro*/ 437 0, /*tp_as_buffer*/ 438 Py_TPFLAGS_DEFAULT, /*tp_flags*/ 439 "GDB thread object", /* tp_doc */ 440 0, /* tp_traverse */ 441 0, /* tp_clear */ 442 0, /* tp_richcompare */ 443 0, /* tp_weaklistoffset */ 444 0, /* tp_iter */ 445 0, /* tp_iternext */ 446 thread_object_methods, /* tp_methods */ 447 0, /* tp_members */ 448 thread_object_getset, /* tp_getset */ 449 0, /* tp_base */ 450 0, /* tp_dict */ 451 0, /* tp_descr_get */ 452 0, /* tp_descr_set */ 453 0, /* tp_dictoffset */ 454 0, /* tp_init */ 455 0 /* tp_alloc */ 456 }; 457