diff options
Diffstat (limited to 'gdb/python/python-internal.h')
| -rw-r--r-- | gdb/python/python-internal.h | 342 |
1 files changed, 268 insertions, 74 deletions
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index c48f260..722edbd 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -1,6 +1,6 @@ /* Gdb/Python header for private use by Python module. - Copyright (C) 2008-2024 Free Software Foundation, Inc. + Copyright (C) 2008-2026 Free Software Foundation, Inc. This file is part of GDB. @@ -22,34 +22,7 @@ #include "extension.h" #include "extension-priv.h" - -/* These WITH_* macros are defined by the CPython API checker that - comes with the Python plugin for GCC. See: - https://gcc-python-plugin.readthedocs.org/en/latest/cpychecker.html - The checker defines a WITH_ macro for each attribute it - exposes. Note that we intentionally do not use - 'cpychecker_returns_borrowed_ref' -- that idiom is forbidden in - gdb. */ - -#ifdef WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE -#define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(ARG) \ - __attribute__ ((cpychecker_type_object_for_typedef (ARG))) -#else -#define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(ARG) -#endif - -#ifdef WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE -#define CPYCHECKER_SETS_EXCEPTION __attribute__ ((cpychecker_sets_exception)) -#else -#define CPYCHECKER_SETS_EXCEPTION -#endif - -#ifdef WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE -#define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \ - __attribute__ ((cpychecker_negative_result_sets_exception)) -#else -#define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION -#endif +#include "registry.h" /* /usr/include/features.h on linux systems will define _POSIX_C_SOURCE if it sees _GNU_SOURCE (which config.h will define). @@ -87,7 +60,7 @@ #include <frameobject.h> #include "py-ref.h" -#define Py_TPFLAGS_CHECKTYPES 0 +static_assert (PY_VERSION_HEX >= 0x03040000); /* If Python.h does not define WITH_THREAD, then the various GIL-related functions will not be defined. However, @@ -134,15 +107,13 @@ typedef unsigned long gdb_py_ulongest; #endif /* HAVE_LONG_LONG */ -#if PY_VERSION_HEX < 0x03020000 -typedef long Py_hash_t; -#endif - -/* PyMem_RawMalloc appeared in Python 3.4. For earlier versions, we can just - fall back to PyMem_Malloc. */ - -#if PY_VERSION_HEX < 0x03040000 -#define PyMem_RawMalloc PyMem_Malloc +#if PY_VERSION_HEX < 0x030a0000 +static inline PyObject * +Py_NewRef (PyObject *obj) +{ + Py_INCREF (obj); + return obj; +} #endif /* A template variable holding the format character (as for @@ -359,20 +330,13 @@ extern int gdb_python_initialized; extern PyObject *gdb_module; extern PyObject *gdb_python_module; -extern PyTypeObject value_object_type - CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("value_object"); -extern PyTypeObject block_object_type - CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("block_object"); -extern PyTypeObject symbol_object_type - CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("symbol_object"); -extern PyTypeObject event_object_type - CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object"); -extern PyTypeObject breakpoint_object_type - CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("breakpoint_object"); -extern PyTypeObject frame_object_type - CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("frame_object"); -extern PyTypeObject thread_object_type - CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("thread_object"); +extern PyTypeObject value_object_type; +extern PyTypeObject block_object_type; +extern PyTypeObject symbol_object_type; +extern PyTypeObject event_object_type; +extern PyTypeObject breakpoint_object_type; +extern PyTypeObject frame_object_type; +extern PyTypeObject thread_object_type; /* Ensure that breakpoint_object_type is initialized and return true. If breakpoint_object_type can't be initialized then set a suitable Python @@ -386,10 +350,8 @@ extern PyTypeObject thread_object_type extern bool gdbpy_breakpoint_init_breakpoint_type (); -struct gdbpy_breakpoint_object +struct gdbpy_breakpoint_object : public PyObject { - PyObject_HEAD - /* The breakpoint number according to gdb. */ int number; @@ -429,22 +391,27 @@ struct gdbpy_breakpoint_object extern gdbpy_breakpoint_object *bppy_pending_object; -struct thread_object +struct thread_object : public gdbpy_dict_wrapper { - PyObject_HEAD - /* The thread we represent. */ struct thread_info *thread; /* The Inferior object to which this thread belongs. */ PyObject *inf_obj; - - /* Dictionary holding user-added attributes. This is the __dict__ - attribute of the object. */ - PyObject *dict; }; -struct inferior_object; +using thread_map_t + = gdb::unordered_map<thread_info *, gdbpy_ref<thread_object>>; + +struct inferior_object : public gdbpy_dict_wrapper +{ + /* The inferior we represent. */ + struct inferior *inferior; + + /* thread_object instances under this inferior. This owns a + reference to each object it contains. */ + thread_map_t *threads; +}; extern struct cmd_list_element *set_python_list; extern struct cmd_list_element *show_python_list; @@ -511,14 +478,20 @@ PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length, const char *encoding, struct type *type); PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2); -PyObject *gdbpy_create_ptid_object (ptid_t ptid); + +/* Return a reference to a new Python Tuple object representing a ptid_t. + The object is a tuple containing (pid, lwp, tid). */ + +extern gdbpy_ref<> gdbpy_create_ptid_object (ptid_t ptid); + PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args); PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args); PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args); PyObject *gdbpy_parameter_value (const setting &var); gdb::unique_xmalloc_ptr<char> gdbpy_parse_command_name (const char *name, struct cmd_list_element ***base_list, - struct cmd_list_element **start_list); + struct cmd_list_element **start_list, + struct cmd_list_element **prefix_cmd = nullptr); PyObject *gdbpy_register_tui_window (PyObject *self, PyObject *args, PyObject *kw); @@ -572,6 +545,20 @@ struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj); frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj); struct gdbarch *arch_object_to_gdbarch (PyObject *obj); +/* Return true if OBJ is a gdb.Style object. OBJ must not be NULL. */ + +extern bool gdbpy_is_style (PyObject *obj); + +/* Return the ui_file_style from OBJ, a gdb.Style object. OBJ must not be + NULL. + + It is possible that OBJ is a gdb.Style object, but the underlying style + cannot be fetched for some reason. If this happens then a Python error + is set and an empty optional is returned. */ + +extern std::optional<ui_file_style> + gdbpy_style_object_to_ui_file_style (PyObject *obj); + extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args, PyObject *kw); @@ -994,8 +981,7 @@ extern PyObject *gdbpy_gdb_error; extern PyObject *gdbpy_gdb_memory_error; extern PyObject *gdbpy_gdberror_exc; -extern void gdbpy_convert_exception (const struct gdb_exception &) - CPYCHECKER_SETS_EXCEPTION; +extern void gdbpy_convert_exception (const struct gdb_exception &); /* Use this in a 'catch' block to convert the exception E to a Python exception and return value VAL to signal that an exception occurred. @@ -1009,18 +995,18 @@ gdbpy_handle_gdb_exception (T val, const gdb_exception &e) return val; } -int get_addr_from_python (PyObject *obj, CORE_ADDR *addr) - CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; +int get_addr_from_python (PyObject *obj, CORE_ADDR *addr); gdbpy_ref<> gdb_py_object_from_longest (LONGEST l); gdbpy_ref<> gdb_py_object_from_ulongest (ULONGEST l); int gdb_py_int_as_long (PyObject *, long *); -PyObject *gdb_py_generic_dict (PyObject *self, void *closure); +PyObject *gdb_py_generic_dict_getter (PyObject *self, void *closure); +PyObject *gdb_py_generic_getattro (PyObject *self, PyObject *attr); +int gdb_py_generic_setattro (PyObject *self, PyObject *attr, PyObject *value); -int gdb_pymodule_addobject (PyObject *module, const char *name, - PyObject *object) - CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; +int gdb_pymodule_addobject (PyObject *mod, const char *name, + PyObject *object); /* Return a Python string (str) object that represents SELF. SELF can be @@ -1115,6 +1101,14 @@ extern std::optional<int> gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR address, disassemble_info *info); +/* Return the gdb.Corefile object representing the core file loaded into + the program space of INF, or None if there is no core file loaded. INF + must not be NULL. If an error occurs then NULL is returned, and a + suitable Python error will be set. */ + +extern gdbpy_ref<> gdbpy_core_file_from_inferior (inferior *inf); + + /* A wrapper for PyType_Ready that also automatically registers the type in the appropriate module. Returns 0 on success, -1 on error. If MOD is supplied, then the type is added to that module. If MOD @@ -1145,4 +1139,204 @@ gdbpy_type_ready (PyTypeObject *type, PyObject *mod = nullptr) # define PyType_Ready POISONED_PyType_Ready #endif +/* A class to manage lifecycle of Python objects for objects that are "owned" + by an objfile or a gdbarch. It keeps track of Python objects and when + the "owning" object (objfile or gdbarch) is about to be freed, ensures that + all Python objects "owned" by that object are properly invalidated. + + The actual tracking of "owned" Python objects is handled externally + by storage class. Storage object is created for each owning object + on demand and it is deleted when owning object is about to be freed. + + The storage class must provide two member types: + + * obj_type - the type of Python object whose lifecycle is managed. + * val_type - the type of GDB structure the Python objects are + representing. + + It must also provide following methods: + + void add (obj_type *obj); + void remove (obj_type *obj); + + Memoizing storage must in addition to method above provide: + + obj_type *lookup (val_type *val); + + Finally it must invalidate all registered Python objects upon deletion. */ +template <typename Storage> +class gdbpy_registry +{ +public: + using obj_type = typename Storage::obj_type; + using val_type = typename Storage::val_type; + + static_assert(std::is_base_of<PyObject, obj_type>::value, + "obj_type must be a subclass of PyObject"); + + /* Register Python object OBJ as being "owned" by OWNER. When OWNER is + about to be freed, OBJ will be invalidated. */ + template <typename O> + void add (O *owner, obj_type *obj) const + { + get_storage (owner)->add (obj); + } + + /* Unregister Python object OBJ. OBJ will no longer be invalidated when + OWNER is about to be be freed. */ + template <typename O> + void remove (O *owner, obj_type *obj) const + { + get_storage (owner)->remove (obj); + } + + /* Lookup pre-existing Python object for given VAL. Return such object + if found, otherwise return NULL. This method always returns new + reference. */ + template <typename O> + obj_type *lookup (O *owner, val_type *val) const + { + obj_type *obj = get_storage (owner)->lookup (val); + Py_XINCREF (static_cast<PyObject *> (obj)); + return obj; + } + +private: + + template<typename O> + using StorageKey = typename registry<O>::template key<Storage>; + + template<typename O> + Storage *get_storage (O *owner, const StorageKey<O> &key) const + { + Storage *r = key.get (owner); + if (r == nullptr) + { + r = new Storage(); + key.set (owner, r); + } + return r; + } + + Storage *get_storage (struct objfile* objf) const + { + return get_storage (objf, m_key_for_objf); + } + + Storage *get_storage (struct gdbarch* arch) const + { + return get_storage (arch, m_key_for_arch); + } + + const registry<objfile>::key<Storage> m_key_for_objf; + const registry<gdbarch>::key<Storage> m_key_for_arch; +}; + +/* Default invalidator for Python objects. */ +template <typename P, typename V, V* P::*val_slot> +struct gdbpy_default_invalidator +{ + void operator() (P *obj) + { + obj->*val_slot = nullptr; + } +}; + +/* A "storage" implementation suitable for temporary (on-demand) objects. */ +template <typename P, + typename V, + V* P::*val_slot, + typename Invalidator = gdbpy_default_invalidator<P, V, val_slot>> +class gdbpy_tracking_registry_storage +{ +public: + using obj_type = P; + using val_type = V; + + void add (obj_type *obj) + { + gdb_assert (obj != nullptr && obj->*val_slot != nullptr); + + m_objects.insert (obj); + } + + void remove (obj_type *obj) + { + gdb_assert (obj != nullptr && obj->*val_slot != nullptr); + gdb_assert (m_objects.contains (obj)); + + m_objects.erase (obj); + } + + ~gdbpy_tracking_registry_storage () + { + Invalidator invalidate; + gdbpy_enter enter_py; + + for (auto each : m_objects) + invalidate (each); + m_objects.clear (); + } + +protected: + gdb::unordered_set<obj_type *> m_objects; +}; + +/* A "storage" implementation suitable for memoized (interned) Python objects. + + Python objects are memoized (interned) temporarily, meaning that when user + drops all their references the Python object is deallocated and removed + from storage. + */ +template <typename P, + typename V, + V* P::*val_slot, + typename Invalidator = gdbpy_default_invalidator<P, V, val_slot>> +class gdbpy_memoizing_registry_storage +{ +public: + using obj_type = P; + using val_type = V; + + void add (obj_type *obj) + { + gdb_assert (obj != nullptr && obj->*val_slot != nullptr); + + m_objects[obj->*val_slot] = obj; + } + + void remove (obj_type *obj) + { + gdb_assert (obj != nullptr && obj->*val_slot != nullptr); + gdb_assert (m_objects.contains (obj->*val_slot)); + + m_objects.erase (obj->*val_slot); + } + + obj_type *lookup (val_type *val) const + { + auto result = m_objects.find (val); + if (result != m_objects.end ()) + return result->second; + else + return nullptr; + } + + ~gdbpy_memoizing_registry_storage () + { + Invalidator invalidate; + gdbpy_enter enter_py; + + for (auto each : m_objects) + invalidate (each.second); + m_objects.clear (); + } + +protected: + gdb::unordered_map<val_type *, obj_type *> m_objects; +}; + +extern int eval_python_command (const char *command, int start_symbol, + const char *filename = nullptr); + #endif /* GDB_PYTHON_PYTHON_INTERNAL_H */ |
