diff options
-rw-r--r-- | bfd/version.h | 2 | ||||
-rw-r--r-- | gdb/NEWS | 12 | ||||
-rw-r--r-- | gdb/cli/cli-script.c | 4 | ||||
-rw-r--r-- | gdb/cli/cli-script.h | 6 | ||||
-rw-r--r-- | gdb/cli/cli-style.c | 21 | ||||
-rw-r--r-- | gdb/cli/cli-style.h | 19 | ||||
-rwxr-xr-x | gdb/contrib/gdb-add-index.sh | 4 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 6 | ||||
-rw-r--r-- | gdb/doc/python.texi | 10 | ||||
-rw-r--r-- | gdb/gcore-1.in | 4 | ||||
-rw-r--r-- | gdb/python/py-symbol.c | 73 | ||||
-rw-r--r-- | gdb/python/py-symtab.c | 181 | ||||
-rw-r--r-- | gdb/python/py-type.c | 92 | ||||
-rw-r--r-- | gdb/python/py-value.c | 40 | ||||
-rw-r--r-- | gdb/python/python-internal.h | 195 | ||||
-rw-r--r-- | gdb/python/python.c | 39 | ||||
-rw-r--r-- | gdb/solib-svr4.c | 99 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-arch.exp | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-styled-execute.exp | 109 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-symtab.exp | 28 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-type.exp | 15 | ||||
-rw-r--r-- | gdb/value.c | 54 | ||||
-rw-r--r-- | gdbserver/netbsd-low.cc | 5 |
23 files changed, 722 insertions, 301 deletions
diff --git a/bfd/version.h b/bfd/version.h index c90df07..a6afff6 100644 --- a/bfd/version.h +++ b/bfd/version.h @@ -16,7 +16,7 @@ In releases, the date is not included in either version strings or sonames. */ -#define BFD_VERSION_DATE 20250318 +#define BFD_VERSION_DATE 20250319 #define BFD_VERSION @bfd_version@ #define BFD_VERSION_STRING @bfd_version_package@ @bfd_version_string@ #define REPORT_BUGS_TO @report_bugs_to@ @@ -45,6 +45,13 @@ show riscv numeric-register-names (e.g 'x1') or their abi names (e.g. 'ra'). Defaults to 'off', matching the old behaviour (abi names). +* Changed commands + +info sharedlibrary + On Linux and FreeBSD, the addresses shown in the output of this + command are now for the full memory range allocated to the shared + library. + * Python API ** New class gdb.Color for dealing with colors. @@ -58,6 +65,11 @@ show riscv numeric-register-names was never documented in the GDB manual, so users should not have been using it. + ** gdb.execute has an additional 'styling' argument. When True, then + output will be styled. The default for this argument is True + when output is going to standard output, and False when output is + going to a string. + * Guile API ** New type <gdb:color> for dealing with colors. diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index 9131768..5decf3b 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -422,14 +422,14 @@ execute_control_commands (struct command_line *cmdlines, int from_tty) std::string execute_control_commands_to_string (struct command_line *commands, - int from_tty) + int from_tty, bool term_out) { std::string result; execute_fn_to_string (result, [&] () { execute_control_commands (commands, from_tty); - }, false); + }, term_out); return result; } diff --git a/gdb/cli/cli-script.h b/gdb/cli/cli-script.h index 23bd83e..df7316e 100644 --- a/gdb/cli/cli-script.h +++ b/gdb/cli/cli-script.h @@ -143,10 +143,12 @@ extern void execute_control_commands (struct command_line *cmdlines, /* Run execute_control_commands for COMMANDS. Capture its output into the returned string, do not display it to the screen. BATCH_FLAG - will be temporarily set to true. */ + will be temporarily set to true. When TERM_OUT is true the output is + collected with terminal behavior (e.g. with styling). When TERM_OUT is + false raw output will be collected (e.g. no styling). */ extern std::string execute_control_commands_to_string - (struct command_line *commands, int from_tty); + (struct command_line *commands, int from_tty, bool term_out); /* Exported to gdb/breakpoint.c */ diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c index 3ca30a4..b436275 100644 --- a/gdb/cli/cli-style.c +++ b/gdb/cli/cli-style.c @@ -51,6 +51,25 @@ static const char * const cli_intensities[] = { nullptr }; +/* When true styling is being temporarily suppressed. */ + +static bool scoped_disable_styling_p = false; + +/* See cli/cli-style.h. */ + +scoped_disable_styling::scoped_disable_styling () +{ + m_old_value = scoped_disable_styling_p; + scoped_disable_styling_p = true; +} + +/* See cli/cli-style.h. */ + +scoped_disable_styling::~scoped_disable_styling () +{ + scoped_disable_styling_p = m_old_value; +} + /* Return true if GDB's output terminal should support styling, otherwise, return false. This function really checks for things that indicate styling might not be supported, so a return value of false indicates @@ -91,7 +110,7 @@ disable_cli_styling () bool term_cli_styling () { - return cli_styling; + return cli_styling && !scoped_disable_styling_p; } /* See cli/cli-style.h. */ diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h index 18827ce..e94b48d 100644 --- a/gdb/cli/cli-style.h +++ b/gdb/cli/cli-style.h @@ -171,4 +171,23 @@ extern void disable_cli_styling (); /* Return true styled output is currently enabled. */ extern bool term_cli_styling (); +/* Allow styling to be temporarily suppressed without changing the value of + 'set style enabled' user setting. This is useful in, for example, the + Python gdb.execute() call which can produce unstyled output. */ +struct scoped_disable_styling +{ + /* Temporarily suppress styling without changing the value of 'set + style enabled' user setting. */ + scoped_disable_styling (); + + /* If the constructor started suppressing styling, then styling is + resumed after this destructor call. */ + ~scoped_disable_styling (); + +private: + + /* The value to restore in the destructor. */ + bool m_old_value; +}; + #endif /* GDB_CLI_CLI_STYLE_H */ diff --git a/gdb/contrib/gdb-add-index.sh b/gdb/contrib/gdb-add-index.sh index 4db1234..b299f83 100755 --- a/gdb/contrib/gdb-add-index.sh +++ b/gdb/contrib/gdb-add-index.sh @@ -22,8 +22,8 @@ GDB=${GDB:=gdb} OBJCOPY=${OBJCOPY:=objcopy} READELF=${READELF:=readelf} -PKGVERSION=@PKGVERSION@ -VERSION=@VERSION@ +PKGVERSION="@PKGVERSION@" +VERSION="@VERSION@" myname="${0##*/}" diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 4734310..e034ac5 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -22166,6 +22166,12 @@ Print the names of the shared libraries which are currently loaded that match @var{regex}. If @var{regex} is omitted then print all shared libraries that are loaded. +For each library, @value{GDBN} also lists the address range allocated +to that library if it can be determined. If the address range cannot +be determined then the address range for the @code{.text} section from +the library will be listed. If the @code{.text} section cannot be +found then no addresses will be listed. + @kindex info dll @item info dll @var{regex} This is an alias of @code{info sharedlibrary}. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 0dbb37b..50342bb 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -285,7 +285,7 @@ offered for debugging purposes only, expect them to change over time. A string containing the python directory (@pxref{Python}). @end defvar -@defun gdb.execute (command @r{[}, from_tty @r{[}, to_string@r{]]}) +@defun gdb.execute (command @r{[}, from_tty @r{[}, to_string @w{@r{[}, styling @r{]]]}}) Evaluate @var{command}, a string, as a @value{GDBN} CLI command. If a GDB exception happens while @var{command} runs, it is translated as described in @ref{Exception Handling,,Exception Handling}. @@ -302,6 +302,14 @@ returned as a string. The default is @code{False}, in which case the return value is @code{None}. If @var{to_string} is @code{True}, the @value{GDBN} virtual terminal will be temporarily set to unlimited width and height, and its pagination will be disabled; @pxref{Screen Size}. + +When @var{styling} is @code{True}, the output, whether sent to +standard output, or to a string, will have styling applied, if +@value{GDBN}'s standard output supports styling, and @kbd{show style +enabled} is @kbd{on}. When @var{styling} is @code{False} then no +styling is applied. The default for @var{styling} is @code{True} when +@var{to_string} is @code{False}, and @code{False} when @var{to_string} +is @code{True}. @end defun @defun gdb.breakpoints () diff --git a/gdb/gcore-1.in b/gdb/gcore-1.in index 129e369..c0979a5 100644 --- a/gdb/gcore-1.in +++ b/gdb/gcore-1.in @@ -20,8 +20,8 @@ # It starts up gdb, attaches to the given PID and invokes the gcore command. # -PKGVERSION=@PKGVERSION@ -VERSION=@VERSION@ +PKGVERSION="@PKGVERSION@" +VERSION="@VERSION@" # Need to check for -o option, but set default basename to "core". prefix=core diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c index 3ce1049..3028a30 100644 --- a/gdb/python/py-symbol.c +++ b/gdb/python/py-symbol.c @@ -29,12 +29,6 @@ struct symbol_object { PyObject_HEAD /* The GDB symbol structure this object is wrapping. */ struct symbol *symbol; - /* A symbol object is associated with an objfile, so keep track with - doubly-linked list, rooted in the objfile. This lets us - invalidate the underlying struct symbol when the objfile is - deleted. */ - symbol_object *prev; - symbol_object *next; }; /* Require a valid symbol. All access to symbol_object->symbol should be @@ -50,26 +44,8 @@ struct symbol_object { } \ } while (0) -/* A deleter that is used when an objfile is about to be freed. */ -struct symbol_object_deleter -{ - void operator() (symbol_object *obj) - { - while (obj) - { - symbol_object *next = obj->next; - - obj->symbol = NULL; - obj->next = NULL; - obj->prev = NULL; - - obj = next; - } - } -}; - -static const registry<objfile>::key<symbol_object, symbol_object_deleter> - sympy_objfile_data_key; +static const gdbpy_registry<gdbpy_memoizing_registry_storage<symbol_object, + symbol, &symbol_object::symbol>> sympy_registry; static PyObject * sympy_str (PyObject *self) @@ -347,19 +323,18 @@ static void set_symbol (symbol_object *obj, struct symbol *symbol) { obj->symbol = symbol; - obj->prev = NULL; - if (symbol->is_objfile_owned () - && symbol->symtab () != NULL) + if (symbol->is_objfile_owned ()) { - struct objfile *objfile = symbol->objfile (); - - obj->next = sympy_objfile_data_key.get (objfile); - if (obj->next) - obj->next->prev = obj; - sympy_objfile_data_key.set (objfile, obj); + /* Can it really happen that symbol->symtab () is NULL? */ + if (symbol->symtab () != nullptr) + { + sympy_registry.add (symbol->objfile (), obj); + } } else - obj->next = NULL; + { + sympy_registry.add (symbol->arch (), obj); + } } /* Create a new symbol object (gdb.Symbol) that encapsulates the struct @@ -369,6 +344,15 @@ symbol_to_symbol_object (struct symbol *sym) { symbol_object *sym_obj; + /* Look if there's already a gdb.Symbol object for given SYMBOL + and if so, return it. */ + if (sym->is_objfile_owned ()) + sym_obj = sympy_registry.lookup (sym->objfile (), sym); + else + sym_obj = sympy_registry.lookup (sym->arch (), sym); + if (sym_obj != nullptr) + return (PyObject*)sym_obj; + sym_obj = PyObject_New (symbol_object, &symbol_object_type); if (sym_obj) set_symbol (sym_obj, sym); @@ -390,15 +374,14 @@ sympy_dealloc (PyObject *obj) { symbol_object *sym_obj = (symbol_object *) obj; - if (sym_obj->prev) - sym_obj->prev->next = sym_obj->next; - else if (sym_obj->symbol != NULL - && sym_obj->symbol->is_objfile_owned () - && sym_obj->symbol->symtab () != NULL) - sympy_objfile_data_key.set (sym_obj->symbol->objfile (), sym_obj->next); - if (sym_obj->next) - sym_obj->next->prev = sym_obj->prev; - sym_obj->symbol = NULL; + if (sym_obj->symbol != nullptr) + { + if (sym_obj->symbol->is_objfile_owned ()) + sympy_registry.remove (sym_obj->symbol->objfile (), sym_obj); + else + sympy_registry.remove (sym_obj->symbol->arch (), sym_obj); + } + Py_TYPE (obj)->tp_free (obj); } diff --git a/gdb/python/py-symtab.c b/gdb/python/py-symtab.c index 99a5094..2381e4d 100644 --- a/gdb/python/py-symtab.c +++ b/gdb/python/py-symtab.c @@ -28,39 +28,12 @@ struct symtab_object { PyObject_HEAD /* The GDB Symbol table structure. */ struct symtab *symtab; - /* A symtab object is associated with an objfile, so keep track with - a doubly-linked list, rooted in the objfile. This allows - invalidation of the underlying struct symtab when the objfile is - deleted. */ - symtab_object *prev; - symtab_object *next; -}; - -/* This function is called when an objfile is about to be freed. - Invalidate the symbol table as further actions on the symbol table - would result in bad data. All access to obj->symtab should be - gated by STPY_REQUIRE_VALID which will raise an exception on - invalid symbol tables. */ -struct stpy_deleter -{ - void operator() (symtab_object *obj) - { - while (obj) - { - symtab_object *next = obj->next; - - obj->symtab = NULL; - obj->next = NULL; - obj->prev = NULL; - obj = next; - } - } }; extern PyTypeObject symtab_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("symtab_object"); -static const registry<objfile>::key<symtab_object, stpy_deleter> - stpy_objfile_data_key; +static const gdbpy_registry<gdbpy_memoizing_registry_storage<symtab_object, + symtab, &symtab_object::symtab>> stpy_registry; /* Require a valid symbol table. All access to symtab_object->symtab should be gated by this call. */ @@ -77,8 +50,6 @@ static const registry<objfile>::key<symtab_object, stpy_deleter> struct sal_object { PyObject_HEAD - /* The GDB Symbol table structure. */ - PyObject *symtab; /* The GDB Symbol table and line structure. */ struct symtab_and_line *sal; /* A Symtab and line object is associated with an objfile, so keep @@ -94,34 +65,19 @@ struct sal_object { data. All access to obj->sal should be gated by SALPY_REQUIRE_VALID which will raise an exception on invalid symbol table and line objects. */ -struct salpy_deleter +struct salpy_invalidator { void operator() (sal_object *obj) { - gdbpy_enter enter_py; - - while (obj) - { - sal_object *next = obj->next; - - gdbpy_ref<> tmp (obj->symtab); - obj->symtab = Py_None; - Py_INCREF (Py_None); - - obj->next = NULL; - obj->prev = NULL; - xfree (obj->sal); - obj->sal = NULL; - - obj = next; - } + xfree (obj->sal); + obj->sal = nullptr; } }; extern PyTypeObject sal_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("sal_object"); -static const registry<objfile>::key<sal_object, salpy_deleter> - salpy_objfile_data_key; +static const gdbpy_registry<gdbpy_tracking_registry_storage<sal_object, + symtab_and_line, &sal_object::sal, salpy_invalidator>> salpy_registry; /* Require a valid symbol table and line object. All access to sal_object->sal should be gated by this call. */ @@ -272,18 +228,15 @@ salpy_str (PyObject *self) { const char *filename; sal_object *sal_obj; - struct symtab_and_line *sal = NULL; + struct symtab_and_line *sal = nullptr; SALPY_REQUIRE_VALID (self, sal); sal_obj = (sal_object *) self; - if (sal_obj->symtab == Py_None) + if (sal_obj->sal->symtab == nullptr) filename = "<unknown>"; else - { - symtab *symtab = symtab_object_to_symtab (sal_obj->symtab); - filename = symtab_to_filename_for_display (symtab); - } + filename = symtab_to_filename_for_display (sal_obj->sal->symtab); return PyUnicode_FromFormat ("symbol and line for %s, line %d", filename, sal->line); @@ -292,16 +245,12 @@ salpy_str (PyObject *self) static void stpy_dealloc (PyObject *obj) { - symtab_object *symtab = (symtab_object *) obj; - - if (symtab->prev) - symtab->prev->next = symtab->next; - else if (symtab->symtab) - stpy_objfile_data_key.set (symtab->symtab->compunit ()->objfile (), - symtab->next); - if (symtab->next) - symtab->next->prev = symtab->prev; - symtab->symtab = NULL; + symtab_object *symtab_obj = (symtab_object *) obj; + + if (symtab_obj->symtab != nullptr) + stpy_registry.remove (symtab_obj->symtab->compunit ()->objfile(), + symtab_obj); + Py_TYPE (obj)->tp_free (obj); } @@ -346,13 +295,13 @@ static PyObject * salpy_get_symtab (PyObject *self, void *closure) { struct symtab_and_line *sal; - sal_object *self_sal = (sal_object *) self; SALPY_REQUIRE_VALID (self, sal); - Py_INCREF (self_sal->symtab); - - return (PyObject *) self_sal->symtab; + if (sal->symtab == nullptr) + Py_RETURN_NONE; + else + return symtab_to_symtab_object (sal->symtab); } /* Implementation of gdb.Symtab_and_line.is_valid (self) -> Boolean. @@ -375,17 +324,10 @@ salpy_dealloc (PyObject *self) { sal_object *self_sal = (sal_object *) self; - if (self_sal->prev) - self_sal->prev->next = self_sal->next; - else if (self_sal->symtab != Py_None) - salpy_objfile_data_key.set - (symtab_object_to_symtab (self_sal->symtab)->compunit ()->objfile (), - self_sal->next); - - if (self_sal->next) - self_sal->next->prev = self_sal->prev; + if (self_sal->sal != nullptr && self_sal->sal->symtab != nullptr) + salpy_registry.remove (self_sal->sal->symtab->compunit ()->objfile (), + self_sal); - Py_DECREF (self_sal->symtab); xfree (self_sal->sal); Py_TYPE (self)->tp_free (self); } @@ -395,48 +337,20 @@ salpy_dealloc (PyObject *self) Also, register the sal_object life-cycle with the life-cycle of the object file associated with this sal, if needed. If a failure occurs during the sal population, this function will return -1. */ -static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION +static void set_sal (sal_object *sal_obj, struct symtab_and_line sal) { - PyObject *symtab_obj; - - if (sal.symtab) - { - symtab_obj = symtab_to_symtab_object (sal.symtab); - /* If a symtab existed in the sal, but it cannot be duplicated, - we exit. */ - if (symtab_obj == NULL) - return -1; - } - else - { - symtab_obj = Py_None; - Py_INCREF (Py_None); - } - sal_obj->sal = ((struct symtab_and_line *) xmemdup (&sal, sizeof (struct symtab_and_line), sizeof (struct symtab_and_line))); - sal_obj->symtab = symtab_obj; - sal_obj->prev = NULL; + sal_obj->prev = nullptr; + sal_obj->next = nullptr; /* If the SAL does not have a symtab, we do not add it to the objfile cleanup observer linked list. */ - if (sal_obj->symtab != Py_None) - { - symtab *symtab = symtab_object_to_symtab (sal_obj->symtab); - - sal_obj->next - = salpy_objfile_data_key.get (symtab->compunit ()->objfile ()); - if (sal_obj->next) - sal_obj->next->prev = sal_obj; - - salpy_objfile_data_key.set (symtab->compunit ()->objfile (), sal_obj); - } - else - sal_obj->next = NULL; - - return 0; + symtab *symtab = sal_obj->sal->symtab; + if (symtab != nullptr) + salpy_registry.add (symtab->compunit ()->objfile (), sal_obj); } /* Given a symtab, and a symtab_object that has previously been @@ -448,16 +362,8 @@ static void set_symtab (symtab_object *obj, struct symtab *symtab) { obj->symtab = symtab; - obj->prev = NULL; - if (symtab) - { - obj->next = stpy_objfile_data_key.get (symtab->compunit ()->objfile ()); - if (obj->next) - obj->next->prev = obj; - stpy_objfile_data_key.set (symtab->compunit ()->objfile (), obj); - } - else - obj->next = NULL; + if (symtab != nullptr) + stpy_registry.add (symtab->compunit ()->objfile (), obj); } /* Create a new symbol table (gdb.Symtab) object that encapsulates the @@ -467,6 +373,16 @@ symtab_to_symtab_object (struct symtab *symtab) { symtab_object *symtab_obj; + /* Look if there's already a gdb.Symtab object for given SYMTAB + and if so, return it. */ + if (symtab != nullptr) + { + symtab_obj = stpy_registry.lookup (symtab->compunit ()->objfile (), + symtab); + if (symtab_obj != nullptr) + return (PyObject*)symtab_obj; + } + symtab_obj = PyObject_New (symtab_object, &symtab_object_type); if (symtab_obj) set_symtab (symtab_obj, symtab); @@ -479,14 +395,13 @@ symtab_to_symtab_object (struct symtab *symtab) PyObject * symtab_and_line_to_sal_object (struct symtab_and_line sal) { - gdbpy_ref<sal_object> sal_obj (PyObject_New (sal_object, &sal_object_type)); - if (sal_obj != NULL) - { - if (set_sal (sal_obj.get (), sal) < 0) - return NULL; - } + sal_object *sal_obj; + + sal_obj = PyObject_New (sal_object, &sal_object_type); + if (sal_obj != nullptr) + set_sal (sal_obj, sal); - return (PyObject *) sal_obj.release (); + return (PyObject *) sal_obj; } /* Return struct symtab_and_line reference that is wrapped by this @@ -560,7 +475,7 @@ PyTypeObject symtab_object_type = { "gdb.Symtab", /*tp_name*/ sizeof (symtab_object), /*tp_basicsize*/ 0, /*tp_itemsize*/ - stpy_dealloc, /*tp_dealloc*/ + stpy_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index 11a96d5..24e754d 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -32,12 +32,6 @@ struct type_object { PyObject_HEAD struct type *type; - - /* If a Type object is associated with an objfile, it is kept on a - doubly-linked list, rooted in the objfile. This lets us copy the - underlying struct type when the objfile is deleted. */ - struct type_object *prev; - struct type_object *next; }; extern PyTypeObject type_object_type @@ -1162,75 +1156,61 @@ typy_richcompare (PyObject *self, PyObject *other, int op) -/* Deleter that saves types when an objfile is being destroyed. */ -struct typy_deleter +/* Forward declaration, see below. */ +static void set_type (type_object *obj, struct type *type); + +/* Invalidator that saves types when an objfile is being destroyed. */ +struct typy_invalidator { void operator() (type_object *obj) { - if (!gdb_python_initialized) - return; - - /* This prevents another thread from freeing the objects we're - operating on. */ - gdbpy_enter enter_py; - - copied_types_hash_t copied_types; - - while (obj) + if (obj->type->is_objfile_owned ()) { - type_object *next = obj->next; + copied_types_hash_t copied_types; - copied_types.clear (); - obj->type = copy_type_recursive (obj->type, copied_types); - - obj->next = NULL; - obj->prev = NULL; - - obj = next; + /* Set a copied (now arch-owned) type. As a side-effect this + adds OBJ to per-arch list. We do not need to remove it from + per-objfile list since the objfile is going to go completely + anyway. */ + set_type (obj, copy_type_recursive (obj->type, copied_types)); + } + else + { + obj->type = nullptr; } } }; -static const registry<objfile>::key<type_object, typy_deleter> - typy_objfile_data_key; +static const gdbpy_registry<gdbpy_memoizing_registry_storage<type_object, + type, &type_object::type, typy_invalidator>> typy_registry; static void set_type (type_object *obj, struct type *type) { + gdb_assert (type != nullptr); + obj->type = type; - obj->prev = NULL; - if (type != nullptr && type->objfile_owner () != nullptr) - { - struct objfile *objfile = type->objfile_owner (); - obj->next = typy_objfile_data_key.get (objfile); - if (obj->next) - obj->next->prev = obj; - typy_objfile_data_key.set (objfile, obj); - } + if (type->objfile_owner () != nullptr) + typy_registry.add (type->objfile_owner (), obj); else - obj->next = NULL; + typy_registry.add (type->arch_owner (), obj); } static void typy_dealloc (PyObject *obj) { - type_object *type = (type_object *) obj; + type_object *type_obj = (type_object *) obj; - if (type->prev) - type->prev->next = type->next; - else if (type->type != nullptr && type->type->objfile_owner () != nullptr) + if (type_obj->type != nullptr) { - /* Must reset head of list. */ - struct objfile *objfile = type->type->objfile_owner (); - - if (objfile) - typy_objfile_data_key.set (objfile, type->next); + if (type_obj->type->is_objfile_owned ()) + typy_registry.remove (type_obj->type->objfile_owner (), type_obj); + else + typy_registry.remove (type_obj->type->arch_owner (), type_obj); } - if (type->next) - type->next->prev = type->prev; - Py_TYPE (type)->tp_free (type); + Py_TYPE (obj)->tp_free (obj); } /* Return number of fields ("length" of the field dictionary). */ @@ -1473,6 +1453,16 @@ type_to_type_object (struct type *type) return gdbpy_handle_gdb_exception (nullptr, except); } + /* Look if there's already a gdb.Type object for given TYPE + and if so, return it. */ + if (type->is_objfile_owned ()) + type_obj = typy_registry.lookup (type->objfile_owner (), type); + else + type_obj = typy_registry.lookup (type->arch_owner (), type); + + if (type_obj != nullptr) + return (PyObject*)type_obj; + type_obj = PyObject_New (type_object, &type_object_type); if (type_obj) set_type (type_obj, type); @@ -1684,7 +1674,7 @@ PyTypeObject type_object_type = "gdb.Type", /*tp_name*/ sizeof (type_object), /*tp_basicsize*/ 0, /*tp_itemsize*/ - typy_dealloc, /*tp_dealloc*/ + typy_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c index 02c50b4..cf1e3ea 100644 --- a/gdb/python/py-value.c +++ b/gdb/python/py-value.c @@ -60,7 +60,6 @@ struct value_object { struct value_object *prev; struct value *value; PyObject *address; - PyObject *type; PyObject *dynamic_type; PyObject *content_bytes; }; @@ -84,8 +83,6 @@ valpy_clear_value (value_object *self) self->value = nullptr; Py_CLEAR (self->address); - Py_CLEAR (self->type); - Py_CLEAR (self->dynamic_type); Py_CLEAR (self->content_bytes); } @@ -438,14 +435,7 @@ valpy_get_type (PyObject *self, void *closure) { value_object *obj = (value_object *) self; - if (!obj->type) - { - obj->type = type_to_type_object (obj->value->type ()); - if (!obj->type) - return NULL; - } - Py_INCREF (obj->type); - return obj->type; + return type_to_type_object (obj->value->type ()); } /* Return dynamic type of the value. */ @@ -454,13 +444,7 @@ static PyObject * valpy_get_dynamic_type (PyObject *self, void *closure) { value_object *obj = (value_object *) self; - struct type *type = NULL; - - if (obj->dynamic_type != NULL) - { - Py_INCREF (obj->dynamic_type); - return obj->dynamic_type; - } + struct type *type = nullptr; try { @@ -493,23 +477,14 @@ valpy_get_dynamic_type (PyObject *self, void *closure) else if (type->code () == TYPE_CODE_STRUCT) type = value_rtti_type (val, NULL, NULL, NULL); else - { - /* Re-use object's static type. */ - type = NULL; - } + type = val->type (); } catch (const gdb_exception &except) { return gdbpy_handle_gdb_exception (nullptr, except); } - if (type == NULL) - obj->dynamic_type = valpy_get_type (self, NULL); - else - obj->dynamic_type = type_to_type_object (type); - - Py_XINCREF (obj->dynamic_type); - return obj->dynamic_type; + return type_to_type_object (type); } /* Implementation of gdb.Value.lazy_string ([encoding] [, length]) -> @@ -1937,15 +1912,14 @@ value_to_value_object (struct value *val) value_object *val_obj; val_obj = PyObject_New (value_object, &value_object_type); - if (val_obj != NULL) + if (val_obj != nullptr) { val->incref (); val_obj->value = val; val_obj->next = nullptr; val_obj->prev = nullptr; - val_obj->address = NULL; - val_obj->type = NULL; - val_obj->dynamic_type = NULL; + val_obj->address = nullptr; + val_obj->dynamic_type = nullptr; val_obj->content_bytes = nullptr; note_value (val_obj); } diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index c48f260..5e67073 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -22,6 +22,7 @@ #include "extension.h" #include "extension-priv.h" +#include "registry.h" /* These WITH_* macros are defined by the CPython API checker that comes with the Python plugin for GCC. See: @@ -1145,4 +1146,198 @@ 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; + + /* 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 (obj); + return obj; + } + +private: + + template<typename O> + using StorageKey = typename registry<O>::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; +}; + #endif /* GDB_PYTHON_PYTHON_INTERNAL_H */ diff --git a/gdb/python/python.c b/gdb/python/python.c index 8f8030c..2aaa30c 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -36,6 +36,7 @@ #include "run-on-main-thread.h" #include "observable.h" #include "build-id.h" +#include "cli/cli-style.h" #if GDB_SELF_TEST #include "gdbsupport/selftest.h" @@ -660,12 +661,14 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) const char *arg; PyObject *from_tty_obj = nullptr; PyObject *to_string_obj = nullptr; - static const char *keywords[] = { "command", "from_tty", "to_string", - nullptr }; + PyObject *styling = nullptr; + static const char *keywords[] + = { "command", "from_tty", "to_string", "styling", nullptr }; - if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg, + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!O!", keywords, &arg, &PyBool_Type, &from_tty_obj, - &PyBool_Type, &to_string_obj)) + &PyBool_Type, &to_string_obj, + &PyBool_Type, &styling)) return nullptr; bool from_tty = false; @@ -686,6 +689,15 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) to_string = (cmp != 0); } + bool styling_p = !to_string; + if (styling != nullptr) + { + int cmp = PyObject_IsTrue (styling); + if (cmp < 0) + return nullptr; + styling_p = (cmp != 0); + } + std::string to_string_res; scoped_restore preventer = prevent_dont_repeat (); @@ -745,14 +757,29 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) scoped_restore save_uiout = make_scoped_restore (¤t_uiout); + /* If the Python 'styling' argument was False then temporarily + disable styling. Otherwise, don't do anything, styling could + already be disabled for some other reason, we shouldn't override + that and force styling on. */ + std::optional<scoped_disable_styling> disable_styling; + if (!styling_p) + disable_styling.emplace (); + /* Use the console interpreter uiout to have the same print format for console or MI. */ interp = interp_lookup (current_ui, "console"); current_uiout = interp->interp_ui_out (); if (to_string) - to_string_res = execute_control_commands_to_string (lines.get (), - from_tty); + { + /* Pass 'true' here to always request styling, however, if + the scoped_disable_styling disabled styling, or the user + has globally disabled styling, then the output will not be + styled. */ + to_string_res + = execute_control_commands_to_string (lines.get (), from_tty, + true); + } else execute_control_commands (lines.get (), from_tty); } diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 8378eca..398123f 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -3093,6 +3093,37 @@ svr4_truncate_ptr (CORE_ADDR addr) return addr & (((CORE_ADDR) 1 << gdbarch_ptr_bit (current_inferior ()->arch ())) - 1); } +/* Find the LOAD-able program header in ABFD that contains ASECT. Return + NULL if no such header can be found. */ + +static Elf_Internal_Phdr * +find_loadable_elf_internal_phdr (bfd *abfd, bfd_section *asect) +{ + Elf_Internal_Ehdr *ehdr = elf_tdata (abfd)->elf_header; + Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr; + + for (int i = 0; i < ehdr->e_phnum; i++) + { + if (phdr[i].p_type == PT_LOAD) + { + /* A section without the SEC_LOAD flag is a no-bits section + (e.g. .bss) and has zero size within ABFD. */ + ULONGEST section_file_size + = (((bfd_section_flags (asect) & SEC_LOAD) != 0) + ? bfd_section_size (asect) + : 0); + + if (asect->filepos >= phdr[i].p_offset + && ((asect->filepos + section_file_size) + <= (phdr[i].p_offset + phdr[i].p_filesz))) + return &phdr[i]; + } + } + + return nullptr; +} + +/* Implement solib_ops::relocate_section_addresses() for svr4 targets. */ static void svr4_relocate_section_addresses (solib &so, target_section *sec) @@ -3101,6 +3132,74 @@ svr4_relocate_section_addresses (solib &so, target_section *sec) sec->addr = svr4_truncate_ptr (sec->addr + lm_addr_check (so, abfd)); sec->endaddr = svr4_truncate_ptr (sec->endaddr + lm_addr_check (so, abfd)); + + struct bfd_section *asect = sec->the_bfd_section; + gdb_assert (asect != nullptr); + + /* Update the address range of SO based on ASECT. */ + if ((bfd_section_flags (asect) & SEC_ALLOC) != 0 + && bfd_get_flavour (abfd) == bfd_target_elf_flavour) + { + /* First, SO must cover the contents of ASECT. */ + if (so.addr_low == 0 || sec->addr < so.addr_low) + so.addr_low = sec->addr; + + if (so.addr_high == 0 || sec->endaddr > so.addr_high) + so.addr_high = sec->endaddr; + + gdb_assert (so.addr_low <= so.addr_high); + + /* But we can do better. Find the program header which contains + ASECT, and figure out its extents. This gives an larger possible + region for SO. */ + Elf_Internal_Phdr *phdr = find_loadable_elf_internal_phdr (abfd, asect); + + if (phdr != nullptr) + { + /* Figure out the alignment required by this segment. */ + ULONGEST minpagesize = get_elf_backend_data (abfd)->minpagesize; + ULONGEST segment_alignment + = std::max (minpagesize, static_cast<ULONGEST> (phdr->p_align)); + ULONGEST at_pagesz; + if (target_auxv_search (AT_PAGESZ, &at_pagesz) > 0) + segment_alignment = std::max (segment_alignment, at_pagesz); + + /* The offset of this section within the segment. */ + ULONGEST section_offset = asect->vma - phdr->p_vaddr; + + /* The start address for the segment, without alignment. */ + CORE_ADDR unaligned_start = sec->addr - section_offset; + + /* And the start address with downward alignment. */ + CORE_ADDR aligned_start + = align_down (unaligned_start, segment_alignment); + + /* The end address of the segment depends on its size. Start + with the size as described in the ELF. This check of the + memory size and file size is what BFD does, so assume it + knows best and copy this logic. */ + ULONGEST seg_size = std::max (phdr->p_memsz, phdr->p_filesz); + + /* But by aligning the start address down we need to also include + that difference in the segment size. */ + seg_size += (unaligned_start - aligned_start); + + /* And align the segment size upward. */ + seg_size = align_up (seg_size, segment_alignment); + + /* Finally, we can compute the end address. */ + CORE_ADDR end = aligned_start + seg_size; + + /* And now we can update the extend of SO. */ + if (so.addr_low == 0 || aligned_start < so.addr_low) + so.addr_low = aligned_start; + + if (so.addr_high == 0 || end > so.addr_high) + so.addr_high = end; + + gdb_assert (so.addr_low <= so.addr_high); + } + } } diff --git a/gdb/testsuite/gdb.python/py-arch.exp b/gdb/testsuite/gdb.python/py-arch.exp index c76fc778..c294011 100644 --- a/gdb/testsuite/gdb.python/py-arch.exp +++ b/gdb/testsuite/gdb.python/py-arch.exp @@ -108,6 +108,11 @@ gdb_test "python print(arch.void_type())" \ "void" \ "get void type" +# Test type identity +gdb_test "python print(arch.integer_type(32) is arch.integer_type(32))" \ + "True" \ + "arch.integer_type(32) always return the same Python object" + # Test for gdb.architecture_names(). First we're going to grab the # complete list of architecture names using the 'complete' command. set arch_names [] diff --git a/gdb/testsuite/gdb.python/py-styled-execute.exp b/gdb/testsuite/gdb.python/py-styled-execute.exp new file mode 100644 index 0000000..0b27c63 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-styled-execute.exp @@ -0,0 +1,109 @@ +# Copyright (C) 2025 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/>. + +# Check the the output of gdb.execute can be styled or not depending +# on the value of the third argument passed to gdb.execute. + +require allow_python_tests + +load_lib gdb-python.exp + +# Use gdb.execute() to run CMD passing different argument values. The +# output should match either STYLED_RE or UNSTYLED_RE depending on +# whether the 'styling' argument is True or False. +proc do_gdb_execute { cmd styled_re unstyled_re } { + gdb_test "python gdb.execute('$cmd')" $styled_re + + foreach from_tty { True False } { + gdb_test \ + "python gdb.execute('$cmd', $from_tty)" \ + $styled_re + gdb_test \ + "python gdb.execute('$cmd', $from_tty, False)" \ + $styled_re + gdb_test \ + "python gdb.execute('$cmd', $from_tty, False, True)" \ + $styled_re + gdb_test \ + "python gdb.execute('$cmd', $from_tty, False, False)" \ + $unstyled_re + gdb_test \ + "python print(gdb.execute('$cmd', $from_tty, True), end='')" \ + $unstyled_re + gdb_test \ + "python print(gdb.execute('$cmd', $from_tty, True, False), end='')" \ + $unstyled_re + gdb_test \ + "python print(gdb.execute('$cmd', $from_tty, True, True), end='')" \ + $styled_re + } +} + +# Test that the output from gdb.execute is styled or not based on the +# arguments passed in. +proc test_gdb_execute_styling {} { + clean_restart + + # Two possible outputs, BASIC_RE, the unstyled output text, or + # STYLED_RE, the same things, but with styling applied. + set text "\"version\" style" + set styled_text \ + [style "\"" version][style "version" version][style "\" style" version] + set basic_re "The $text foreground color is: \[^\r\n\]+" + set styled_re "The $styled_text foreground color is: \[^\r\n\]+" + + # The command we'll run. It's output matches the above regexp. + set show_style_version_cmd "show style version foreground" + + # Another command we'll run. The output of this command is never + # styled, but we run this to check that the output doesn't change + # even when gdb.execute() asks for styled, or unstyled output. + set show_style_enabled_cmd "show style enabled" + + with_test_prefix "with style enabled on" { + do_gdb_execute $show_style_version_cmd $styled_re $basic_re + + # This time, print the value of 'show style enabled'. This + # output is unstyled, so there's only one regexp. The + # interesting thing here is that we don't expect the output to + # change, even when gdb.execute() is printing unstyled output. + # The "styling=False" argument to gdb.execute() is separate to + # the 'set style enabled on|off' setting. + set re "CLI output styling is enabled\\." + do_gdb_execute $show_style_enabled_cmd $re $re + } + + gdb_test_no_output "set style enabled off" + + with_test_prefix "with style enabled off" { + # With 'set style enabled off' in use, even a request to + # gdb.execute() to produce styled output should produce + # unstyled output. The assumption is that 'set style enabled + # off' is done by the user, while the gdb.execute() is likely + # from some Python extension. The users request for no + # styling overrules the extensions request for styled output. + do_gdb_execute $show_style_version_cmd $basic_re $basic_re + + # Now check that even when we request styled output, the 'show + # style enabled' value is always reported as disabled. + set re "CLI output styling is disabled\\." + do_gdb_execute $show_style_enabled_cmd $re $re + } +} + +# Run the tests. +with_ansi_styling_terminal { + test_gdb_execute_styling +} diff --git a/gdb/testsuite/gdb.python/py-symtab.exp b/gdb/testsuite/gdb.python/py-symtab.exp index 4765ef5..18d77a0 100644 --- a/gdb/testsuite/gdb.python/py-symtab.exp +++ b/gdb/testsuite/gdb.python/py-symtab.exp @@ -90,6 +90,34 @@ gdb_test_multiple "python print (\"simple_struct\" in static_symbols)" \ } } +# Test symtab identity +gdb_test "python print (symtab is symtab)"\ + "True" \ + "test symtab identity 1" +gdb_test "python print (symtab is gdb.selected_frame().find_sal().symtab)"\ + "True" \ + "test symtab identity 2" +gdb_test "python print (sal.symtab is gdb.selected_frame().find_sal().symtab)"\ + "True" \ + "test symtab identity 3" +gdb_test "python print (symtab is not \"xxx\")"\ + "True" \ + "test symtab non-identity with non-symtab" + +# Test symtab equality +gdb_test "python print (symtab == symtab)"\ + "True" \ + "test symtab equality 1" +gdb_test "python print (symtab == gdb.selected_frame().find_sal().symtab)"\ + "True" \ + "test symtab equality 2" +gdb_test "python print (sal.symtab == gdb.selected_frame().find_sal().symtab)"\ + "True" \ + "test symtab equality 3" +gdb_test "python print (symtab != \"xxx\")"\ + "True" \ + "test symtab non-equality with non-symtab" + # Test is_valid when the objfile is unloaded. This must be the last # test as it unloads the object file in GDB. gdb_unload diff --git a/gdb/testsuite/gdb.python/py-type.exp b/gdb/testsuite/gdb.python/py-type.exp index 7e469c9..c9d4353 100644 --- a/gdb/testsuite/gdb.python/py-type.exp +++ b/gdb/testsuite/gdb.python/py-type.exp @@ -324,6 +324,19 @@ proc test_type_equality {} { } } +# Test type identity +proc test_type_identity {} { + gdb_test_no_output "python v1 = gdb.parse_and_eval('global_unsigned_int')" + gdb_test_no_output "python v2 = gdb.parse_and_eval('global_unsigned_int')" + + gdb_test "python print(v1.type is v2.type)" "True" + + gdb_test_no_output "python t1 = gdb.lookup_type ('char')" + gdb_test_no_output "python t2 = gdb.lookup_type ('char')" + + gdb_test "python print(t1 is t2)" "True" +} + # Test the gdb.Type.is_scalar property. proc test_is_scalar { lang } { if {$lang == "c++"} { @@ -376,6 +389,7 @@ if { [build_inferior "${binfile}" "c"] == 0 } { test_is_scalar "c" test_is_signed "c" test_type_equality + test_type_identity } } @@ -392,6 +406,7 @@ if { [build_inferior "${binfile}-cxx" "c++"] == 0 } { test_is_scalar "c++" test_is_signed "c++" test_type_equality + test_type_identity } } diff --git a/gdb/value.c b/gdb/value.c index e498632..ddc0959 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -55,10 +55,17 @@ /* Definition of a user function. */ struct internal_function { + internal_function (std::string name, internal_function_fn_noside handler, + void *cookie) + : name (std::move (name)), + handler (handler), + cookie (cookie) + {} + /* The name of the function. It is a bit odd to have this in the function itself -- the user might use a differently-named convenience variable to hold the function. */ - char *name; + std::string name; /* The handler. */ internal_function_fn_noside handler; @@ -67,6 +74,8 @@ struct internal_function void *cookie; }; +using internal_function_up = std::unique_ptr<internal_function>; + /* Returns true if the ranges defined by [offset1, offset1+len1) and [offset2, offset2+len2) overlap. */ @@ -1865,6 +1874,19 @@ struct internalvar : name (std::move (name)) {} + internalvar (internalvar &&other) + : name (std::move(other.name)), + kind (other.kind), + u (other.u) + { + other.kind = INTERNALVAR_VOID; + } + + ~internalvar () + { + clear_internalvar (this); + } + std::string name; /* We support various different kinds of content of an internal variable. @@ -2277,13 +2299,13 @@ set_internalvar_string (struct internalvar *var, const char *string) } static void -set_internalvar_function (struct internalvar *var, struct internal_function *f) +set_internalvar_function (internalvar *var, internal_function_up f) { /* Clean up old contents. */ clear_internalvar (var); var->kind = INTERNALVAR_FUNCTION; - var->u.fn.function = f; + var->u.fn.function = f.release (); var->u.fn.canonical = 1; /* Variables installed here are always the canonical version. */ } @@ -2302,6 +2324,10 @@ clear_internalvar (struct internalvar *var) xfree (var->u.string); break; + case INTERNALVAR_FUNCTION: + delete var->u.fn.function; + break; + default: break; } @@ -2316,18 +2342,6 @@ internalvar_name (const struct internalvar *var) return var->name.c_str (); } -static struct internal_function * -create_internal_function (const char *name, - internal_function_fn_noside handler, void *cookie) -{ - struct internal_function *ifn = new (struct internal_function); - - ifn->name = xstrdup (name); - ifn->handler = handler; - ifn->cookie = cookie; - return ifn; -} - const char * value_internal_function_name (struct value *val) { @@ -2338,7 +2352,7 @@ value_internal_function_name (struct value *val) result = get_internalvar_function (VALUE_INTERNALVAR (val), &ifn); gdb_assert (result); - return ifn->name; + return ifn->name.c_str (); } struct value * @@ -2373,11 +2387,9 @@ static struct cmd_list_element * do_add_internal_function (const char *name, const char *doc, internal_function_fn_noside handler, void *cookie) { - struct internal_function *ifn; - struct internalvar *var = lookup_internalvar (name); - - ifn = create_internal_function (name, handler, cookie); - set_internalvar_function (var, ifn); + set_internalvar_function (lookup_internalvar (name), + std::make_unique<internal_function> (name, handler, + cookie)); return add_cmd (name, no_class, function_command, doc, &functionlist); } diff --git a/gdbserver/netbsd-low.cc b/gdbserver/netbsd-low.cc index 9e7314b..0c75f4b 100644 --- a/gdbserver/netbsd-low.cc +++ b/gdbserver/netbsd-low.cc @@ -453,7 +453,10 @@ netbsd_process_target::detach (process_info *process) void netbsd_process_target::mourn (struct process_info *proc) { - proc->for_each_thread (remove_thread); + proc->for_each_thread ([proc] (thread_info *thread) + { + proc->remove_thread (thread); + }); remove_process (proc); } |