diff options
Diffstat (limited to 'gdb')
36 files changed, 976 insertions, 539 deletions
@@ -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..5484245 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. */ @@ -353,7 +372,9 @@ set_style_enabled (const char *args, int from_tty, struct cmd_list_element *c) warning ("The current terminal doesn't support styling. Styled output " "might not appear as expected."); - g_source_cache.clear (); + /* It is not necessary to flush the source cache here. The source cache + tracks whether entries are styled or not. */ + gdb::observers::styling_changed.notify (); } 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/dwarf2/attribute.h b/gdb/dwarf2/attribute.h index 4dce04d..ce6c563 100644 --- a/gdb/dwarf2/attribute.h +++ b/gdb/dwarf2/attribute.h @@ -105,8 +105,8 @@ struct attribute /* Return an unsigned constant value. This only handles constant forms (i.e., form_is_constant -- and not the extended list of "unsigned" forms) and assumes an unsigned value is desired. This - can intended for use with DWARF-defined enumerations like DW_CC_* - or DW_INL_*, but also in situations where a nonnegative constant + can be used with DWARF-defined enumerations like DW_CC_* or + DW_INL_*, but also in situations where a nonnegative constant integer is specified by DWARF. If a signed form and negative value is used, or if a non-constant diff --git a/gdb/dwarf2/die.c b/gdb/dwarf2/die.c index 500d7bf..9437c2f 100644 --- a/gdb/dwarf2/die.c +++ b/gdb/dwarf2/die.c @@ -184,9 +184,9 @@ dump_die_1 (struct ui_file *f, int level, int max_level, struct die_info *die) } } - if (die->sibling != NULL && level > 0) + if (die->next != NULL && level > 0) { - dump_die_1 (f, level, max_level, die->sibling); + dump_die_1 (f, level, max_level, die->next); } } diff --git a/gdb/dwarf2/die.h b/gdb/dwarf2/die.h index 770964e..cffb5cb 100644 --- a/gdb/dwarf2/die.h +++ b/gdb/dwarf2/die.h @@ -22,6 +22,7 @@ #include "complaints.h" #include "dwarf2/attribute.h" +#include "gdbsupport/next-iterator.h" /* This data structure holds a complete die structure. */ struct die_info @@ -103,6 +104,13 @@ struct die_info return 0; } + /* Return a range suitable for iterating over the children of this + DIE. */ + next_range<die_info> children () const + { + return next_range<die_info> (child); + } + /* DWARF-2 tag for this DIE. */ ENUM_BITFIELD(dwarf_tag) tag : 16; @@ -128,9 +136,9 @@ struct die_info /* The dies in a compilation unit form an n-ary tree. PARENT points to this die's parent; CHILD points to the first child of this node; and all the children of a given node are chained - together via their SIBLING fields. */ + together via their NEXT fields. */ struct die_info *child; /* Its first child, if any. */ - struct die_info *sibling; /* Its next sibling, if any. */ + struct die_info *next; /* Its next sibling, if any. */ struct die_info *parent; /* Its parent, if any. */ /* An array of attributes, with NUM_ATTRS elements. There may be diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index b9040a5..8875e97 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -5456,12 +5456,11 @@ dwarf2_compute_name (const char *name, if (lang == language_cplus && strchr (name, '<') == NULL) { struct attribute *attr; - struct die_info *child; int first = 1; die->building_fullname = 1; - for (child = die->child; child != NULL; child = child->sibling) + for (die_info *child : die->children ()) { struct type *type; LONGEST value; @@ -5825,7 +5824,7 @@ read_import_statement (struct die_info *die, struct dwarf2_cu *cu) { struct objfile *objfile = cu->per_objfile->objfile; struct attribute *import_attr; - struct die_info *imported_die, *child_die; + struct die_info *imported_die; struct dwarf2_cu *imported_cu; const char *imported_name; const char *imported_name_prefix; @@ -5908,10 +5907,8 @@ read_import_statement (struct die_info *die, struct dwarf2_cu *cu) else canonical_name = imported_name; - if (die->tag == DW_TAG_imported_module - && cu->lang () == language_fortran) - for (child_die = die->child; child_die && child_die->tag; - child_die = child_die->sibling) + if (die->tag == DW_TAG_imported_module && cu->lang () == language_fortran) + for (die_info *child_die : die->children ()) { /* DWARF-4: A Fortran use statement with a “rename list” may be represented by an imported module entry with an import attribute @@ -6098,7 +6095,6 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu) struct objfile *objfile = per_objfile->objfile; CORE_ADDR lowpc; struct attribute *attr; - struct die_info *child_die; unrelocated_addr unrel_low, unrel_high; get_scope_pc_bounds (die, &unrel_low, &unrel_high, cu); @@ -6145,15 +6141,9 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu) handle_DW_AT_stmt_list (die, cu, fnd, unrel_low, unrel_low != unrel_high); /* Process all dies in compilation unit. */ - if (die->child != NULL) - { - child_die = die->child; - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->sibling; - } - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); + per_objfile->sym_cu = nullptr; /* Decode macro information, if present. Dwarf 2 macro information @@ -6309,22 +6299,13 @@ dwarf2_cu::setup_type_unit_groups (struct die_info *die) static void read_type_unit_scope (struct die_info *die, struct dwarf2_cu *cu) { - struct die_info *child_die; - /* Initialize (or reinitialize) the machinery for building symtabs. We do this before processing child DIEs, so that the line header table is available for DW_AT_decl_file. */ cu->setup_type_unit_groups (die); - if (die->child != NULL) - { - child_die = die->child; - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->sibling; - } - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); } /* DWO/DWP files. @@ -8210,8 +8191,8 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) break; } - concrete_child = concrete_child->sibling; - abstract_child = abstract_child->sibling; + concrete_child = concrete_child->next; + abstract_child = abstract_child->next; } /* Walk the origin's children in parallel to the concrete children. @@ -8224,9 +8205,7 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) std::vector<sect_offset> offsets; - for (die_info *child_die = die->child; - child_die && child_die->tag; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { /* We are trying to process concrete instance entries: DW_TAG_call_site DIEs indeed have a DW_AT_abstract_origin tag, but @@ -8238,7 +8217,7 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) { if (are_isomorphic) corresponding_abstract_child - = corresponding_abstract_child->sibling; + = corresponding_abstract_child->next; continue; } @@ -8296,7 +8275,7 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) } if (are_isomorphic) - corresponding_abstract_child = corresponding_abstract_child->sibling; + corresponding_abstract_child = corresponding_abstract_child->next; } if (!offsets.empty ()) @@ -8314,8 +8293,7 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) } auto offsets_it = offsets.begin (); - die_info *origin_child_die = origin_die->child; - while (origin_child_die != nullptr && origin_child_die->tag != 0) + for (die_info *origin_child_die : origin_die->children ()) { /* Is ORIGIN_CHILD_DIE referenced by any of the DIE children? */ while (offsets_it < offsets.end () @@ -8332,8 +8310,6 @@ inherit_abstract_dies (struct die_info *die, struct dwarf2_cu *cu) if (!origin_child_die->in_process) process_die (origin_child_die, origin_cu); } - - origin_child_die = origin_child_die->sibling; } origin_cu->list_in_scope = origin_previous_list_in_scope; @@ -8424,7 +8400,6 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) struct context_stack *newobj; CORE_ADDR lowpc; CORE_ADDR highpc; - struct die_info *child_die; struct attribute *attr, *call_line, *call_file; const char *name; struct block *block; @@ -8501,7 +8476,7 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) /* If we have any template arguments, then we must allocate a different sort of symbol. */ - for (child_die = die->child; child_die; child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_template_type_param || child_die->tag == DW_TAG_template_value_param) @@ -8539,23 +8514,18 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) cu->list_in_scope = cu->get_builder ()->get_local_symbols (); - if (die->child != NULL) + for (die_info *child_die : die->children ()) { - child_die = die->child; - while (child_die && child_die->tag) + if (child_die->tag == DW_TAG_template_type_param + || child_die->tag == DW_TAG_template_value_param) { - if (child_die->tag == DW_TAG_template_type_param - || child_die->tag == DW_TAG_template_value_param) - { - struct symbol *arg = new_symbol (child_die, NULL, cu); + struct symbol *arg = new_symbol (child_die, NULL, cu); - if (arg != NULL) - template_args.push_back (arg); - } - else - process_die (child_die, cu); - child_die = child_die->sibling; + if (arg != NULL) + template_args.push_back (arg); } + else + process_die (child_die, cu); } inherit_abstract_dies (die, cu); @@ -8571,13 +8541,9 @@ read_func_scope (struct die_info *die, struct dwarf2_cu *cu) while (spec_die) { - child_die = spec_die->child; - while (child_die && child_die->tag) - { - if (child_die->tag == DW_TAG_imported_module) - process_die (child_die, spec_cu); - child_die = child_die->sibling; - } + for (die_info *child_die : spec_die->children ()) + if (child_die->tag == DW_TAG_imported_module) + process_die (child_die, spec_cu); /* In some cases, GCC generates specification DIEs that themselves contain DW_AT_specification attributes. */ @@ -8646,7 +8612,6 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) { dwarf2_per_objfile *per_objfile = cu->per_objfile; CORE_ADDR lowpc, highpc; - struct die_info *child_die; /* Ignore blocks with missing or invalid low and high pc attributes. */ /* ??? Perhaps consider discontiguous blocks defined by DW_AT_ranges @@ -8661,9 +8626,7 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) /* DW_TAG_lexical_block has no attributes, process its children as if there was no wrapping by that DW_TAG_lexical_block. GCC does no longer produces such DWARF since GCC r224161. */ - for (child_die = die->child; - child_die != NULL && child_die->tag; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { /* We might already be processing this DIE. This can happen in an unusual circumstance -- where a subroutine A @@ -8682,15 +8645,9 @@ read_lexical_block_scope (struct die_info *die, struct dwarf2_cu *cu) highpc = per_objfile->relocate (unrel_high); cu->get_builder ()->push_context (0, lowpc); - if (die->child != NULL) - { - child_die = die->child; - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->sibling; - } - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); + inherit_abstract_dies (die, cu); struct context_stack cstk = cu->get_builder ()->pop_context (); @@ -8733,7 +8690,6 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) struct gdbarch *gdbarch = objfile->arch (); struct attribute *attr; int nparams; - struct die_info *child_die; attr = dwarf2_attr (die, DW_AT_call_return_pc, cu); if (attr == NULL) @@ -8754,8 +8710,7 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) /* Count parameters at the caller. */ nparams = 0; - for (child_die = die->child; child_die && child_die->tag; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { if (child_die->tag != DW_TAG_call_site_parameter && child_die->tag != DW_TAG_GNU_call_site_parameter) @@ -8924,9 +8879,7 @@ read_call_site_scope (struct die_info *die, struct dwarf2_cu *cu) "block nor reference, for DIE %s [in module %s]"), sect_offset_str (die->sect_off), objfile_name (objfile)); - for (child_die = die->child; - child_die && child_die->tag; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { struct call_site_parameter *parameter; struct attribute *loc, *origin; @@ -9646,7 +9599,6 @@ dwarf2_get_subprogram_pc_bounds (struct die_info *die, struct dwarf2_cu *cu) { unrelocated_addr low, high; - struct die_info *child = die->child; if (dwarf2_get_pc_bounds (die, &low, &high, cu, nullptr, nullptr) >= PC_BOUNDS_RANGES) @@ -9664,12 +9616,11 @@ dwarf2_get_subprogram_pc_bounds (struct die_info *die, subprograms, then check their pc bounds. Likewise, we need to check lexical blocks as well, as they may also contain subprogram definitions. */ - while (child && child->tag) + for (die_info *child : die->children ()) { if (child->tag == DW_TAG_subprogram || child->tag == DW_TAG_lexical_block) dwarf2_get_subprogram_pc_bounds (child, lowpc, highpc, cu); - child = child->sibling; } } @@ -9695,9 +9646,7 @@ get_scope_pc_bounds (struct die_info *die, } else { - struct die_info *child = die->child; - - while (child && child->tag) + for (die_info *child : die->children ()) { switch (child->tag) { case DW_TAG_subprogram: @@ -9725,8 +9674,6 @@ get_scope_pc_bounds (struct die_info *die, /* Ignore. */ break; } - - child = child->sibling; } } @@ -11277,9 +11224,7 @@ handle_variant_part (struct die_info *die, struct type *type, objfile_name (cu->per_objfile->objfile)); } - for (die_info *child_die = die->child; - child_die != NULL; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) handle_struct_member_die (child_die, type, fi, template_args, cu); } @@ -11330,9 +11275,7 @@ handle_variant (struct die_info *die, struct type *type, else variant.discriminant_value = discr->constant_value (0); - for (die_info *variant_child = die->child; - variant_child != NULL; - variant_child = variant_child->sibling) + for (die_info *variant_child : die->children ()) handle_struct_member_die (variant_child, type, fi, template_args, cu); variant.last_field = fi->fields.size (); @@ -11401,7 +11344,6 @@ static void process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) { struct objfile *objfile = cu->per_objfile->objfile; - struct die_info *child_die; struct type *type; type = get_die_type (die, cu); @@ -11414,13 +11356,8 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) struct field_info fi; std::vector<struct symbol *> template_args; - child_die = die->child; - - while (child_die && child_die->tag) - { - handle_struct_member_die (child_die, type, &fi, &template_args, cu); - child_die = child_die->sibling; - } + for (die_info *child_die : die->children ()) + handle_struct_member_die (child_die, type, &fi, &template_args, cu); /* Attach template arguments to type. */ if (!template_args.empty ()) @@ -11558,9 +11495,7 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) current die is a declaration. Normally, of course, a declaration won't have any children at all. */ - child_die = die->child; - - while (child_die != NULL && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_member || child_die->tag == DW_TAG_variable @@ -11572,8 +11507,6 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) } else process_die (child_die, cu); - - child_die = child_die->sibling; } /* Do not consider external references. According to the DWARF standard, @@ -11673,16 +11606,13 @@ update_enumeration_type_from_children (struct die_info *die, struct type *type, struct dwarf2_cu *cu) { - struct die_info *child_die; int unsigned_enum = 1; int flag_enum = 1; auto_obstack obstack; std::vector<struct field> fields; - for (child_die = die->child; - child_die != NULL && child_die->tag; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { struct attribute *attr; LONGEST value; @@ -11840,10 +11770,7 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { - struct die_info *child_die; - - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag != DW_TAG_enumerator) { @@ -11851,8 +11778,6 @@ process_enumeration_scope (struct die_info *die, struct dwarf2_cu *cu) } else new_symbol (child_die, this_type, cu); - - child_die = child_die->sibling; } } @@ -12033,9 +11958,7 @@ quirk_ada_thick_pointer (struct die_info *die, struct dwarf2_cu *cu, int bounds_offset = -1; int max_align = -1; std::vector<struct field> range_fields; - for (struct die_info *child_die = die->child; - child_die; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_subrange_type) { @@ -12136,7 +12059,6 @@ static struct type * read_array_type (struct die_info *die, struct dwarf2_cu *cu) { struct objfile *objfile = cu->per_objfile->objfile; - struct die_info *child_die; struct type *type; struct type *element_type, *range_type, *index_type; struct attribute *attr; @@ -12191,8 +12113,7 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu) } std::vector<struct type *> range_types; - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_subrange_type || child_die->tag == DW_TAG_generic_subrange) @@ -12206,7 +12127,6 @@ read_array_type (struct die_info *die, struct dwarf2_cu *cu) range_types.push_back (child_type); } } - child_die = child_die->sibling; } if (range_types.empty ()) @@ -12473,15 +12393,12 @@ read_common_block (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { struct objfile *objfile = cu->per_objfile->objfile; - struct die_info *child_die; - size_t n_entries = 0, size; + size_t size; struct common_block *common_block; struct symbol *sym; - for (child_die = die->child; - child_die && child_die->tag; - child_die = child_die->sibling) - ++n_entries; + auto range = die->children (); + size_t n_entries = std::distance (range.begin (), range.end ()); size = (sizeof (struct common_block) + (n_entries - 1) * sizeof (struct symbol *)); @@ -12491,9 +12408,7 @@ read_common_block (struct die_info *die, struct dwarf2_cu *cu) memset (common_block->contents, 0, n_entries * sizeof (struct symbol *)); common_block->n_entries = 0; - for (child_die = die->child; - child_die && child_die->tag; - child_die = child_die->sibling) + for (die_info *child_die : die->children ()) { /* Create the symbol in the DW_TAG_common_block block in the current symbol scope. */ @@ -12616,13 +12531,8 @@ read_namespace (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { - struct die_info *child_die = die->child; - - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->sibling; - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); } } @@ -12660,17 +12570,13 @@ read_module_type (struct die_info *die, struct dwarf2_cu *cu) static void read_module (struct die_info *die, struct dwarf2_cu *cu) { - struct die_info *child_die = die->child; struct type *type; type = read_type_die (die, cu); new_symbol (die, type, cu); - while (child_die && child_die->tag) - { - process_die (child_die, cu); - child_die = child_die->sibling; - } + for (die_info *child_die : die->children ()) + process_die (child_die, cu); } /* Return the name of the namespace represented by DIE. Set @@ -13168,22 +13074,18 @@ read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu) if (die->child != NULL) { struct type *void_type = builtin_type (objfile)->builtin_void; - struct die_info *child_die; int nparams, iparams; /* Count the number of parameters. FIXME: GDB currently ignores vararg functions, but knows about vararg member functions. */ nparams = 0; - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_formal_parameter) nparams++; else if (child_die->tag == DW_TAG_unspecified_parameters) ftype->set_has_varargs (true); - - child_die = child_die->sibling; } /* Allocate storage for parameters and fill them in. */ @@ -13195,8 +13097,7 @@ read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu) ftype->field (iparams).set_type (void_type); iparams = 0; - child_die = die->child; - while (child_die && child_die->tag) + for (die_info *child_die : die->children ()) { if (child_die->tag == DW_TAG_formal_parameter) { @@ -13253,7 +13154,6 @@ read_subroutine_type (struct die_info *die, struct dwarf2_cu *cu) ftype->field (iparams).set_type (arg_type); iparams++; } - child_die = child_die->sibling; } } @@ -14350,7 +14250,7 @@ read_unspecified_type (struct die_info *die, struct dwarf2_cu *cu) return set_die_type (die, type, cu); } -/* Read a single die and all its descendents. Set the die's sibling +/* Read a single die and all its descendents. Set the die's next field to NULL; set other fields in the die correctly, and set all of the descendents' fields correctly. PARENT is the parent of the die in question. */ @@ -14371,7 +14271,7 @@ cutu_reader::read_die_and_children (die_info *parent) else die->child = nullptr; - die->sibling = nullptr; + die->next = nullptr; die->parent = parent; return die; } @@ -14396,7 +14296,7 @@ cutu_reader::read_die_and_siblings (die_info *parent) if (first_die == nullptr) first_die = die; else - last_sibling->sibling = die; + last_sibling->next = die; last_sibling = die; } @@ -14435,7 +14335,7 @@ cutu_reader::read_all_dies () and updating die_info::num_attrs. Return a newly allocated die with its information, except for its - child, sibling, and parent fields. */ + child, next, and parent fields. */ die_info * cutu_reader::read_full_die (int num_extra_attrs, bool allow_reprocess) @@ -14479,7 +14379,7 @@ cutu_reader::read_full_die (int num_extra_attrs, bool allow_reprocess) /* Read a die and all its attributes. Return a newly allocated die with its information, except for its - child, sibling, and parent fields. */ + child, next, and parent fields. */ die_info * cutu_reader::read_toplevel_die (gdb::array_view<attribute *> extra_attrs) @@ -17767,7 +17667,6 @@ guess_full_die_structure_name (struct die_info *die, struct dwarf2_cu *cu) { struct die_info *spec_die; struct dwarf2_cu *spec_cu; - struct die_info *child; struct objfile *objfile = cu->per_objfile->objfile; spec_cu = cu; @@ -17778,9 +17677,7 @@ guess_full_die_structure_name (struct die_info *die, struct dwarf2_cu *cu) cu = spec_cu; } - for (child = die->child; - child != NULL; - child = child->sibling) + for (die_info *child : die->children ()) { if (child->tag == DW_TAG_subprogram) { @@ -18106,18 +18003,19 @@ unnamed_template_tag_name (die_info *die, dwarf2_cu *cu) arrive at our entry. */ size_t nth_unnamed = 0; - die_info *child = die->parent->child; - while (child != die) - { - gdb_assert (child != nullptr); - if (child->tag == DW_TAG_template_type_param - || child->tag == DW_TAG_template_value_param) - { - if (dwarf2_attr (child, DW_AT_name, cu) == nullptr) - ++nth_unnamed; - } - child = child->sibling; - } + for (die_info *child : die->parent->children ()) + { + if (child == die) + break; + + gdb_assert (child != nullptr); + if (child->tag == DW_TAG_template_type_param + || child->tag == DW_TAG_template_value_param) + { + if (dwarf2_attr (child, DW_AT_name, cu) == nullptr) + ++nth_unnamed; + } + } const std::string name_str = "<unnamed" + std::to_string (nth_unnamed) + ">"; return cu->per_objfile->objfile->intern (name_str.c_str ()); 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/linux-thread-db.c b/gdb/linux-thread-db.c index 9d84187..f946c2a 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -778,9 +778,6 @@ check_thread_db (struct thread_db_info *info, bool log_progress) } catch (const gdb_exception_error &except) { - if (warning_pre_print) - gdb_puts (warning_pre_print, gdb_stderr); - exception_fprintf (gdb_stderr, except, _("libthread_db integrity checks failed: ")); @@ -705,7 +705,7 @@ captured_main_1 (struct captured_main_args *context) /* Prefix warning messages with the command name. */ gdb::unique_xmalloc_ptr<char> tmp_warn_preprint - = xstrprintf ("%s: warning: ", gdb_program_name); + = xstrprintf ("%s: ", gdb_program_name); warning_pre_print = tmp_warn_preprint.get (); current_directory = getcwd (NULL, 0); @@ -1169,7 +1169,7 @@ captured_main_1 (struct captured_main_args *context) /* Set off error and warning messages with a blank line. */ tmp_warn_preprint.reset (); - warning_pre_print = _("\nwarning: "); + warning_pre_print = "\n"; /* Read and execute the system-wide gdbinit file, if it exists. This is done *before* all the command line arguments are @@ -1274,7 +1274,7 @@ captured_main_1 (struct captured_main_args *context) current_inferior ()->set_tty (ttyarg); /* Error messages should no longer be distinguished with extra output. */ - warning_pre_print = _("warning: "); + warning_pre_print = ""; /* Read the .gdbinit file in the current directory, *if* it isn't the same as the $HOME/.gdbinit file (it should exist, also). */ 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..3f1a206 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>::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; +}; + #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/source-cache.c b/gdb/source-cache.c index f08c872..30c9e61 100644 --- a/gdb/source-cache.c +++ b/gdb/source-cache.c @@ -325,11 +325,26 @@ source_cache::ensure (struct symtab *s) least one caller. */ if (i != size - 1) std::swap (m_source_map[i], m_source_map[size - 1]); - return true; + + /* If the styling status of the cached entry matches our desired + styling status, or we know this file cannot be styled, in + which case, this (unstyled) content, is the best we can do. */ + if (((source_styling && gdb_stdout->can_emit_style_escape ()) + == m_source_map[size - 1].styled) + || m_no_styling_files.count (fullname) > 0) + return true; + + /* We found a match, but styling status doesn't match the desired + styling status. We already moved the matching item to the + back of M_SOURCE_MAP, so drop the entry now, and then + recompute with the desired styling. */ + m_source_map.pop_back (); + break; } } std::string contents; + bool styled_p = false; try { contents = get_plain_source_lines (s, fullname); @@ -343,21 +358,21 @@ source_cache::ensure (struct symtab *s) if (source_styling && gdb_stdout->can_emit_style_escape () && m_no_styling_files.count (fullname) == 0) { - bool already_styled + styled_p = try_source_highlight (contents, s->language (), fullname); - if (!already_styled) + if (!styled_p) { std::optional<std::string> ext_contents; ext_contents = ext_lang_colorize (fullname, contents); if (ext_contents.has_value ()) { contents = std::move (*ext_contents); - already_styled = true; + styled_p = true; } } - if (!already_styled) + if (!styled_p) { /* Styling failed. Styling can fail for instance for these reasons: @@ -374,7 +389,8 @@ source_cache::ensure (struct symtab *s) } } - source_text result = { std::move (fullname), std::move (contents) }; + source_text result + = { std::move (fullname), std::move (contents), styled_p }; m_source_map.push_back (std::move (result)); if (m_source_map.size () > MAX_ENTRIES) diff --git a/gdb/source-cache.h b/gdb/source-cache.h index 03f4b79..c7d204b 100644 --- a/gdb/source-cache.h +++ b/gdb/source-cache.h @@ -78,6 +78,8 @@ private: std::string fullname; /* The contents of the file. */ std::string contents; + /* True if CONTENTS are styled. Otherwise, false. */ + bool styled; }; /* A helper function for get_source_lines reads a source file. 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-source-styling.exp b/gdb/testsuite/gdb.python/py-source-styling.exp index ba7e795..8eed56b 100644 --- a/gdb/testsuite/gdb.python/py-source-styling.exp +++ b/gdb/testsuite/gdb.python/py-source-styling.exp @@ -33,12 +33,43 @@ if { [build_executable "failed to build" ${testfile} ${srcfile}] == -1 } { set line_number [gdb_get_line_number "List this line."] +# Helper proc. Run CMD, which should produce a source listing, and +# check if the source code is styled or not. EXPECT_STYLED indicates +# if we expect the source listing to be styled or not. +proc check_source_listing_styling { cmd expect_styled { testname "" } } { + if { $testname eq "" } { + set testname $cmd + } + + set seen_style_escape false + gdb_test_multiple $cmd $testname { + -re -wrap "Python Exception.*" { + fail $gdb_test_name + return + } + -re "\033" { + set seen_style_escape true + exp_continue + } + -re "$::gdb_prompt $" { + gdb_assert { $seen_style_escape == $expect_styled } \ + $gdb_test_name + } + } +} + # Check that the Python pygments module can be used for source # highlighting when GNU source highlight is not available (or is # disabled, as is done in this test). proc test_pygments_styling {} { clean_restart $::binfile + # Remote host boards disable styling via GDB's command line. Turn + # it back on now. + if {[is_remote host]} { + gdb_test "set style enabled on" + } + if { ![gdb_py_module_available "pygments"] } { unsupported "pygments module not available" return @@ -52,19 +83,7 @@ proc test_pygments_styling {} { gdb_test "maint flush source-cache" "Source cache flushed\\." - set seen_style_escape false - gdb_test_multiple "list $::line_number" "" { - -re "Python Exception.*" { - fail $gdb_test_name - } - -re "\033" { - set seen_style_escape true - exp_continue - } - -re "$::gdb_prompt $" { - gdb_assert { $seen_style_escape } $gdb_test_name - } - } + check_source_listing_styling "list $::line_number" true } # Use gdb.execute to list source code containing non-utf-8 character. @@ -93,6 +112,67 @@ proc test_gdb_execute_non_utf8_source {} { gdb_test "python print(source)" ".*List this line.*" } +# Use gdb.execute() to list source code. Alternate between asking for +# styled, and unstyled source code. In some cases we ask for the +# output to be returned via a string, and in other cases we ask for +# the output to be sent straight to stdout. +proc_with_prefix test_source_cache_style_tracking {} { + clean_restart $::binfile + + # Remote host boards disable styling via GDB's command line. Turn + # it back on now. + if {[is_remote host]} { + gdb_test "set style enabled on" + } + + gdb_test_no_output "set host-charset ISO-8859-1" + + # Commands which return styled, and non-styled source code mixed + # together. This ensures that the source cache will need to keep + # discarding the entry with the wrong styling mode. All of these + # gdb.execute calls send their output via a string. + check_source_listing_styling \ + "python print(gdb.execute('list $::line_number', to_string=True), end='')" \ + false + check_source_listing_styling \ + "python print(gdb.execute('list $::line_number', to_string=True, styling=True), end='')" \ + true + foreach from_tty { True False } { + check_source_listing_styling \ + "python print(gdb.execute('list $::line_number', $from_tty, True), end='')" \ + false + check_source_listing_styling \ + "python print(gdb.execute('list $::line_number', $from_tty, True, True), end='')" \ + true + check_source_listing_styling \ + "python print(gdb.execute('list $::line_number', $from_tty, True, False), end='')" \ + false + } + + # The same again, but this time the output is sent directly to + # stdout. + check_source_listing_styling \ + "python gdb.execute('list $::line_number')" \ + true + check_source_listing_styling \ + "python gdb.execute('list $::line_number', to_string=False, styling=False)" \ + false + check_source_listing_styling \ + "python gdb.execute('list $::line_number', to_string=False, styling=True)" \ + true + foreach from_tty { True False } { + check_source_listing_styling \ + "python gdb.execute('list $::line_number', $from_tty, False, False)" \ + false + check_source_listing_styling \ + "python gdb.execute('list $::line_number', $from_tty, False, True)" \ + true + check_source_listing_styling \ + "python gdb.execute('list $::line_number', $from_tty, False)" \ + true + } +} + # We need an ANSI-capable terminal to get the output, additionally we # need to set LC_ALL so GDB knows the terminal is UTF-8 capable, # otherwise we'll get a UnicodeEncodeError trying to encode the @@ -100,4 +180,5 @@ proc test_gdb_execute_non_utf8_source {} { with_ansi_styling_terminal { test_pygments_styling test_gdb_execute_non_utf8_source + test_source_cache_style_tracking } 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/testsuite/gdb.rocm/precise-memory.cpp b/gdb/testsuite/gdb.rocm/precise-memory.cpp index 769b58a..7a8c37e 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory.cpp +++ b/gdb/testsuite/gdb.rocm/precise-memory.cpp @@ -31,7 +31,17 @@ __global__ void kernel () { - __builtin_amdgcn_s_sleep (1); + + /* Simple kernel which loads from address 0 to trigger a pagefault. + When precise memory is not enabled, it is expected that the memory fault + is reported after the s_nop instruction. With precise-memory, the + exception should be reported on the s_nop. */ + asm volatile ("s_mov_b64 [s10, s11], 0\n" + "s_load_dword s12, [s10, s11]\n" + "s_nop 0" + : + : + : "s10", "s11", "s12"); } int diff --git a/gdb/testsuite/gdb.rocm/precise-memory.exp b/gdb/testsuite/gdb.rocm/precise-memory.exp index f423a11..8c39f80 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory.exp @@ -39,18 +39,40 @@ proc do_test { } { "AMDGPU precise memory access reporting is off \\(currently disabled\\)." \ "show precise-memory setting in CLI before" - if {[hip_devices_support_precise_memory]} { - gdb_test_no_output "set amdgpu precise-memory on" - set cli_effective_value "enabled" - } else { - gdb_test "set amdgpu precise-memory on" \ - "warning: AMDGPU precise memory access reporting could not be enabled." - set cli_effective_value "disabled" + # Assume precise-memory is available, unless GDB reports otherwise. + gdb_test_multiple "set amdgpu precise-memory on" "" { + -re -wrap "warning: AMDGPU precise memory access reporting could not be enabled\\." { + set cli_effective_value "disabled" + pass $gdb_test_name + } + -re -wrap "^" { + set cli_effective_value "enabled" + pass $gdb_test_name + } } gdb_test "show amdgpu precise-memory" \ - "AMDGPU precise memory access reporting is on \\(currently ${cli_effective_value}\\)." \ + "AMDGPU precise memory access reporting is on \\(currently ${cli_effective_value}\\)\\." \ "show precise-memory setting in CLI after" + + if { $cli_effective_value eq "disabled" } { + return + } + + # Get to the begining of the GPU kernel without precise memory enabled. + with_test_prefix "goto gpu code" { + gdb_test_no_output "set amdgpu precise-memory off" + gdb_breakpoint "kernel" allow-pending + gdb_test "continue" "Thread ${::decimal}.* hit Breakpoint .*" + gdb_test_no_output "set amdgpu precise-memory on" + } + + # If precise-memory is available, run until a SIGSEGV is reported. At + # that point, the PC should point to the s_nop instruction (the one + # following the one which caused the memory violation). + gdb_test "continue" "Thread ${::decimal}\[^\r\n\]* received signal SIGSEGV, Segmentation fault.*" + + gdb_test "x/i \$pc" "=> ${::hex} <_Z6kernelv\\+${::decimal}>:\[ \t\]+s_nop\[ \t\]+0" } } diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c index 2e23f12..d0707cd 100644 --- a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c @@ -43,6 +43,7 @@ function_that_segfaults () { int *p = 0; *p = 1; /* Segfault happens here. */ + return 0; } int @@ -55,6 +56,7 @@ void * worker_func (void *arg) { int a = 42; /* Breakpoint here. */ + return NULL; } void diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp index c9508c9..feec37b 100644 --- a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp @@ -79,7 +79,7 @@ proc run_condition_test { message n_expected_hits condition \ gdb_breakpoint \ "${::srcfile}:${::cond_bp_line} if ((++\$n_cond_eval) && (${condition}))" - # And a breakpoint that we hit when the test is over, this one is + # Add a breakpoint that we hit when the test is over, this one is # not conditional. Only the main thread gets here once all the # other threads have finished. gdb_breakpoint "${::srcfile}:${::stop_bp_line}" @@ -114,7 +114,7 @@ proc run_condition_test { message n_expected_hits condition \ # includes an inferior call), it is still possible to kill the running # inferior, and then restart the inferior. # -# At once point doing this would result in GDB giving an assertion error. +# At one point doing this would result in GDB giving an assertion error. proc_with_prefix run_kill_and_restart_test { target_async target_non_stop } { # This test relies on the 'start' command, which is not possible with # the plain 'remote' target. diff --git a/gdb/testsuite/lib/rocm.exp b/gdb/testsuite/lib/rocm.exp index 3eb51db..5164f1e 100644 --- a/gdb/testsuite/lib/rocm.exp +++ b/gdb/testsuite/lib/rocm.exp @@ -176,22 +176,3 @@ proc hip_devices_support_debug_multi_process {} { } return 1 } - -# Return true if all the devices on the host support precise memory. - -proc hip_devices_support_precise_memory {} { - set unsupported_targets \ - {gfx900 gfx906 gfx908 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032} - - set targets [find_amdgpu_devices] - if { [llength $targets] == 0 } { - return 0 - } - - foreach target $targets { - if { [lsearch -exact $unsupported_targets $target] != -1 } { - return 0 - } - } - return 1 -} diff --git a/gdb/utils.c b/gdb/utils.c index 3d216e1..ee7cf4d 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -119,7 +119,7 @@ show_sevenbit_strings (struct ui_file *file, int from_tty, /* String to be printed before warning messages, if any. */ -const char *warning_pre_print = "\nwarning: "; +const char *warning_pre_print = "\n"; bool pagination_enabled = true; static void @@ -176,8 +176,8 @@ vwarning (const char *string, va_list args) term_state.emplace (); target_terminal::ours_for_output (); } - if (warning_pre_print) - gdb_puts (warning_pre_print, gdb_stderr); + gdb_puts (warning_pre_print, gdb_stderr); + gdb_puts (_("warning: "), gdb_stderr); gdb_vprintf (gdb_stderr, string, args); gdb_printf (gdb_stderr, "\n"); } 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); } |