diff options
Diffstat (limited to 'gdb')
98 files changed, 3246 insertions, 1542 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in index fc0c565..7654fb1 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -907,13 +907,22 @@ ALL_TARGET_OBS = \ vax-tdep.o \ windows-tdep.o \ x86-tdep.o \ - xcoffread.o \ xstormy16-tdep.o \ xtensa-config.o \ xtensa-linux-tdep.o \ xtensa-tdep.o \ z80-tdep.o +# Object files for reading specific types of debug information. +coff_SRCS = coffread.c coff-pe-read.c +dbx_SRCS = dbxread.c +elf_SRCS = elfread.c stap-probe.c dtrace-probe.c +macho_SRCS = machoread.c +mips_SRCS = mipsread.c +xcoff_SRCS = xcoffread.c +FORMAT_SRCS = @FORMAT_SRCS@ +FORMAT_OBS = $(patsubst %.c,%.o,$(FORMAT_SRCS)) + # The following native-target dependent variables are defined on # configure.nat. NAT_FILE = @NAT_FILE@ @@ -1070,8 +1079,6 @@ COMMON_SFILES = \ c-varobj.c \ charset.c \ cli-out.c \ - coff-pe-read.c \ - coffread.c \ complaints.c \ completer.c \ copying.c \ @@ -1085,7 +1092,6 @@ COMMON_SFILES = \ d-lang.c \ d-namespace.c \ d-valprint.c \ - dbxread.c \ dcache.c \ debug.c \ debuginfod-support.c \ @@ -1150,7 +1156,6 @@ COMMON_SFILES = \ memtag.c \ minidebug.c \ minsyms.c \ - mipsread.c \ namespace.c \ objc-lang.c \ objfiles.c \ @@ -1243,7 +1248,6 @@ SFILES = \ d-exp.y \ dtrace-probe.c \ elf-none-tdep.c \ - elfread.c \ f-exp.y \ gcore-elf.c \ gdb.c \ @@ -1870,7 +1874,6 @@ ALLDEPFILES = \ x86-gnu-nat.c \ x86-nat.c \ x86-tdep.c \ - xcoffread.c \ xstormy16-tdep.c \ xtensa-config.c \ xtensa-linux-nat.c \ @@ -1925,7 +1928,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ $(patsubst %.c,%.o,$(COMMON_SFILES)) \ $(SUBDIR_CLI_OBS) \ $(SUBDIR_MI_OBS) \ - $(SUBDIR_TARGET_OBS) + $(SUBDIR_TARGET_OBS) \ + $(FORMAT_OBS) SUBDIRS = doc @subdirs@ data-directory CLEANDIRS = $(SUBDIRS) @@ -224,6 +224,17 @@ vFile:stat * Support for stabs debugging format and the a.out/dbx object format is deprecated, and will be removed in GDB 18. +* Configure changes + +--enable-binary-file-formats=[FORMAT,...] +--enable-binary-file-formats=all + A user can now decide to only compile support for certain file formats. + The available formats at this point are: dbx, coff, xcoff, elf, mach-o + and mips. Some targets require specific file formats to be available, + and in such cases, the configure script will warn the user and add + support anyway. By default, all formats will be compiled in, to + continue the behavior from before adding the switch. + * A new configure option was added, allowing support for the compile subsystem to be disabled at configure time, in the form of --disable-gdb-compile. @@ -417,6 +417,30 @@ more obscure GDB `configure' options are not listed here. There is no convenient way to generate a list of all available targets. +`--enable-binary-file-formats=FORMAT,FORMAT,...' +`--enable-binary-file-formats=all' + Configure GDB to only be be able to read selected file formats. + The special value "all" causes all file formats to be compiled + in, and is the the default behavior of the option. This option + is meant for advanced users who are sure of what they expect, + if you are unsure which options you will need on your debugging + sessions, we recommend that you not use this feature. The + accepted options are: + * coff: Main format on Windows systems, this is required to + compile with windows target support; + * dbx (also known as a.out): Legacy file format, this is + recommended if you know you will be dealing with this + file format; + * elf: Main format on Linux systems, this is heavily + recommended when compiling with linux support; + * macho: Main format on MacOS systems, this is heavily + recommended when compiling for those targets; + * mips (also known as ecoff): Main file format for targets + running on MIPS CPUs, this is heavily recommended when + supporting those targets; + * xcoff: Main format on AIX systems, this is required to + compile for AIX targets and rs6000 CPUs. + `--with-gdb-datadir=PATH' Set the GDB-specific data directory. GDB will look here for certain supporting files or scripts. This defaults to the `gdb' diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 329d114..1955169 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -1677,8 +1677,6 @@ ada_decode_symbol (const struct general_symbol_info *arg) void ada_fixup_array_indexes_type (struct type *index_desc_type) { - int i; - if (index_desc_type == NULL) return; gdb_assert (index_desc_type->num_fields () > 0); @@ -1696,13 +1694,13 @@ ada_fixup_array_indexes_type (struct type *index_desc_type) return; /* Fixup each field of INDEX_DESC_TYPE. */ - for (i = 0; i < index_desc_type->num_fields (); i++) + for (auto &field : index_desc_type->fields ()) { - const char *name = index_desc_type->field (i).name (); + const char *name = field.name (); struct type *raw_type = ada_check_typedef (ada_find_any_type (name)); if (raw_type) - index_desc_type->field (i).set_type (raw_type); + field.set_type (raw_type); } } @@ -7080,17 +7078,16 @@ ada_search_struct_field (const char *name, struct value *arg, int offset, else if (ada_is_variant_part (type, i)) { /* PNH: Do we ever get here? See find_struct_field. */ - int j; struct type *field_type = ada_check_typedef (type->field (i).type ()); int var_offset = offset + type->field (i).loc_bitpos () / 8; - for (j = 0; j < field_type->num_fields (); j += 1) + for (const auto &field : field_type->fields ()) { - struct value *v = ada_search_struct_field /* Force line - break. */ - (name, arg, - var_offset + field_type->field (j).loc_bitpos () / 8, - field_type->field (j).type ()); + struct value *v + = (ada_search_struct_field + (name, arg, + var_offset + field.loc_bitpos () / 8, + field.type ())); if (v != NULL) return v; @@ -8182,12 +8179,11 @@ ada_is_redundant_index_type_desc (struct type *array_type, struct type *desc_type) { struct type *this_layer = check_typedef (array_type); - int i; - for (i = 0; i < desc_type->num_fields (); i++) + for (const auto &field : desc_type->fields ()) { if (!ada_is_redundant_range_encoding (this_layer->index_type (), - desc_type->field (i).type ())) + field.type ())) return 0; this_layer = check_typedef (this_layer->target_type ()); } @@ -8784,9 +8780,9 @@ ada_atr_enum_val (struct expression *exp, enum noside noside, struct type *type, error (_("'Enum_Val requires integral argument")); LONGEST value = value_as_long (arg); - for (int i = 0; i < type->num_fields (); ++i) + for (const auto &field : type->fields ()) { - if (type->field (i).loc_enumval () == value) + if (field.loc_enumval () == value) return value_from_longest (original_type, value); } @@ -10502,7 +10498,6 @@ static LONGEST convert_char_literal (struct type *type, LONGEST val) { char name[12]; - int f; if (type == NULL) return val; @@ -10519,17 +10514,17 @@ convert_char_literal (struct type *type, LONGEST val) else xsnprintf (name, sizeof (name), "QWW%08lx", (unsigned long) val); size_t len = strlen (name); - for (f = 0; f < type->num_fields (); f += 1) + for (const auto &field : type->fields ()) { /* Check the suffix because an enum constant in a package will have a name like "pkg__QUxx". This is safe enough because we already have the correct type, and because mangling means there can't be clashes. */ - const char *ename = type->field (f).name (); + const char *ename = field.name (); size_t elen = strlen (ename); if (elen >= len && strcmp (name, ename + elen - len) == 0) - return type->field (f).loc_enumval (); + return field.loc_enumval (); } return val; } diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index 82dd1e0..d5ea4af 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -513,20 +513,19 @@ amd64_has_unaligned_fields (struct type *type) if (type->code () == TYPE_CODE_STRUCT || type->code () == TYPE_CODE_UNION) { - for (int i = 0; i < type->num_fields (); i++) + for (const auto &field : type->fields ()) { - struct type *subtype = check_typedef (type->field (i).type ()); + struct type *subtype = check_typedef (field.type ()); /* Ignore static fields, empty fields (for example nested empty structures), and bitfields (these are handled by the caller). */ - if (type->field (i).is_static () - || (type->field (i).bitsize () == 0 - && subtype->length () == 0) - || type->field (i).is_packed ()) + if (field.is_static () + || (field.bitsize () == 0 && subtype->length () == 0) + || field.is_packed ()) continue; - int bitpos = type->field (i).loc_bitpos (); + int bitpos = field.loc_bitpos (); if (bitpos % 8 != 0) return true; diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index 08f542c..57626ab 100644 --- a/gdb/ax-gdb.c +++ b/gdb/ax-gdb.c @@ -538,8 +538,7 @@ gen_var_ref (struct agent_expr *ax, struct axs_value *value, struct symbol *var) break; case LOC_CONST_BYTES: - internal_error (_("gen_var_ref: LOC_CONST_BYTES " - "symbols are not supported")); + error (_("gen_var_ref: LOC_CONST_BYTES symbols are not supported")); /* Variable at a fixed location in memory. Easy. */ case LOC_STATIC: diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index df7bdbe..b6b3f6b 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -251,8 +251,8 @@ cp_type_print_method_args (struct type *mtype, const char *prefix, enum language language, const struct type_print_options *flags) { - struct field *args = mtype->fields (); - int nargs = mtype->num_fields (); + auto args = mtype->fields (); + int nargs = args.size (); int varargs = mtype->has_varargs (); int i; @@ -515,16 +515,15 @@ c_type_print_args (struct type *type, struct ui_file *stream, int linkage_name, enum language language, const struct type_print_options *flags) { - int i; int printed_any = 0; gdb_printf (stream, "("); - for (i = 0; i < type->num_fields (); i++) + for (const auto &field : type->fields ()) { struct type *param_type; - if (type->field (i).is_artificial () && linkage_name) + if (field.is_artificial () && linkage_name) continue; if (printed_any) @@ -533,7 +532,7 @@ c_type_print_args (struct type *type, struct ui_file *stream, stream->wrap_here (4); } - param_type = type->field (i).type (); + param_type = field.type (); if (language == language_cplus && linkage_name) { diff --git a/gdb/config.in b/gdb/config.in index 149aeaf..efc3100 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -745,6 +745,9 @@ /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS +/* Which binary file formats were requested at configure time. */ +#undef SUPPORTED_BINARY_FILE_FORMATS + /* automatically load a system-wide gdbinit file */ #undef SYSTEM_GDBINIT diff --git a/gdb/configure b/gdb/configure index 8fc3b04..cdea00f 100755 --- a/gdb/configure +++ b/gdb/configure @@ -706,6 +706,7 @@ LIBGUI LTLIBLZMA LIBLZMA HAVE_LIBLZMA +FORMAT_SRCS SER_HARDWIRE WERROR_CFLAGS WARN_CFLAGS @@ -932,6 +933,7 @@ with_relocated_sources with_auto_load_dir with_auto_load_safe_path enable_targets +enable_binary_file_formats enable_gdb_mdebug_support enable_gdb_dwarf_support with_amd_dbgapi @@ -1645,6 +1647,10 @@ Optional Features: --disable-nls do not use Native Language Support --enable-targets=TARGETS alternative target configurations + --enable-binary-file-formats=FORMATS + enable support for selected file formats (default + 'all') available formats: coff, dbx, elf, macho, + mips, xcoff, all --enable-gdb-mdebug-support Enable support for the mdebug debuginfo format (default 'yes') @@ -11507,7 +11513,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11510 "configure" +#line 11516 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11613,7 +11619,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11616 "configure" +#line 11622 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -24882,6 +24888,18 @@ esac fi +# Check whether --enable-binary_file_formats was given. +if test "${enable_binary_file_formats+set}" = set; then : + enableval=$enable_binary_file_formats; case "${enableval}" in + yes | "") as_fn_error $? "enable-binary-file-formats option must specify file formats or 'all'" "$LINENO" 5 + ;; + no) enable_binary_file_formats= ;; + *) enable_binary_file_formats=$enableval ;; +esac +else + enable_binary_file_formats=all +fi + # Check whether to support mdebug/ecoff debug information. # Check whether --enable-gdb-mdebug-support was given. @@ -24986,10 +25004,21 @@ TARGET_OBS= all_targets= HAVE_NATIVE_GCORE_TARGET= +# File formats that will be enabled based on the selected +# target(s). These are chosen because they are required to +# compile one or more of the selected targets. +target_formats= + +# If all targets were requested, this is all formats that should +# accompany them. These are just the ones required for compilation +# to succeed, not the formats suggested based on targets. +all_target_formats="coff xcoff" + for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'` do if test "$targ_alias" = "all"; then all_targets=true + target_formats=$all_target_formats else # Canonicalize the secondary target names. result=`$ac_config_sub $targ_alias 2>/dev/null` @@ -31628,6 +31657,12 @@ fi # Note that WIN32APILIBS is set by GDB_AC_COMMON. WIN32LIBS="$WIN32LIBS $WIN32APILIBS" +# Object files to be used when building with support for all file formats. +# This should not have elf or macho, as support for those formats depends +# on BFD enabling them as well. +all_binary_file_srcs="\$(dbx_SRCS) \$(mips_SRCS) \$(coff_SRCS) \$(xcoff_SRCS)" + +bfd_supports_elf=no # Add ELF support to GDB, but only if BFD includes ELF support. OLD_CFLAGS=$CFLAGS @@ -31645,7 +31680,7 @@ WIN32LIBS="$WIN32LIBS $WIN32APILIBS" CC="./libtool --quiet --mode=link $CC" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ELF support in BFD" >&5 $as_echo_n "checking for ELF support in BFD... " >&6; } -if ${gdb_cv_var_elf+:} false; then : +if ${gdb_cv_var_bfd_elf+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -31664,24 +31699,23 @@ return bfd_get_elf_phdr_upper_bound (NULL); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - gdb_cv_var_elf=yes + gdb_cv_var_bfd_elf=yes else - gdb_cv_var_elf=no + gdb_cv_var_bfd_elf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_var_elf" >&5 -$as_echo "$gdb_cv_var_elf" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_var_bfd_elf" >&5 +$as_echo "$gdb_cv_var_bfd_elf" >&6; } CC=$OLD_CC CFLAGS=$OLD_CFLAGS LDFLAGS=$OLD_LDFLAGS LIBS=$OLD_LIBS -if test "$gdb_cv_var_elf" = yes; then - CONFIG_OBS="$CONFIG_OBS elfread.o stap-probe.o dtrace-probe.o \ - gcore-elf.o elf-none-tdep.o" +if test "$gdb_cv_var_bfd_elf" = yes; then + CONFIG_OBS="$CONFIG_OBS gcore-elf.o elf-none-tdep.o" $as_echo "#define HAVE_ELF 1" >>confdefs.h @@ -31744,9 +31778,12 @@ if test "$ac_res" != no; then : fi fi + bfd_supports_elf=yes + all_binary_file_srcs="$all_binary_file_srcs \$(elf_SRCS)" fi # Add macho support to GDB, but only if BFD includes it. +bfd_supports_macho=no OLD_CFLAGS=$CFLAGS OLD_LDFLAGS=$LDFLAGS @@ -31763,7 +31800,7 @@ fi CC="./libtool --quiet --mode=link $CC" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Mach-O support in BFD" >&5 $as_echo_n "checking for Mach-O support in BFD... " >&6; } -if ${gdb_cv_var_macho+:} false; then : +if ${gdb_cv_var_bfd_macho+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -31782,30 +31819,81 @@ return bfd_mach_o_lookup_command (NULL, 0, NULL); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : - gdb_cv_var_macho=yes + gdb_cv_var_bfd_macho=yes else - gdb_cv_var_macho=no + gdb_cv_var_bfd_macho=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_var_macho" >&5 -$as_echo "$gdb_cv_var_macho" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_var_bfd_macho" >&5 +$as_echo "$gdb_cv_var_bfd_macho" >&6; } CC=$OLD_CC CFLAGS=$OLD_CFLAGS LDFLAGS=$OLD_LDFLAGS LIBS=$OLD_LIBS -if test "$gdb_cv_var_macho" = yes; then - CONFIG_OBS="$CONFIG_OBS machoread.o" +if test "$gdb_cv_var_bfd_macho" = yes; then + bfd_supports_macho=yes + all_binary_file_srcs="$all_binary_file_srcs \$(macho_SRCS)" fi +FORMAT_SRCS= + +if test "$enable_binary_file_formats" != "all"; then + # Formats that are required by some requested target(s). + # Warn users that they are added, so no one is surprised. + for req in $target_formats; do + case ,$enable_binary_file_formats, in + *,$req,*) + # Do nothing. + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \"$req is required to support one or more requested targets. Adding it\"" >&5 +$as_echo "$as_me: WARNING: \"$req is required to support one or more requested targets. Adding it\"" >&2;} + enable_binary_file_formats="${enable_binary_file_formats},$req" + ;; + esac + done + + +cat >>confdefs.h <<_ACEOF +#define SUPPORTED_BINARY_FILE_FORMATS "$enable_binary_file_formats" +_ACEOF + +fi + +enable_binary_file_formats=$(echo $enable_binary_file_formats | sed 's/,/ /g') + +# Go through all requested and required binary file formats to compile +# GDB, and double check that we can compile them. +for format in $enable_binary_file_formats +do + if test "$format" = "elf" && test "$bfd_supports_elf" != "yes"; then + as_fn_error but BFD does not support it." "\"elf support was requested" "$LINENO" 5; + elif test "$format" = "macho" && test "$bfd_supports_macho" != "yes"; then + as_fn_error but BFD does not support it." "\"Mach-O support was requested" "$LINENO" 5; + fi + + if test "$format" = "all"; then + FORMAT_SRCS="$all_binary_file_srcs" + # We don't break here in case the user requested Mach-O or ELF, but + # BFD is not configured to support it. If we were to break, we would + # silently drop the requested support instead of erroring out. + else + fmt=$(echo "$format _SRCS" | sed 's/ //') + FORMAT_SRCS="$FORMAT_SRCS \$($fmt)" + fi +done + + + # Add any host-specific objects to GDB. CONFIG_OBS="${CONFIG_OBS} ${gdb_host_obs}" # If building on ELF, look for lzma support for embedded compressed debug info. -if test "$gdb_cv_var_elf" = yes; then +if test "$gdb_cv_var_bfd_elf" = yes; then # Check whether --with-lzma was given. if test "${with_lzma+set}" = set; then : diff --git a/gdb/configure.ac b/gdb/configure.ac index 226e27e..2ef53fe 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -191,6 +191,16 @@ AS_HELP_STRING([--enable-targets=TARGETS], [alternative target configurations]), ;; esac]) +AC_ARG_ENABLE(binary_file_formats, + AS_HELP_STRING([--enable-binary-file-formats=FORMATS], + [enable support for selected file formats (default 'all') + available formats: coff, dbx, elf, macho, mips, xcoff, all]), +[case "${enableval}" in + yes | "") AC_MSG_ERROR(enable-binary-file-formats option must specify file formats or 'all') + ;; + no) enable_binary_file_formats= ;; + *) enable_binary_file_formats=$enableval ;; +esac], [enable_binary_file_formats=all]) # Check whether to support mdebug/ecoff debug information. AC_ARG_ENABLE(gdb-mdebug-support, @@ -240,10 +250,21 @@ TARGET_OBS= all_targets= HAVE_NATIVE_GCORE_TARGET= +# File formats that will be enabled based on the selected +# target(s). These are chosen because they are required to +# compile one or more of the selected targets. +target_formats= + +# If all targets were requested, this is all formats that should +# accompany them. These are just the ones required for compilation +# to succeed, not the formats suggested based on targets. +all_target_formats="coff xcoff" + for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'` do if test "$targ_alias" = "all"; then all_targets=true + target_formats=$all_target_formats else # Canonicalize the secondary target names. result=`$ac_config_sub $targ_alias 2>/dev/null` @@ -1952,32 +1973,87 @@ fi # Note that WIN32APILIBS is set by GDB_AC_COMMON. WIN32LIBS="$WIN32LIBS $WIN32APILIBS" +# Object files to be used when building with support for all file formats. +# This should not have elf or macho, as support for those formats depends +# on BFD enabling them as well. +all_binary_file_srcs="\$(dbx_SRCS) \$(mips_SRCS) \$(coff_SRCS) \$(xcoff_SRCS)" + +bfd_supports_elf=no # Add ELF support to GDB, but only if BFD includes ELF support. -GDB_AC_CHECK_BFD([for ELF support in BFD], gdb_cv_var_elf, +GDB_AC_CHECK_BFD([for ELF support in BFD], gdb_cv_var_bfd_elf, [bfd_get_elf_phdr_upper_bound (NULL)], elf-bfd.h) -if test "$gdb_cv_var_elf" = yes; then - CONFIG_OBS="$CONFIG_OBS elfread.o stap-probe.o dtrace-probe.o \ - gcore-elf.o elf-none-tdep.o" +if test "$gdb_cv_var_bfd_elf" = yes; then + CONFIG_OBS="$CONFIG_OBS gcore-elf.o elf-none-tdep.o" AC_DEFINE(HAVE_ELF, 1, [Define if ELF support should be included.]) # -ldl is provided by bfd/Makefile.am (LIBDL) <PLUGINS>. if test "$plugins" = "yes"; then AC_SEARCH_LIBS(dlopen, dl) fi + bfd_supports_elf=yes + all_binary_file_srcs="$all_binary_file_srcs \$(elf_SRCS)" fi # Add macho support to GDB, but only if BFD includes it. -GDB_AC_CHECK_BFD([for Mach-O support in BFD], gdb_cv_var_macho, +bfd_supports_macho=no +GDB_AC_CHECK_BFD([for Mach-O support in BFD], gdb_cv_var_bfd_macho, [bfd_mach_o_lookup_command (NULL, 0, NULL)], mach-o.h) -if test "$gdb_cv_var_macho" = yes; then - CONFIG_OBS="$CONFIG_OBS machoread.o" +if test "$gdb_cv_var_bfd_macho" = yes; then + bfd_supports_macho=yes + all_binary_file_srcs="$all_binary_file_srcs \$(macho_SRCS)" +fi + +FORMAT_SRCS= + +if test "$enable_binary_file_formats" != "all"; then + # Formats that are required by some requested target(s). + # Warn users that they are added, so no one is surprised. + for req in $target_formats; do + case ,$enable_binary_file_formats, in + *,$req,*) + # Do nothing. + ;; + *) + AC_MSG_WARN("$req is required to support one or more requested targets. Adding it") + enable_binary_file_formats="${enable_binary_file_formats},$req" + ;; + esac + done + + AC_DEFINE_UNQUOTED(SUPPORTED_BINARY_FILE_FORMATS, "$enable_binary_file_formats", + Which binary file formats were requested at configure time. ) fi +enable_binary_file_formats=$(echo $enable_binary_file_formats | sed 's/,/ /g') + +# Go through all requested and required binary file formats to compile +# GDB, and double check that we can compile them. +for format in $enable_binary_file_formats +do + if test "$format" = "elf" && test "$bfd_supports_elf" != "yes"; then + AC_MSG_ERROR("elf support was requested, but BFD does not support it."); + elif test "$format" = "macho" && test "$bfd_supports_macho" != "yes"; then + AC_MSG_ERROR("Mach-O support was requested, but BFD does not support it."); + fi + + if test "$format" = "all"; then + FORMAT_SRCS="$all_binary_file_srcs" + # We don't break here in case the user requested Mach-O or ELF, but + # BFD is not configured to support it. If we were to break, we would + # silently drop the requested support instead of erroring out. + else + fmt=$(echo "$format _SRCS" | sed 's/ //') + FORMAT_SRCS="$FORMAT_SRCS \$($fmt)" + fi +done + +AC_SUBST(FORMAT_SRCS) + # Add any host-specific objects to GDB. CONFIG_OBS="${CONFIG_OBS} ${gdb_host_obs}" # If building on ELF, look for lzma support for embedded compressed debug info. -if test "$gdb_cv_var_elf" = yes; then +if test "$gdb_cv_var_bfd_elf" = yes; then AC_ARG_WITH(lzma, AS_HELP_STRING([--with-lzma], [support lzma compression (auto/yes/no)]), [], [with_lzma=auto]) diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 255c77e..332f5b7 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -505,7 +505,7 @@ powerpc-*-openbsd*) ;; powerpc-*-aix* | rs6000-*-* | powerpc64-*-aix*) # Target: PowerPC running AIX - gdb_target_obs="rs6000-tdep.o rs6000-aix-tdep.o xcoffread.o \ + gdb_target_obs="rs6000-tdep.o rs6000-aix-tdep.o \ ppc-sysv-tdep.o solib-aix.o \ ravenscar-thread.o ppc-ravenscar-thread.o" ;; @@ -522,8 +522,8 @@ powerpc*-*-linux*) powerpc-*-lynx*178) # Target: PowerPC running Lynx178. gdb_target_obs="rs6000-tdep.o rs6000-lynx178-tdep.o \ - xcoffread.o ppc-sysv-tdep.o \ - ravenscar-thread.o ppc-ravenscar-thread.o" + ppc-sysv-tdep.o ravenscar-thread.o \ + ppc-ravenscar-thread.o" ;; powerpc*-*-*) # Target: PowerPC running eabi @@ -835,3 +835,17 @@ for t in x ${gdb_target_obs}; do gdb_have_gcore=true fi done + +# Decide which file formats are absolutely required based on +# the requested targets. Warn later that they are added, in +# case the user didn't manually request them, or all readers. +# It's fine to add the same format multiple times since the +# loop that reads the options to FORMAT_OBS will ensure that +# they are only added once. +for i in $gdb_target_obs; do + case "${i}" in + *"windows-tdep.o" ) target_formats="${target_formats} coff";; + "rs6000-aix-tdep.o" ) target_formats="${target_formats} xcoff";; + "rs6000-lynx178-tdep.o" ) target_formats="${target_formats} xcoff";; + esac +done diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 2bbaf14..d4a5a63 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -41277,6 +41277,14 @@ Configure @value{GDBN} for cross-debugging programs running on the specified list of targets. The special value @samp{all} configures @value{GDBN} for debugging programs running on any target it supports. +@item --enable-binary-file-formats=@r{[}@var{format}@r{]}@dots{} +@itemx --enable-binary-file-formats=all +Configure @value{GDBN} to support certain binary file formats. If a +format is the main (or only) file format for a given target, the +configure script may add support to it anyway, and warn the user. +If not given, all file formats that @value{GDBN} supports are compiled +in. + @item --with-gdb-datadir=@var{path} Set the @value{GDBN}-specific data directory. @value{GDBN} will look here for certain supporting files or scripts. This defaults to the diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c index 97677c0..ddf4935 100644 --- a/gdb/dwarf2/read-debug-names.c +++ b/gdb/dwarf2/read-debug-names.c @@ -768,12 +768,12 @@ build_and_check_cu_lists_from_debug_names (dwarf2_per_bfd *per_bfd, return build_and_check_cu_list_from_debug_names (per_bfd, dwz_map, dwz->info); } -/* This does all the work for dwarf2_read_debug_names, but putting it - into a separate function makes some cleanup a bit simpler. */ +/* See read-debug-names.h. */ -static bool -do_dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) +bool +dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) { + scoped_remove_all_units remove_all_units (*per_objfile->per_bfd); mapped_debug_names_reader map; mapped_debug_names_reader dwz_map; struct objfile *objfile = per_objfile->objfile; @@ -850,17 +850,7 @@ do_dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) (per_objfile, std::move (map))); auto idx = std::make_unique<debug_names_index> (std::move (cidn)); per_bfd->start_reading (std::move (idx)); + remove_all_units.disable (); return true; } - -/* See read-debug-names.h. */ - -bool -dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile) -{ - bool result = do_dwarf2_read_debug_names (per_objfile); - if (!result) - per_objfile->per_bfd->all_units.clear (); - return result; -} diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index 76317fe..fc7b654 100644 --- a/gdb/dwarf2/read-gdb-index.c +++ b/gdb/dwarf2/read-gdb-index.c @@ -1419,7 +1419,7 @@ create_addrmap_from_gdb_index (dwarf2_per_objfile *per_objfile, cu_index = extract_unsigned_integer (iter, 4, BFD_ENDIAN_LITTLE); iter += 4; - if (lo > hi) + if (lo >= hi) { complaint (_(".gdb_index address table has invalid range (%s - %s)"), hex_string (lo), hex_string (hi)); @@ -1489,6 +1489,7 @@ dwarf2_read_gdb_index offset_type cu_list_elements, types_list_elements, dwz_list_elements = 0; struct objfile *objfile = per_objfile->objfile; dwarf2_per_bfd *per_bfd = per_objfile->per_bfd; + scoped_remove_all_units remove_all_units (*per_bfd); gdb::array_view<const gdb_byte> main_index_contents = get_gdb_index_contents (objfile, per_bfd); @@ -1544,10 +1545,7 @@ dwarf2_read_gdb_index an index. */ if (per_bfd->infos.size () > 1 || per_bfd->types.size () > 1) - { - per_bfd->all_units.clear (); - return false; - } + return false; dwarf2_section_info *section = (per_bfd->types.size () == 1 @@ -1566,6 +1564,7 @@ dwarf2_read_gdb_index set_main_name_from_gdb_index (per_objfile, map.get ()); per_bfd->index_table = std::move (map); + remove_all_units.disable (); return true; } diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index ec8d376..7bd0850 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -3679,6 +3679,10 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile, void finalize_all_units (dwarf2_per_bfd *per_bfd) { + /* Sanity check. */ + gdb_assert (per_bfd->all_units.size () + == per_bfd->num_comp_units + per_bfd->num_type_units); + /* Ensure that the all_units vector is in the expected order for dwarf2_find_containing_unit to be able to perform a binary search. */ std::sort (per_bfd->all_units.begin (), per_bfd->all_units.end (), @@ -3694,6 +3698,7 @@ void create_all_units (dwarf2_per_objfile *per_objfile) { gdb_assert (per_objfile->per_bfd->all_units.empty ()); + scoped_remove_all_units remove_all_units (*per_objfile->per_bfd); signatured_type_set sig_types; @@ -3714,8 +3719,6 @@ create_all_units (dwarf2_per_objfile *per_objfile) if (!dwz->types.empty ()) { - per_objfile->per_bfd->all_units.clear (); - /* See enhancement PR symtab/30838. */ error (_(DWARF_ERROR_PREFIX ".debug_types section not supported in dwz file")); @@ -3725,6 +3728,7 @@ create_all_units (dwarf2_per_objfile *per_objfile) per_objfile->per_bfd->signatured_types = std::move (sig_types); finalize_all_units (per_objfile->per_bfd); + remove_all_units.disable (); } /* Return the initial uleb128 in the die at INFO_PTR. */ @@ -4504,9 +4508,9 @@ quirk_rust_enum (struct type *type, struct objfile *objfile) else { struct type *disr_type = nullptr; - for (int i = 0; i < type->num_fields (); ++i) + for (const auto &field : type->fields ()) { - disr_type = type->field (i).type (); + disr_type = field.type (); if (disr_type->code () != TYPE_CODE_STRUCT) { @@ -4545,7 +4549,7 @@ quirk_rust_enum (struct type *type, struct objfile *objfile) field *new_fields = (struct field *) TYPE_ZALLOC (type, ((type->num_fields () + 1) * sizeof (struct field))); - memcpy (new_fields + 1, type->fields (), + memcpy (new_fields + 1, type->fields ().data (), type->num_fields () * sizeof (struct field)); type->set_fields (new_fields); type->set_num_fields (type->num_fields () + 1); @@ -4559,13 +4563,12 @@ quirk_rust_enum (struct type *type, struct objfile *objfile) variant name. For convenience we build a map here. */ struct type *enum_type = disr_field->type (); gdb::unordered_map<std::string_view, ULONGEST> discriminant_map; - for (int i = 0; i < enum_type->num_fields (); ++i) + for (const auto &field : enum_type->fields ()) { - if (enum_type->field (i).loc_kind () == FIELD_LOC_KIND_ENUMVAL) + if (field.loc_kind () == FIELD_LOC_KIND_ENUMVAL) { - const char *name - = rust_last_path_segment (enum_type->field (i).name ()); - discriminant_map[name] = enum_type->field (i).loc_enumval (); + const char *name = rust_last_path_segment (field.name ()); + discriminant_map[name] = field.loc_enumval (); } } @@ -4601,7 +4604,7 @@ quirk_rust_enum (struct type *type, struct objfile *objfile) if (sub_type->num_fields () > 0) { sub_type->set_num_fields (sub_type->num_fields () - 1); - sub_type->set_fields (sub_type->fields () + 1); + sub_type->set_fields (sub_type->fields ().data () + 1); } type->field (i).set_name (variant_name); sub_type->set_name @@ -10629,7 +10632,6 @@ dwarf2_add_member_fn (struct field_info *fip, struct die_info *die, smash_to_method_type (fnp->type, type, this_type->target_type (), this_type->fields (), - this_type->num_fields (), this_type->has_varargs ()); /* Handle static member functions. @@ -10851,8 +10853,7 @@ quirk_gcc_member_function_pointer (struct type *type, struct objfile *objfile) self_type = pfn_type->field (0).type ()->target_type (); new_type = type_allocator (type).new_type (); smash_to_method_type (new_type, self_type, pfn_type->target_type (), - pfn_type->fields (), pfn_type->num_fields (), - pfn_type->has_varargs ()); + pfn_type->fields (), pfn_type->has_varargs ()); smash_to_methodptr_type (type, new_type); } @@ -12773,8 +12774,7 @@ read_tag_ptr_to_member_type (struct die_info *die, struct dwarf2_cu *cu) = type_allocator (cu->per_objfile->objfile, cu->lang ()).new_type (); smash_to_method_type (new_type, domain, to_type->target_type (), - to_type->fields (), to_type->num_fields (), - to_type->has_varargs ()); + to_type->fields (), to_type->has_varargs ()); type = lookup_methodptr_type (new_type); } else diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h index 4e3f8d7..74ec420 100644 --- a/gdb/dwarf2/read.h +++ b/gdb/dwarf2/read.h @@ -673,6 +673,36 @@ public: std::string captured_debug_dir; }; +/* Scoped object to remove all units from PER_BFD and clear other associated + fields in case of failure. */ + +struct scoped_remove_all_units +{ + explicit scoped_remove_all_units (dwarf2_per_bfd &per_bfd) + : m_per_bfd (&per_bfd) + {} + + DISABLE_COPY_AND_ASSIGN (scoped_remove_all_units); + + ~scoped_remove_all_units () + { + if (m_per_bfd == nullptr) + return; + + m_per_bfd->all_units.clear (); + m_per_bfd->num_comp_units = 0; + m_per_bfd->num_type_units = 0; + } + + /* Disable this object. Call this to keep the units of M_PER_BFD on the + success path. */ + void disable () { m_per_bfd = nullptr; } + +private: + /* This is nullptr if the object is disabled. */ + dwarf2_per_bfd *m_per_bfd; +}; + /* An iterator for all_units that is based on index. This approach makes it possible to iterate over all_units safely, when some caller in the loop may add new units. */ @@ -492,7 +492,7 @@ fake_method::fake_method (type_instance_flags flags, fake_method::~fake_method () { - xfree (m_type.fields ()); + xfree (m_type.fields ().data ()); } namespace expr diff --git a/gdb/event-top.c b/gdb/event-top.c index f96982a..3138e8c 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -1180,6 +1180,9 @@ quit (void) throw_forced_quit ("SIGTERM"); } + /* Pressing ^C cancels i-search. Tell readline that a ^C happened. */ + rl_callback_sigcleanup (); + #ifdef __MSDOS__ /* No steenking SIGINT will ever be coming our way when the program is resumed. Don't lie. */ diff --git a/gdb/f-typeprint.c b/gdb/f-typeprint.c index 7d0cdc0..e96d27c 100644 --- a/gdb/f-typeprint.c +++ b/gdb/f-typeprint.c @@ -299,8 +299,6 @@ void f_language::f_type_print_base (struct type *type, struct ui_file *stream, int show, int level) const { - int index; - QUIT; stream->wrap_here (4); @@ -423,14 +421,13 @@ f_language::f_type_print_base (struct type *type, struct ui_file *stream, if (show > 0) { gdb_puts ("\n", stream); - for (index = 0; index < type->num_fields (); index++) + for (const auto &field : type->fields ()) { - f_type_print_base (type->field (index).type (), stream, - show - 1, level + 4); + f_type_print_base (field.type (), stream, show - 1, level + 4); gdb_puts (" :: ", stream); - fputs_styled (type->field (index).name (), + fputs_styled (field.name (), variable_name_style.style (), stream); - f_type_print_varspec_suffix (type->field (index).type (), + f_type_print_varspec_suffix (field.type (), stream, show - 1, 0, 0, 0, false); gdb_puts ("\n", stream); } diff --git a/gdb/frame.c b/gdb/frame.c index fc4cbca..50223f4 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2663,15 +2663,14 @@ get_prev_frame (const frame_info_ptr &this_frame) { FRAME_SCOPED_DEBUG_ENTER_EXIT; - CORE_ADDR frame_pc; - int frame_pc_p; + std::optional<CORE_ADDR> frame_pc; /* There is always a frame. If this assertion fails, suspect that something should be calling get_selected_frame() or get_current_frame(). */ gdb_assert (this_frame != NULL); - frame_pc_p = get_frame_pc_if_available (this_frame, &frame_pc); + frame_pc = get_frame_pc_if_available (this_frame); /* tausq/2004-12-07: Dummy frames are skipped because it doesn't make much sense to stop unwinding at a dummy frame. One place where a dummy @@ -2686,7 +2685,7 @@ get_prev_frame (const frame_info_ptr &this_frame) if (this_frame->level >= 0 && get_frame_type (this_frame) == NORMAL_FRAME && !user_set_backtrace_options.backtrace_past_main - && frame_pc_p + && frame_pc.has_value () && inside_main_func (this_frame)) /* Don't unwind past main(). Note, this is done _before_ the frame has been marked as previously unwound. That way if the @@ -2733,7 +2732,7 @@ get_prev_frame (const frame_info_ptr &this_frame) if (this_frame->level >= 0 && get_frame_type (this_frame) == NORMAL_FRAME && !user_set_backtrace_options.backtrace_past_entry - && frame_pc_p + && frame_pc.has_value () && inside_entry_func (this_frame)) { frame_debug_got_null_frame (this_frame, "inside entry func"); @@ -2747,7 +2746,7 @@ get_prev_frame (const frame_info_ptr &this_frame) && (get_frame_type (this_frame) == NORMAL_FRAME || get_frame_type (this_frame) == INLINE_FRAME) && get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME - && frame_pc_p && frame_pc == 0) + && frame_pc.has_value () && *frame_pc == 0) { frame_debug_got_null_frame (this_frame, "zero PC"); return NULL; @@ -2763,25 +2762,24 @@ get_frame_pc (const frame_info_ptr &frame) return frame_unwind_pc (frame_info_ptr (frame->next)); } -bool -get_frame_pc_if_available (const frame_info_ptr &frame, CORE_ADDR *pc) +std::optional<CORE_ADDR> +get_frame_pc_if_available (const frame_info_ptr &frame) { + std::optional<CORE_ADDR> pc; gdb_assert (frame->next != NULL); try { - *pc = frame_unwind_pc (frame_info_ptr (frame->next)); + pc = frame_unwind_pc (frame_info_ptr (frame->next)); } catch (const gdb_exception_error &ex) { - if (ex.error == NOT_AVAILABLE_ERROR) - return false; - else + if (ex.error != NOT_AVAILABLE_ERROR) throw; } - return true; + return pc; } /* Return an address that falls within THIS_FRAME's code block. */ @@ -2870,7 +2868,7 @@ find_frame_sal (const frame_info_ptr &frame) { frame_info_ptr next_frame; int notcurrent; - CORE_ADDR pc; + std::optional<CORE_ADDR> pc; if (frame_inlined_callees (frame) > 0) { @@ -2914,11 +2912,11 @@ find_frame_sal (const frame_info_ptr &frame) PC and such a PC indicates the current (rather than next) instruction/line, consequently, for such cases, want to get the line containing fi->pc. */ - if (!get_frame_pc_if_available (frame, &pc)) + if (!(pc = get_frame_pc_if_available (frame))) return {}; - notcurrent = (pc != get_frame_address_in_block (frame)); - return find_pc_line (pc, notcurrent); + notcurrent = (*pc != get_frame_address_in_block (frame)); + return find_pc_line (*pc, notcurrent); } /* Per "frame.h", return the ``address'' of the frame. Code should diff --git a/gdb/frame.h b/gdb/frame.h index b240662..99a7983 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -504,7 +504,8 @@ extern CORE_ADDR get_frame_pc (const frame_info_ptr &); /* Same as get_frame_pc, but return a boolean indication of whether the PC is actually available, instead of throwing an error. */ -extern bool get_frame_pc_if_available (const frame_info_ptr &frame, CORE_ADDR *pc); +extern std::optional<CORE_ADDR> get_frame_pc_if_available + (const frame_info_ptr &frame); /* An address (not necessarily aligned to an instruction boundary) that falls within THIS frame's code block. diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c index 14a903b..24e6d0b 100644 --- a/gdb/gdbtypes.c +++ b/gdb/gdbtypes.c @@ -1080,10 +1080,10 @@ get_discrete_low_bound (struct type *type) entries. */ LONGEST low = type->field (0).loc_enumval (); - for (int i = 0; i < type->num_fields (); i++) + for (const auto &field : type->fields ()) { - if (type->field (i).loc_enumval () < low) - low = type->field (i).loc_enumval (); + if (field.loc_enumval () < low) + low = field.loc_enumval (); } return low; @@ -1147,10 +1147,10 @@ get_discrete_high_bound (struct type *type) entries. */ LONGEST high = type->field (0).loc_enumval (); - for (int i = 0; i < type->num_fields (); i++) + for (const auto &field : type->fields ()) { - if (type->field (i).loc_enumval () > high) - high = type->field (i).loc_enumval (); + if (field.loc_enumval () > high) + high = field.loc_enumval (); } return high; @@ -1602,15 +1602,15 @@ smash_to_methodptr_type (struct type *type, struct type *to_type) void smash_to_method_type (struct type *type, struct type *self_type, - struct type *to_type, struct field *args, - int nargs, int varargs) + struct type *to_type, gdb::array_view<struct field> args, + int varargs) { smash_type (type); type->set_code (TYPE_CODE_METHOD); type->set_target_type (to_type); set_type_self_type (type, self_type); - type->set_fields (args); - type->set_num_fields (nargs); + type->set_fields (args.data ()); + type->set_num_fields (args.size ()); if (varargs) type->set_has_varargs (true); @@ -2494,23 +2494,22 @@ resolve_dynamic_union (struct type *type, const frame_info_ptr &frame) { struct type *resolved_type; - int i; unsigned int max_len = 0; gdb_assert (type->code () == TYPE_CODE_UNION); resolved_type = copy_type (type); resolved_type->copy_fields (type); - for (i = 0; i < resolved_type->num_fields (); ++i) + for (auto &field : resolved_type->fields ()) { struct type *t; - if (type->field (i).is_static ()) + if (field.is_static ()) continue; - t = resolve_dynamic_type_internal (resolved_type->field (i).type (), - addr_stack, frame, false); - resolved_type->field (i).set_type (t); + t = resolve_dynamic_type_internal (field.type (), addr_stack, + frame, false); + field.set_type (t); struct type *real_type = check_typedef (t); if (real_type->length () > max_len) @@ -2791,7 +2790,6 @@ resolve_dynamic_struct (struct type *type, const frame_info_ptr &frame) { struct type *resolved_type; - int i; unsigned resolved_type_bit_length = 0; gdb_assert (type->code () == TYPE_CODE_STRUCT); @@ -2812,22 +2810,21 @@ resolve_dynamic_struct (struct type *type, resolved_type->copy_fields (type); } - for (i = 0; i < resolved_type->num_fields (); ++i) + for (auto &field : resolved_type->fields ()) { unsigned new_bit_length; - if (resolved_type->field (i).is_static ()) + if (field.is_static ()) continue; - resolve_dynamic_field (resolved_type->field (i), addr_stack, frame); + resolve_dynamic_field (field, addr_stack, frame); - new_bit_length = resolved_type->field (i).loc_bitpos (); - if (resolved_type->field (i).bitsize () != 0) - new_bit_length += resolved_type->field (i).bitsize (); + new_bit_length = field.loc_bitpos (); + if (field.bitsize () != 0) + new_bit_length += field.bitsize (); else { - struct type *real_type - = check_typedef (resolved_type->field (i).type ()); + struct type *real_type = check_typedef (field.type ()); new_bit_length += (real_type->length () * TARGET_CHAR_BIT); } @@ -3394,7 +3391,8 @@ check_stub_method (struct type *type, int method_id, int signature_id) /* MTYPE may currently be a function (TYPE_CODE_FUNC). We want a method (TYPE_CODE_METHOD). */ smash_to_method_type (mtype, type, mtype->target_type (), - argtypes, argcount, p[-2] == '.'); + gdb::make_array_view (argtypes, argcount), + p[-2] == '.'); mtype->set_is_stub (false); TYPE_FN_FIELD_STUB (f, signature_id) = 0; } @@ -3698,12 +3696,12 @@ type_align (struct type *type) case TYPE_CODE_UNION: { int number_of_non_static_fields = 0; - for (unsigned i = 0; i < type->num_fields (); ++i) + for (const auto &field : type->fields ()) { - if (!type->field (i).is_static ()) + if (!field.is_static ()) { number_of_non_static_fields++; - ULONGEST f_align = type_align (type->field (i).type ()); + ULONGEST f_align = type_align (field.type ()); if (f_align == 0) { /* Don't pretend we know something we don't. */ @@ -5038,19 +5036,14 @@ rank_one_type (struct type *parm, struct type *arg, struct value *value) situation. */ static void -print_args (struct field *args, int nargs, int spaces) +print_args (gdb::array_view<struct field> args, int spaces) { - if (args != NULL) + for (int i = 0; i < args.size (); i++) { - int i; - - for (i = 0; i < nargs; i++) - { - gdb_printf - ("%*s[%d] name '%s'\n", spaces, "", i, - args[i].name () != NULL ? args[i].name () : "<NULL>"); - recursive_dump_type (args[i].type (), spaces + 2); - } + gdb_printf + ("%*s[%d] name '%s'\n", spaces, "", i, + args[i].name () != NULL ? args[i].name () : "<NULL>"); + recursive_dump_type (args[i].type (), spaces + 2); } } @@ -5091,9 +5084,8 @@ dump_fn_fieldlists (struct type *type, int spaces) gdb_printf ("%*sargs %s\n", spaces + 8, "", - host_address_to_string (TYPE_FN_FIELD_ARGS (f, overload_idx))); + host_address_to_string (TYPE_FN_FIELD_ARGS (f, overload_idx).data ())); print_args (TYPE_FN_FIELD_ARGS (f, overload_idx), - TYPE_FN_FIELD_TYPE (f, overload_idx)->num_fields (), spaces + 8 + 2); gdb_printf ("%*sfcontext %s\n", spaces + 8, "", @@ -5375,7 +5367,7 @@ recursive_dump_type (struct type *type, int spaces) } gdb_printf ("\n"); } - gdb_printf ("%s\n", host_address_to_string (type->fields ())); + gdb_printf ("%s\n", host_address_to_string (type->fields ().data ())); for (idx = 0; idx < type->num_fields (); idx++) { field &fld = type->field (idx); @@ -5756,7 +5748,7 @@ append_composite_type_field_raw (struct type *t, const char *name, struct field *f; t->set_num_fields (t->num_fields () + 1); - t->set_fields (XRESIZEVEC (struct field, t->fields (), + t->set_fields (XRESIZEVEC (struct field, t->fields ().data (), t->num_fields ())); f = &t->field (t->num_fields () - 1); memset (f, 0, sizeof f[0]); @@ -5907,7 +5899,7 @@ type::alloc_fields (unsigned int nfields, bool init) return; } - size_t size = nfields * sizeof (*this->fields ()); + size_t size = nfields * sizeof (struct field); struct field *fields = (struct field *) (init ? TYPE_ZALLOC (this, size) @@ -5926,8 +5918,8 @@ type::copy_fields (struct type *src) if (nfields == 0) return; - size_t size = nfields * sizeof (*this->fields ()); - memcpy (this->fields (), src->fields (), size); + size_t size = nfields * sizeof (struct field); + memcpy (this->fields ().data (), src->fields ().data (), size); } /* See gdbtypes.h. */ @@ -5940,8 +5932,8 @@ type::copy_fields (std::vector<struct field> &src) if (nfields == 0) return; - size_t size = nfields * sizeof (*this->fields ()); - memcpy (this->fields (), src.data (), size); + size_t size = nfields * sizeof (struct field); + memcpy (this->fields ().data (), src.data (), size); } /* See gdbtypes.h. */ diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h index 9e2efe9..75c77b3 100644 --- a/gdb/gdbtypes.h +++ b/gdb/gdbtypes.h @@ -1080,12 +1080,6 @@ struct type this->main_type->m_nfields = num_fields; } - /* Get the fields array of this type. */ - struct field *fields () const - { - return this->main_type->flds_bnds.fields; - } - /* Get the field at index IDX. */ struct field &field (int idx) const { @@ -1093,6 +1087,13 @@ struct type return this->fields ()[idx]; } + /* Return an array view of the fields. */ + gdb::array_view<struct field> fields () const + { + return gdb::make_array_view (this->main_type->flds_bnds.fields, + num_fields ()); + } + /* Set the fields array of this type. */ void set_fields (struct field *fields) { @@ -2470,8 +2471,9 @@ extern struct type *lookup_memberptr_type (struct type *, struct type *); extern struct type *lookup_methodptr_type (struct type *); extern void smash_to_method_type (struct type *type, struct type *self_type, - struct type *to_type, struct field *args, - int nargs, int varargs); + struct type *to_type, + gdb::array_view<struct field> args, + int varargs); extern void smash_to_memberptr_type (struct type *, struct type *, struct type *); diff --git a/gdb/guile/scm-type.c b/gdb/guile/scm-type.c index 5114579..13676ce 100644 --- a/gdb/guile/scm-type.c +++ b/gdb/guile/scm-type.c @@ -510,7 +510,7 @@ tyscm_field_smob_to_field (field_smob *f_smob) struct type *type = tyscm_field_smob_containing_type (f_smob); /* This should be non-NULL by construction. */ - gdb_assert (type->fields () != NULL); + gdb_assert (type->fields ().data () != nullptr); return &type->field (f_smob->field_num); } diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index f97c98e..21a5a28 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -8977,41 +8977,12 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) tdep->num_core_regs = I386_NUM_GREGS + I387_NUM_REGS; tdep->register_names = i386_register_names; - /* No upper YMM registers. */ - tdep->ymmh_register_names = NULL; - tdep->ymm0h_regnum = -1; - - /* No upper ZMM registers. */ - tdep->zmmh_register_names = NULL; - tdep->zmm0h_regnum = -1; - - /* No high XMM registers. */ - tdep->xmm_avx512_register_names = NULL; - tdep->xmm16_regnum = -1; - - /* No upper YMM16-31 registers. */ - tdep->ymm16h_register_names = NULL; - tdep->ymm16h_regnum = -1; - tdep->num_byte_regs = 8; tdep->num_word_regs = 8; tdep->num_dword_regs = 0; tdep->num_mmx_regs = 8; tdep->num_ymm_regs = 0; - /* No AVX512 registers. */ - tdep->k0_regnum = -1; - tdep->num_zmm_regs = 0; - tdep->num_ymm_avx512_regs = 0; - tdep->num_xmm_avx512_regs = 0; - - /* No PKEYS registers */ - tdep->pkru_regnum = -1; - tdep->num_pkeys_regs = 0; - - /* No segment base registers. */ - tdep->fsbase_regnum = -1; - tdesc_arch_data_up tdesc_data = tdesc_data_alloc (); set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction); diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index e849b33..e4895b1 100644 --- a/gdb/i386-tdep.h +++ b/gdb/i386-tdep.h @@ -69,56 +69,57 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base size_t sizeof_fpregset = 0; /* Register number for %st(0). The register numbers for the other - registers follow from this one. Set this to -1 to indicate the - absence of an FPU. */ - int st0_regnum = 0; + registers follow from this one. Set this to a value >= 0 if FPU is + present. */ + int st0_regnum = -1; /* Number of MMX registers. */ int num_mmx_regs = 0; - /* Register number for %mm0. Set this to -1 to indicate the absence - of MMX support. */ - int mm0_regnum = 0; + /* Register number for %mm0. Set this to a value >= 0 if MMX is + supported. */ + int mm0_regnum = -1; /* Number of pseudo YMM registers. */ int num_ymm_regs = 0; - /* Register number for %ymm0. Set this to -1 to indicate the absence - of pseudo YMM register support. */ - int ymm0_regnum = 0; + /* Register number for %ymm0. Set this to a value >= 0 if pseudo YMM + registers are supported. */ + int ymm0_regnum = -1; /* Number of AVX512 OpMask registers (K-registers) */ int num_k_regs = 0; - /* Register number for %k0. Set this to -1 to indicate the absence - of AVX512 OpMask register support. */ - int k0_regnum = 0; + /* Register number for %k0. Set this to a value >= 0 if AVX512 OpMask + is supported. */ + int k0_regnum = -1; /* Number of pseudo ZMM registers ($zmm0-$zmm31). */ int num_zmm_regs = 0; - /* Register number for %zmm0. Set this to -1 to indicate the absence - of pseudo ZMM register support. */ - int zmm0_regnum = 0; + /* Register number for %zmm0. Set this to a value >= 0 if pseudo ZMM + registers are supported. */ + int zmm0_regnum = -1; /* Number of byte registers. */ int num_byte_regs = 0; - /* Register pseudo number for %al. */ - int al_regnum = 0; + /* Register pseudo number for %al. If supported, set this to a + value >= 0. */ + int al_regnum = -1; /* Number of pseudo word registers. */ int num_word_regs = 0; - /* Register number for %ax. */ - int ax_regnum = 0; + /* Register number for %ax. If supported, set this to a value >= 0. */ + int ax_regnum = -1; /* Number of pseudo dword registers. */ int num_dword_regs = 0; - /* Register number for %eax. Set this to -1 to indicate the absence - of pseudo dword register support. */ - int eax_regnum = 0; + /* Register number for %eax. Set this to a value >= 0 if pseudo dword + registers are supported. */ + int eax_regnum = -1; /* Number of core registers. */ int num_core_regs = 0; @@ -129,14 +130,16 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base /* Number of SSE registers added in AVX512. */ int num_xmm_avx512_regs = 0; - /* Register number of XMM16, the first XMM register added in AVX512. */ - int xmm16_regnum = 0; + /* Register number of XMM16, the first XMM register added in AVX512. + Set this to a value >= 0 if XMM registers are supported. */ + int xmm16_regnum = -1; /* Number of YMM registers added in AVX512. */ int num_ymm_avx512_regs = 0; - /* Register number of YMM16, the first YMM register added in AVX512. */ - int ymm16_regnum = 0; + /* Register number of YMM16, the first YMM register added in AVX512. + Set this to a value >= 0 if YMM registers are supported. */ + int ymm16_regnum = -1; /* Bits of the extended control register 0 (the XFEATURE_ENABLED_MASK register), excluding the x87 bit, which are supported by this GDB. */ @@ -152,23 +155,23 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base /* Register names. */ const char * const *register_names = nullptr; - /* Register number for %ymm0h. Set this to -1 to indicate the absence - of upper YMM register support. */ - int ymm0h_regnum = 0; + /* Register number for %ymm0h. Set this to a value >= 0 if they are + supported. */ + int ymm0h_regnum = -1; /* Upper YMM register names. Only used for tdesc_numbered_register. */ const char * const *ymmh_register_names = nullptr; - /* Register number for %ymm16h. Set this to -1 to indicate the absence - of support for YMM16-31. */ - int ymm16h_regnum = 0; + /* Register number for %ymm16h. Set this to a value >= 0 if they are + supported. */ + int ymm16h_regnum = -1; /* YMM16-31 register names. Only used for tdesc_numbered_register. */ const char * const *ymm16h_register_names = nullptr; - /* Register number for %zmm0h. Set this to -1 to indicate the absence - of ZMM_HI256 register support. */ - int zmm0h_regnum = 0; + /* Register number for %zmm0h. Set this to a value >= 0 if ZMM_HI256 + registers are supported. */ + int zmm0h_regnum = -1; /* OpMask register names. */ const char * const *k_register_names = nullptr; @@ -185,15 +188,16 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base /* Number of PKEYS registers. */ int num_pkeys_regs = 0; - /* Register number for PKRU register. */ - int pkru_regnum = 0; + /* Register number for PKRU register. If supported, set this to a value + >= 0. */ + int pkru_regnum = -1; /* PKEYS register names. */ const char * const *pkeys_register_names = nullptr; - /* Register number for %fsbase. Set this to -1 to indicate the - absence of segment base registers. */ - int fsbase_regnum = 0; + /* Register number for %fsbase. If supported, set this to a value + >= 0. */ + int fsbase_regnum = -1; /* Target description. */ const struct target_desc *tdesc = nullptr; diff --git a/gdb/macroscope.c b/gdb/macroscope.c index 7aa0784..a8b78c9 100644 --- a/gdb/macroscope.c +++ b/gdb/macroscope.c @@ -97,12 +97,12 @@ default_macro_scope () { struct symtab_and_line sal; frame_info_ptr frame; - CORE_ADDR pc; + std::optional<CORE_ADDR> pc; /* If there's a selected frame, use its PC. */ frame = deprecated_safe_get_selected_frame (); - if (frame && get_frame_pc_if_available (frame, &pc)) - sal = find_pc_line (pc, 0); + if (frame && (pc = get_frame_pc_if_available (frame))) + sal = find_pc_line (*pc, 0); /* Fall back to the current listing position. */ else @@ -1330,10 +1330,8 @@ captured_main_1 (struct captured_main_args *context) } static void -captured_main (void *data) +captured_main (captured_main_args *context) { - struct captured_main_args *context = (struct captured_main_args *) data; - captured_main_1 (context); /* NOTE: cagney/1999-11-07: There is probably no reason for not diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c index 5bb15c2..b9302c5 100644 --- a/gdb/mdebugread.c +++ b/gdb/mdebugread.c @@ -1034,7 +1034,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend, t->set_code (type_code); t->set_length (sh->value); t->alloc_fields (nfields); - f = t->fields(); + f = t->fields ().data (); if (type_code == TYPE_CODE_ENUM) { diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c index 6d8dce8..8c0854b 100644 --- a/gdb/nat/aarch64-hw-point.c +++ b/gdb/nat/aarch64-hw-point.c @@ -710,10 +710,8 @@ aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state, itself. For instance, the access size of an stp instruction is 16. So, if we use stp to store to address p, and set a watchpoint on address p + 8, the reported ADDR_TRAP can be p + 8 (observed on - RK3399 SOC). But it also can be p (observed on M1 SOC). Checking - for this situation introduces the possibility of false positives, - so we only do this for hw_write watchpoints. */ - const CORE_ADDR max_access_size = type == hw_write ? 16 : 8; + RK3399 SOC). But it also can be p (observed on M1 SOC). */ + const CORE_ADDR max_access_size = 16; const CORE_ADDR addr_watch_base = addr_watch_aligned - (max_access_size - AARCH64_HWP_MAX_LEN_PER_REG); if (!(addr_trap >= addr_watch_base diff --git a/gdb/ppc-sysv-tdep.c b/gdb/ppc-sysv-tdep.c index cae5aa6..f872f73 100644 --- a/gdb/ppc-sysv-tdep.c +++ b/gdb/ppc-sysv-tdep.c @@ -2060,10 +2060,12 @@ ppc64_sysv_abi_return_value (struct gdbarch *gdbarch, struct value *function, } /* Small character arrays are returned, right justified, in r3. */ - if (valtype->code () == TYPE_CODE_ARRAY + if (tdep->elf_abi == POWERPC_ELF_V1 + && valtype->code () == TYPE_CODE_ARRAY && !valtype->is_vector () && valtype->length () <= 8 - && valtype->target_type ()->code () == TYPE_CODE_INT + && (valtype->target_type ()->code () == TYPE_CODE_INT + || valtype->target_type ()->code () == TYPE_CODE_CHAR) && valtype->target_type ()->length () == 1) { int regnum = tdep->ppc_gp0_regnum + 3; diff --git a/gdb/printcmd.c b/gdb/printcmd.c index 19fbc20..c6d7075 100644 --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -753,10 +753,10 @@ pc_prefix (CORE_ADDR addr) if (has_stack_frames ()) { frame_info_ptr frame; - CORE_ADDR pc; + std::optional<CORE_ADDR> pc; frame = get_selected_frame (NULL); - if (get_frame_pc_if_available (frame, &pc) && pc == addr) + if ((pc = get_frame_pc_if_available (frame)) && *pc == addr) return "=> "; } return " "; @@ -1290,7 +1290,9 @@ should_validate_memtags (gdbarch *gdbarch, struct value *value) return false; /* We do. Check whether it includes any tags. */ - return target_is_address_tagged (gdbarch, value_as_address (value)); + struct type *val_type = value->type (); + const gdb_byte *data = value->contents ().data (); + return target_is_address_tagged (gdbarch, unpack_pointer (val_type, data)); } /* Helper for parsing arguments for print_command_1. */ diff --git a/gdb/python/lib/gdb/dap/varref.py b/gdb/python/lib/gdb/dap/varref.py index 8a13c51..1b54fe7 100644 --- a/gdb/python/lib/gdb/dap/varref.py +++ b/gdb/python/lib/gdb/dap/varref.py @@ -146,6 +146,10 @@ class BaseReference(ABC): if self._children is None: self._children = [None] * self.child_count() for idx in range(start, start + count): + if idx >= len(self._children): + raise DAPException( + f"requested child {idx} outside range of variable {self._ref}" + ) if self._children[idx] is None: (name, value) = self.fetch_one_child(idx) name = self._compute_name(name) diff --git a/gdb/python/lib/gdb/printing.py b/gdb/python/lib/gdb/printing.py index cba27d2..cda033f 100644 --- a/gdb/python/lib/gdb/printing.py +++ b/gdb/python/lib/gdb/printing.py @@ -415,11 +415,17 @@ def make_visualizer(value): result = NoOpArrayPrinter(ty, value) elif ty.code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION): result = NoOpStructPrinter(ty, value) - elif ty.code in ( - gdb.TYPE_CODE_PTR, - gdb.TYPE_CODE_REF, - gdb.TYPE_CODE_RVALUE_REF, + elif ( + ty.code + in ( + gdb.TYPE_CODE_PTR, + gdb.TYPE_CODE_REF, + gdb.TYPE_CODE_RVALUE_REF, + ) + and ty.target().code != gdb.TYPE_CODE_VOID ): + # Note we avoid "void *" here because those pointers can't + # be dereferenced without a cast. result = NoOpPointerReferencePrinter(value) else: result = NoOpScalarPrinter(value) diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c index 0ea629f..70e1684 100644 --- a/gdb/python/py-finishbreakpoint.c +++ b/gdb/python/py-finishbreakpoint.c @@ -175,7 +175,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs) struct frame_id frame_id; PyObject *internal = NULL; int internal_bp = 0; - CORE_ADDR pc; + std::optional<CORE_ADDR> pc; if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords, &frame_obj, &internal)) @@ -249,9 +249,9 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs) try { - if (get_frame_pc_if_available (frame, &pc)) + if ((pc = get_frame_pc_if_available (frame))) { - struct symbol *function = find_pc_function (pc); + struct symbol *function = find_pc_function (*pc); if (function != nullptr) { struct type *ret_type = diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c index c546aa7..10ae636 100644 --- a/gdb/python/py-type.c +++ b/gdb/python/py-type.c @@ -1312,10 +1312,9 @@ static PyObject * typy_has_key (PyObject *self, PyObject *args) { struct type *type = ((type_object *) self)->type; - const char *field; - int i; + const char *field_name; - if (!PyArg_ParseTuple (args, "s", &field)) + if (!PyArg_ParseTuple (args, "s", &field_name)) return NULL; /* We want just fields of this type, not of base types, so instead of @@ -1326,11 +1325,11 @@ typy_has_key (PyObject *self, PyObject *args) if (type == NULL) return NULL; - for (i = 0; i < type->num_fields (); i++) + for (const auto &field : type->fields ()) { - const char *t_field_name = type->field (i).name (); + const char *t_field_name = field.name (); - if (t_field_name && (strcmp_iw (t_field_name, field) == 0)) + if (t_field_name && (strcmp_iw (t_field_name, field_name) == 0)) Py_RETURN_TRUE; } Py_RETURN_FALSE; diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c index 3957413..3605552 100644 --- a/gdb/rust-lang.c +++ b/gdb/rust-lang.c @@ -126,15 +126,15 @@ rust_underscore_fields (struct type *type) if (type->code () != TYPE_CODE_STRUCT) return false; - for (int i = 0; i < type->num_fields (); ++i) + for (const auto &field : type->fields ()) { - if (!type->field (i).is_static ()) + if (!field.is_static ()) { char buf[20]; xsnprintf (buf, sizeof (buf), "%d", field_number); - const char *field_name = type->field (i).name (); + const char *field_name = field.name (); if (startswith (field_name, "__")) field_name += 2; if (strcmp (buf, field_name) != 0) @@ -376,11 +376,11 @@ rust_array_like_element_type (struct type *type) { /* Caller must check this. */ gdb_assert (rust_slice_type_p (type)); - for (int i = 0; i < type->num_fields (); ++i) + for (const auto &field : type->fields ()) { - if (strcmp (type->field (i).name (), "data_ptr") == 0) + if (strcmp (field.name (), "data_ptr") == 0) { - struct type *base_type = type->field (i).type ()->target_type (); + struct type *base_type = field.type ()->target_type (); if (rewrite_slice_type (base_type, nullptr, 0, nullptr)) return nullptr; return base_type; @@ -1017,9 +1017,9 @@ rust_internal_print_type (struct type *type, const char *varstring, } gdb_puts ("{\n", stream); - for (int i = 0; i < type->num_fields (); ++i) + for (const auto &field : type->fields ()) { - const char *name = type->field (i).name (); + const char *name = field.name (); QUIT; diff --git a/gdb/source.c b/gdb/source.c index 0fd370b..8cb5c36 100644 --- a/gdb/source.c +++ b/gdb/source.c @@ -1312,14 +1312,12 @@ print_source_lines_base (struct symtab *s, int line, int stopline, int nlines = stopline - line; struct ui_out *uiout = current_uiout; - /* Regardless of whether we can open the file, set current_source_symtab. */ + /* Regardless of whether we can open the file, we'll want to set + current_source_symtab, but not if throw an error, or return without + printing any source lines. */ current_source_location *loc = get_source_location (current_program_space); - loc->set (s, line); - first_line_listed = line; - last_line_listed = line; - /* If printing of source lines is disabled, just print file and line number. */ if (uiout->test_flags (ui_source_list) && source_open) @@ -1380,6 +1378,10 @@ print_source_lines_base (struct symtab *s, int line, int stopline, uiout->text ("\n"); } + loc->set (s, line); + first_line_listed = line; + last_line_listed = line; + return; } @@ -1399,12 +1401,9 @@ print_source_lines_base (struct symtab *s, int line, int stopline, } const char *iter = lines.c_str (); - int new_lineno = loc->line (); - while (nlines-- > 0 && *iter != '\0') + int new_lineno = line; + for (; nlines-- > 0 && *iter != '\0'; ++new_lineno) { - char buf[20]; - - last_line_listed = loc->line (); if (flags & PRINT_SOURCE_LINES_FILENAME) { uiout->message ("%ps", @@ -1415,7 +1414,6 @@ print_source_lines_base (struct symtab *s, int line, int stopline, uiout->message ("%ps\t", styled_string (line_number_style.style (), pulongest (new_lineno))); - ++new_lineno; while (*iter != '\0') { @@ -1457,6 +1455,8 @@ print_source_lines_base (struct symtab *s, int line, int stopline, } else if (*iter > 0 && *iter < 040) { + char buf[20]; + xsnprintf (buf, sizeof (buf), "^%c", *iter + 0100); uiout->text (buf); ++iter; @@ -1470,7 +1470,11 @@ print_source_lines_base (struct symtab *s, int line, int stopline, uiout->text ("\n"); } - loc->set (loc->symtab (), new_lineno); + /* As NEW_LINENO was incremented after displaying the last source line, + the last line shown was the one before NEW_LINENO. */ + first_line_listed = line; + last_line_listed = new_lineno - 1; + loc->set (s, new_lineno); } diff --git a/gdb/stabsread.c b/gdb/stabsread.c index 6ee61e0..74d0175 100644 --- a/gdb/stabsread.c +++ b/gdb/stabsread.c @@ -4431,8 +4431,9 @@ again: if (args == NULL) return error_type (pp, objfile); type = dbx_alloc_type (typenums, objfile); - smash_to_method_type (type, domain, return_type, args, - nargs, varargs); + smash_to_method_type (type, domain, return_type, + gdb::make_array_view (args, nargs), + varargs); } break; diff --git a/gdb/stack.c b/gdb/stack.c index e633566..e3fa1f4 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -1165,10 +1165,10 @@ do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts, if (set_current_sal) { - CORE_ADDR pc; + std::optional<CORE_ADDR> pc; - if (get_frame_pc_if_available (frame, &pc)) - last_displayed_symtab_info.set (sal.pspace, pc, sal.symtab, sal.line); + if ((pc = get_frame_pc_if_available (frame))) + last_displayed_symtab_info.set (sal.pspace, *pc, sal.symtab, sal.line); else last_displayed_symtab_info.invalidate (); } @@ -1325,16 +1325,15 @@ print_frame (struct ui_out *uiout, enum language funlang = language_unknown; struct value_print_options opts; struct symbol *func; - CORE_ADDR pc = 0; - int pc_p; + std::optional <CORE_ADDR> pc; - pc_p = get_frame_pc_if_available (frame, &pc); + pc = get_frame_pc_if_available (frame); gdb::unique_xmalloc_ptr<char> funname = find_frame_funname (frame, &funlang, &func); annotate_frame_begin (print_level ? frame_relative_level (frame) : 0, - gdbarch, pc); + gdbarch, pc.value_or (0)); { ui_out_emit_tuple tuple_emitter (uiout, "frame"); @@ -1352,8 +1351,8 @@ print_frame (struct ui_out *uiout, || print_what == LOC_AND_ADDRESS) { annotate_frame_address (); - if (pc_p) - print_pc (uiout, gdbarch, frame, pc); + if (pc.has_value ()) + print_pc (uiout, gdbarch, frame, *pc); else uiout->field_string ("addr", "<unavailable>", metadata_style.style ()); @@ -1422,7 +1421,7 @@ print_frame (struct ui_out *uiout, } if (print_what != SHORT_LOCATION - && pc_p && (funname == NULL || sal.symtab == NULL)) + && pc.has_value () && (funname == NULL || sal.symtab == NULL)) { const char *lib = solib_name_from_address (get_frame_program_space (frame), @@ -1481,8 +1480,7 @@ info_frame_command_core (const frame_info_ptr &fi, bool selected_frame_p) enum language funlang = language_unknown; const char *pc_regname; struct gdbarch *gdbarch; - CORE_ADDR frame_pc; - int frame_pc_p; + std::optional<CORE_ADDR> frame_pc; /* Initialize it to avoid "may be used uninitialized" warning. */ CORE_ADDR caller_pc = 0; int caller_pc_p = 0; @@ -1503,7 +1501,7 @@ info_frame_command_core (const frame_info_ptr &fi, bool selected_frame_p) get_frame_pc(). */ pc_regname = "pc"; - frame_pc_p = get_frame_pc_if_available (fi, &frame_pc); + frame_pc = get_frame_pc_if_available (fi); func = get_frame_function (fi); symtab_and_line sal = find_frame_sal (fi); s = sal.symtab; @@ -1525,9 +1523,9 @@ info_frame_command_core (const frame_info_ptr &fi, bool selected_frame_p) funname = func_only.get (); } } - else if (frame_pc_p) + else if (frame_pc.has_value ()) { - bound_minimal_symbol msymbol = lookup_minimal_symbol_by_pc (frame_pc); + bound_minimal_symbol msymbol = lookup_minimal_symbol_by_pc (*frame_pc); if (msymbol.minsym != NULL) { funname = msymbol.minsym->print_name (); @@ -1548,7 +1546,7 @@ info_frame_command_core (const frame_info_ptr &fi, bool selected_frame_p) gdb_puts (paddress (gdbarch, get_frame_base (fi))); gdb_printf (":\n"); gdb_printf (" %s = ", pc_regname); - if (frame_pc_p) + if (frame_pc.has_value ()) gdb_puts (paddress (gdbarch, get_frame_pc (fi))); else fputs_styled ("<unavailable>", metadata_style.style (), gdb_stdout); @@ -2337,9 +2335,9 @@ print_frame_local_vars (const frame_info_ptr &frame, { struct print_variable_and_value_data cb_data; const struct block *block; - CORE_ADDR pc; + std::optional<CORE_ADDR> pc; - if (!get_frame_pc_if_available (frame, &pc)) + if (!(pc = get_frame_pc_if_available (frame))) { if (!quiet) gdb_printf (stream, @@ -2499,11 +2497,11 @@ print_frame_arg_vars (const frame_info_ptr &frame, { struct print_variable_and_value_data cb_data; struct symbol *func; - CORE_ADDR pc; + std::optional<CORE_ADDR> pc; std::optional<compiled_regex> preg; std::optional<compiled_regex> treg; - if (!get_frame_pc_if_available (frame, &pc)) + if (!(pc = get_frame_pc_if_available (frame))) { if (!quiet) gdb_printf (stream, diff --git a/gdb/testsuite/gdb.ada/return-small-char-array.exp b/gdb/testsuite/gdb.ada/return-small-char-array.exp new file mode 100644 index 0000000..75c781e --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array.exp @@ -0,0 +1,40 @@ +# Copyright 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/>. + +load_lib "ada.exp" + +require allow_ada_tests + +standard_ada_testfile proc + +if { [gdb_compile_ada $srcfile $binfile executable debug] != "" } { + return -1 +} + +clean_restart $testfile + +set bp_location [gdb_get_line_number "STOP" $testdir/proc.adb] +runto "proc.adb:$bp_location" + +gdb_test "print Value.Name(My_Value)" \ + { = "abcd"} + +# Step into the function. +gdb_test "step 2" \ + "return Of_Value;" + +# and finish. +gdb_test "finish" \ + { = "abcd"} diff --git a/gdb/testsuite/gdb.ada/return-small-char-array/proc.adb b/gdb/testsuite/gdb.ada/return-small-char-array/proc.adb new file mode 100644 index 0000000..b18d9fe --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array/proc.adb @@ -0,0 +1,22 @@ +-- Copyright 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/>. + +with Value; +procedure Proc is + My_Value : Value.T := "abcd"; +begin + null; -- STOP + My_Value := Value.Name(My_Value); +end; diff --git a/gdb/testsuite/gdb.ada/return-small-char-array/value.adb b/gdb/testsuite/gdb.ada/return-small-char-array/value.adb new file mode 100644 index 0000000..2dd9faa --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array/value.adb @@ -0,0 +1,21 @@ +-- Copyright 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/>. + +package body Value is + function Name (Of_Value : T) return T is + begin + return Of_Value; + end Name; +end Value; diff --git a/gdb/testsuite/gdb.ada/return-small-char-array/value.ads b/gdb/testsuite/gdb.ada/return-small-char-array/value.ads new file mode 100644 index 0000000..16b171e --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array/value.ads @@ -0,0 +1,20 @@ +-- Copyright 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/>. + +package Value is + type T is new String (1 .. 4); + + function Name (Of_Value : T) return T; +end; diff --git a/gdb/testsuite/gdb.base/color-prompt.exp b/gdb/testsuite/gdb.base/color-prompt.exp new file mode 100644 index 0000000..c037185 --- /dev/null +++ b/gdb/testsuite/gdb.base/color-prompt.exp @@ -0,0 +1,29 @@ +# Copyright 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 using a prompt with color in CLI. + +# Using tuiterm requires setting TERM on host. +require {!is_remote host} + +# We use a tuiterm, which allows us to determine cursor position. +tuiterm_env +Term::clean_restart 8 80 + +# We start with an empty screen, to generate a visible prompt. +Term::gen_prompt + +set tui 0 +source $srcdir/$subdir/../gdb.tui/color-prompt.exp.tcl diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index 01e3cc1..d857165 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -759,7 +759,6 @@ set show_conv_list \ {$_probe_arg10 = <error: No frame selected>} \ {$_probe_arg11 = <error: No frame selected>} \ {$_cimag = <internal function _cimag>} \ - {$_colorsupport = "monochrome"} \ {$_creal = <internal function _creal>} \ {$_isvoid = <internal function _isvoid>} \ {$_shell = <internal function _shell>} \ @@ -767,8 +766,6 @@ set show_conv_list \ {$_gdb_maint_setting = <internal function _gdb_maint_setting>} \ {$_gdb_setting_str = <internal function _gdb_setting_str>} \ {$_gdb_setting = <internal function _gdb_setting>} \ - {$_gdb_major = 17} \ - {$_gdb_minor = 1} \ {$_shell_exitsignal = void} \ {$_shell_exitcode = 0} \ {$_active_linker_namespaces = 1} \ @@ -788,10 +785,52 @@ if [allow_python_tests] { {$_any_caller_matches = <internal function _any_caller_matches>} \ } } -gdb_test_list_exact "show convenience" "show convenience" \ - "\[^\r\n\]+\[\r\n\]+" \ - "\[^\r\n\]+" \ - $show_conv_list + +set lines [gdb_get_lines_no_pass "show convenience"] +set matches 0 +set all_found 1 +foreach s $show_conv_list { + if {  $lines] } { + verbose -log "didn't match: '$s'" + set all_found 0 + break + } + incr matches +} + +set re_var [string_to_regexp {$_colorsupport}] +if { [is_remote host] } { + set re_val {[^\r\n]+} +} else { + set re_val [string_to_regexp {"monochrome"}] +} +if { [regexp "$re_var = $re_val" $lines] } { + incr matches +} else { + set all_found 0 +} + +set re_vars \ + [list \ + [string_to_regexp {$_gdb_major}] \ + [string_to_regexp {$_gdb_minor}]] +foreach re_var $re_vars { + if { [regexp "$re_var = $decimal" $lines] } { + incr matches + } else { + set all_found 0 + } +} + +if { [regexp [string_to_regexp {$_tlb = void}] $lines] } { + incr matches +} else { + # Convenience variable _tlb is added only if support for windows targets + # is enabled. Don't complain if it's missing. +} + +gdb_assert { $all_found && $matches == [count_newlines $lines] } \ + "show convenience" #test show directories gdb_test "show directories" "Source directories searched: .cdir\[:;\].cwd" diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp index da17002..e7bf6f3 100644 --- a/gdb/testsuite/gdb.base/dlmopen.exp +++ b/gdb/testsuite/gdb.base/dlmopen.exp @@ -358,15 +358,19 @@ proc_with_prefix test_solib_unmap_events { } { # dynamic linker as pending when some instances of the library were # unloaded, despite there really only being one copy of the dynamic # linker actually loaded into the inferior's address space. - gdb_test_multiple "info breakpoints $bpnum" "check b/p status" { - -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+\\*$::hex\\s*\r\n\\s+stop only if \\(0\\)" { - fail $gdb_test_name - } - - -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex\\s*\[^\r\n\]+\r\n\\s+stop only if \\(0\\)" { - pass $gdb_test_name - } - } + set hs {[^\r\n]} + set re_pass \ + [multi_line \ + "" \ + [join \ + [list \ + "$bpnum" "breakpoint" "keep" "y" "$::hex$hs+"] \ + {\s+}] \ + [string cat \ + {\s+} \ + [string_to_regexp "stop only if (0)"] \ + ([string_to_regexp " (target evals)"])?]] + gdb_test "info breakpoints $bpnum" $re_pass "check b/p status" # With all the dlclose calls now complete, we should be back to a # single copy of the dynamic linker. diff --git a/gdb/testsuite/gdb.base/exprs.exp b/gdb/testsuite/gdb.base/exprs.exp index f703c18..81f6f19 100644 --- a/gdb/testsuite/gdb.base/exprs.exp +++ b/gdb/testsuite/gdb.base/exprs.exp @@ -285,11 +285,14 @@ gdb_test "print v_short + " \ gdb_test "print v_short =}{= 3" \ "A syntax error in expression, near `\\}\\{= 3'\\." +set hs {[^\r\n]} +set re_debug [string cat $hs* {[Ss]hift} $hs*] + gdb_test_no_output "set debug parse 1" set saw_start 0 set saw_val 0 gdb_test_multiple "print 23" "print with debugging" -lbl { - -re "\r\nStarting parse(?=\r\n)" { + -re "\r\n${re_debug}(?=\r\n)" { set saw_start 1 exp_continue } diff --git a/gdb/testsuite/gdb.base/readline-ask.exp b/gdb/testsuite/gdb.base/readline-ask.exp index 3f98e13..2948970 100644 --- a/gdb/testsuite/gdb.base/readline-ask.exp +++ b/gdb/testsuite/gdb.base/readline-ask.exp @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +require {!is_remote host} + standard_testfile .c set inputrc ${srcdir}/${subdir}/${testfile}.inputrc diff --git a/gdb/testsuite/gdb.base/readline.exp b/gdb/testsuite/gdb.base/readline.exp index 198d686..9b87790 100644 --- a/gdb/testsuite/gdb.base/readline.exp +++ b/gdb/testsuite/gdb.base/readline.exp @@ -21,6 +21,8 @@ # Tests for readline operations. # +require {!is_remote host} + # This function is used to test operate-and-get-next. # NAME is the name of the test. # ARGS is a list of alternating commands and expected results. diff --git a/gdb/testsuite/gdb.base/source-search.c b/gdb/testsuite/gdb.base/source-search.c new file mode 100644 index 0000000..2320c5c --- /dev/null +++ b/gdb/testsuite/gdb.base/source-search.c @@ -0,0 +1,127 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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/>. */ + +int +main (void) +{ + /* Line 21 */ + /* Line 22 */ + /* Line 23 */ + /* Line 24 */ + /* Line 25 */ + /* Line 26 */ + /* Line 27 */ + /* Line 28 */ + /* Line 29 */ + /* Line 30 */ + /* Line 31 */ + /* Line 32 */ + /* Line 33 */ + /* Line 34 */ + /* Line 35 */ + /* Line 36 */ + /* Line 37 */ + /* Line 38 */ + /* Line 39 */ + /* Line 40 */ + /* Line 41 */ + /* Line 42 */ + /* Line 43 */ + /* Line 44 */ + /* Line 45 */ + /* Line 46 */ + /* Line 47 */ + /* Line 48 */ + /* Line 49 */ + /* Line 50 */ + /* Line 51 */ + /* Line 52 */ + /* Line 53 */ + /* Line 54 */ + /* Line 55 */ + /* Line 56 */ + /* Line 57 */ + /* Line 58 */ + /* Line 59 */ + /* Line 60 */ + /* Line 61 */ + /* Line 62 */ + /* Line 63 */ + /* Line 64 */ + /* Line 65 */ + /* Line 66 */ + /* Line 67 */ + /* Line 68 */ + /* Line 69 */ + /* Line 70 */ + /* Line 71 */ + /* Line 72 */ + /* Line 73 */ + /* Line 74 */ + /* Line 75 */ + /* Line 76 */ + /* Line 77 */ + /* Line 78 */ + /* Line 79 */ + /* Line 80 */ + /* Line 81 */ + /* Line 82 */ + /* Line 83 */ + /* Line 84 */ + /* Line 85 */ + /* Line 86 */ + /* Line 87 */ + /* Line 88 */ + /* Line 89 */ + /* Line 90 */ + /* Line 91 */ + /* Line 92 */ + /* Line 93 */ + /* Line 94 */ + /* Line 95 */ + /* Line 96 */ + /* Line 97 */ + /* Line 98 */ + /* Line 99 */ + /* Line 100 */ + /* Line 101 */ + /* Line 102 */ + /* Line 103 */ + /* Line 104 */ + /* Line 105 */ + /* Line 106 */ + /* Line 107 */ + /* Line 108 */ + /* Line 109 */ + /* Line 110 */ + /* Line 111 */ + /* Line 112 */ + /* Line 113 */ + /* Line 114 */ + /* Line 115 */ + /* Line 116 */ + /* Line 117 */ + /* Line 118 */ + /* Line 119 */ + /* Line 120 */ + /* Line 121 */ + /* Line 122 */ + /* Line 123 */ + /* Line 124 */ + /* Line 125 */ + return 0; +} /* Last line. */ diff --git a/gdb/testsuite/gdb.base/source-search.exp b/gdb/testsuite/gdb.base/source-search.exp new file mode 100644 index 0000000..559c500 --- /dev/null +++ b/gdb/testsuite/gdb.base/source-search.exp @@ -0,0 +1,106 @@ +# Copyright 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/>. + +# Test 'forward-search' and 'reverse-search' commands. This test +# relies on some hard-coded line numbers relating to the source file. +# We could switch to using gdb_get_line_number, but it doesn't feel +# like that would add much value; just don't change the source file. + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { + return +} + +gdb_test "forward-search This testcase is part" \ + "1\\s+/\\* This testcase is part of GDB, the GNU debugger\\." \ + "search for first line of the file" + +gdb_test "forward-search This testcase is part" \ + "Expression not found" \ + "repeated search doesn't find the same first line" + +# The 'reverse-search' command starts searching from the line before +# the last line displayed. So in this case, the reverse search starts +# from line 0, i.e. nothing is searched. +gdb_test "reverse-search This testcase is part" \ + "Expression not found" \ + "reverse search doesn't find the first line either" + +# List some source lines, and then perform some forward-searches. The +# searches start from the first line after the last line displayed. +gdb_test "list 20" ".*" \ + "list source code ahead of a forward-search" +gdb_test "forward-search Line 2" \ + "25\\s+/\\* Line 25 \\*/" \ + "first forward-search after a list" +gdb_test "forward-search Line 2" \ + "26\\s+/\\* Line 26 \\*/" \ + "second forward-search after a list" +gdb_test "forward-search Line 2" \ + "27\\s+/\\* Line 27 \\*/" \ + "third forward-search after a list" + +# Now reverse-search from where we got too. +gdb_test "reverse-search Line 2" \ + "26\\s+/\\* Line 26 \\*/" \ + "first reverse-search for 'Line 2'" +gdb_test "reverse-search Line 2" \ + "25\\s+/\\* Line 25 \\*/" \ + "second reverse-search for 'Line 2'" +gdb_test "reverse-search Line 2" \ + "24\\s+/\\* Line 24 \\*/" \ + "third reverse-search for 'Line 2'" + +# List some source lines, and then perform a reverse-search. The +# search starts frm the first line before the last line displayed. +gdb_test "list 20" ".*" \ + "list source code ahead of a reverse-search" +gdb_test "reverse-search Line 2" \ + "23\\s+/\\* Line 23 \\*/" \ + "reverse-search after a list" + +# List the last lines of the file, then reverse search for the last +# line. As reverse-search starts on the line before the last line +# displayed, this will fail to find the last line. +gdb_test "list 127" +gdb_test "reverse-search Last line" \ + "Expression not found" \ + "reverse search for the last line fails" + +# List some lines from the middle of the file. Then try an invalid +# 'list' command. Finally, check searches pick up from the middle of +# the file where the first 'list' successfully completed. +foreach_with_prefix search_direction { forward reverse } { + foreach_with_prefix bad_list { out-of-range backwards } { + gdb_test "list 50" + + if { $bad_list eq "out-of-range" } { + gdb_test "list 1000" \ + "Line number 995 out of range; \[^\r\n\]+ has 127 lines\\." + } else { + gdb_test_no_output "list 60,50" + } + + if { $search_direction eq "forward" } { + set line 55 + } else { + set line 53 + } + + gdb_test "${search_direction}-search Line" \ + "$line\\s+/\\* Line $line \\*/" + } +} diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index a6c18d3..6b1b08e 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +require {!is_remote host} + load_lib gdb-python.exp # Test CLI output styling. diff --git a/gdb/testsuite/gdb.base/tls-common.exp.tcl b/gdb/testsuite/gdb.base/tls-common.exp.tcl index 7aa7f46..fc212a9 100644 --- a/gdb/testsuite/gdb.base/tls-common.exp.tcl +++ b/gdb/testsuite/gdb.base/tls-common.exp.tcl @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-dlobj-lib.c b/gdb/testsuite/gdb.base/tls-dlobj-lib.c index c69bab7..e82a064 100644 --- a/gdb/testsuite/gdb.base/tls-dlobj-lib.c +++ b/gdb/testsuite/gdb.base/tls-dlobj-lib.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-dlobj.c b/gdb/testsuite/gdb.base/tls-dlobj.c index 322bdda..a93f4a7 100644 --- a/gdb/testsuite/gdb.base/tls-dlobj.c +++ b/gdb/testsuite/gdb.base/tls-dlobj.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-dlobj.exp b/gdb/testsuite/gdb.base/tls-dlobj.exp index 02f2ff8..32e869e 100644 --- a/gdb/testsuite/gdb.base/tls-dlobj.exp +++ b/gdb/testsuite/gdb.base/tls-dlobj.exp @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj.c b/gdb/testsuite/gdb.base/tls-multiobj.c index 10e67da..dd4aadb 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj.c +++ b/gdb/testsuite/gdb.base/tls-multiobj.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj.exp b/gdb/testsuite/gdb.base/tls-multiobj.exp index 97acb33..2a52610 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj.exp +++ b/gdb/testsuite/gdb.base/tls-multiobj.exp @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj1.c b/gdb/testsuite/gdb.base/tls-multiobj1.c index 86e7222..6207173 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj1.c +++ b/gdb/testsuite/gdb.base/tls-multiobj1.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj2.c b/gdb/testsuite/gdb.base/tls-multiobj2.c index cea0709..9ff8b67 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj2.c +++ b/gdb/testsuite/gdb.base/tls-multiobj2.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj3.c b/gdb/testsuite/gdb.base/tls-multiobj3.c index bb0f239..3594eba0 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj3.c +++ b/gdb/testsuite/gdb.base/tls-multiobj3.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-nothreads.c b/gdb/testsuite/gdb.base/tls-nothreads.c index b3aaa33..cac20f8 100644 --- a/gdb/testsuite/gdb.base/tls-nothreads.c +++ b/gdb/testsuite/gdb.base/tls-nothreads.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-nothreads.exp b/gdb/testsuite/gdb.base/tls-nothreads.exp index 92a5cd9..105e686 100644 --- a/gdb/testsuite/gdb.base/tls-nothreads.exp +++ b/gdb/testsuite/gdb.base/tls-nothreads.exp @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/watchpoint-unaligned.c b/gdb/testsuite/gdb.base/watchpoint-unaligned.c index d3c1349..ca2fa45 100644 --- a/gdb/testsuite/gdb.base/watchpoint-unaligned.c +++ b/gdb/testsuite/gdb.base/watchpoint-unaligned.c @@ -18,6 +18,8 @@ #include <stdint.h> #include <assert.h> +static volatile int volatile_dummy; + static int again; static volatile struct @@ -53,6 +55,40 @@ write_size8twice (void) data.u.size8twice[offset] = first; data.u.size8twice[offset + 1] = second; #endif + + /* Setting a breakpoint on an instruction after an instruction triggering a + watchpoint makes it ambiguous which one will be reported. + Insert a dummy instruction in between to make sure the watchpoint gets + reported. */ + volatile_dummy = 1; + + return; /* write_size8twice_return */ +} + +static void +read_size8twice (void) +{ + static uint64_t volatile first; + static uint64_t volatile second; + +#ifdef __aarch64__ + volatile void *p = &data.u.size8twice[offset]; + asm volatile ("ldp %0, %1, [%2]" + : "=r" (first), "=r" (second) /* output */ + : "r" (p) /* input */ + : /* clobber */); +#else + first = data.u.size8twice[offset]; + second = data.u.size8twice[offset + 1]; +#endif + + /* Setting a breakpoint on an instruction after an instruction triggering a + watchpoint makes it ambiguous which one will be reported. + Insert a dummy instruction inbetween to make sure the watchpoint gets + reported. */ + volatile_dummy = 1; + + return; /* read_size8twice_return */ } int @@ -63,6 +99,7 @@ main (void) assert (sizeof (data) == 8 + 3 * 8); write_size8twice (); + read_size8twice (); while (size) { diff --git a/gdb/testsuite/gdb.base/watchpoint-unaligned.exp b/gdb/testsuite/gdb.base/watchpoint-unaligned.exp index 9220402..85b1eb7 100644 --- a/gdb/testsuite/gdb.base/watchpoint-unaligned.exp +++ b/gdb/testsuite/gdb.base/watchpoint-unaligned.exp @@ -151,6 +151,63 @@ foreach_with_prefix wpcount {4 7} { gdb_assert $got_hit $test } +proc size8twice { function cmd offset index } { + clean_restart $::testfile + + if { ![runto $function] } { + return -1 + } + + # Set offset in the inferior. + gdb_test_no_output "set var offset = $offset" + + # Set a breakpoint. + set bp_src_string "${function}_return" + set bp_loc [gdb_get_line_number $bp_src_string] + gdb_breakpoint $bp_loc \ + "Breakpoint $::decimal at $::hex" "$bp_src_string" + + # Set a hardware watchpoint. + set watch_index [expr $offset + $index] + set test "$cmd data.u.size8twice\[$watch_index\]" + set wpnum 0 + gdb_test_multiple $test "" { + -re -wrap "Hardware (read )?watchpoint ($::decimal): .*" { + set wpnum $expect_out(2,string) + pass $gdb_test_name + } + -re -wrap "Watchpoint ($::decimal): .*" { + if {[istarget "arm*-*-*"]} { + untested $gdb_test_name + } else { + fail $gdb_test_name + } + } + } + + if { ! $wpnum } { + # No hardware watchpoint, we're done. + return 0 + } + + # Try to trigger the hardware watchpoint. + set got_hit 0 + gdb_test_multiple "continue" "" { + -re -wrap "\r\nCould not insert hardware watchpoint .*" { + } + -re -wrap "Hardware (read )?watchpoint $wpnum:.*(New value|Value) = .*" { + set got_hit 1 + send_gdb "continue\n" + exp_continue + } + -re -wrap " $bp_src_string .*" { + } + } + gdb_assert { $got_hit } + + return $got_hit +} + # We've got an array with 3 8-byte elements. Do a store of 16 bytes, # to: # - elements 0 and 1 (offset == 0), and @@ -158,49 +215,21 @@ foreach_with_prefix wpcount {4 7} { # For each case, check setting a watchpoint at: # - the first written element (index == 0), and # - the second element (index == 1). -foreach_with_prefix offset { 0 1 } { - foreach_with_prefix index { 0 1 } { - - clean_restart $binfile - - if ![runto_main] { - return -1 - } - - gdb_test_no_output "set var offset = $offset" - gdb_breakpoint [gdb_get_line_number "final_return"] \ - "Breakpoint $decimal at $hex" "final_return" - set watch_index [expr $offset + $index] - set test "watch data.u.size8twice\[$watch_index\]" - set wpnum 0 - gdb_test_multiple $test $test { - -re "Hardware watchpoint (\[0-9\]+): .*\r\n$gdb_prompt $" { - set wpnum $expect_out(1,string) - pass $gdb_test_name - } - -re "Watchpoint (\[0-9\]+): .*\r\n$gdb_prompt $" { - if {[istarget "arm*-*-*"]} { - untested $gdb_test_name - } else { - fail $gdb_test_name - } +foreach_with_prefix fun { write_size8twice read_size8twice } { + if { $fun == "write_size8twice" } { + set cmd "watch" + } else { + set cmd "rwatch" + } + foreach_with_prefix offset { 0 1 } { + foreach_with_prefix index { 0 1 } { + set res [size8twice $fun $cmd $offset $index] + if { $res != 1 } { + break } } - if {$wpnum} { - set test "continue" - set got_hit 0 - gdb_test_multiple $test $test { - -re "\r\nCould not insert hardware watchpoint .*\r\n$gdb_prompt $" { - } - -re "Hardware watchpoint $wpnum:.*New value = .*\r\n$gdb_prompt $" { - set got_hit 1 - send_gdb "continue\n" - exp_continue - } - -re " final_return .*\r\n$gdb_prompt $" { - } - } - gdb_assert $got_hit "size8twice write" + if { $res != 1 } { + break } } } diff --git a/gdb/testsuite/gdb.cp/method-ref-return.cc b/gdb/testsuite/gdb.cp/method-ref-return.cc new file mode 100644 index 0000000..4169bfe --- /dev/null +++ b/gdb/testsuite/gdb.cp/method-ref-return.cc @@ -0,0 +1,42 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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/>. */ + +/* Test that we can access class method/data member via reference. */ + +struct foo +{ + foo () : m_a (42) {} + int get_a () const { return m_a; } + int m_a; +}; + +struct bar +{ + bar () : m_foo () {} + const foo &get_foo () const { return m_foo; } + foo m_foo; +}; + +int +main (int argc, char *argv[]) +{ + bar b; + const foo &ref = b.get_foo (); + int ret = ref.m_a; // breakpoint here + ret += ref.get_a (); + return ret; +}
\ No newline at end of file diff --git a/gdb/testsuite/gdb.cp/method-ref-return.exp b/gdb/testsuite/gdb.cp/method-ref-return.exp new file mode 100644 index 0000000..1ac5ac9 --- /dev/null +++ b/gdb/testsuite/gdb.cp/method-ref-return.exp @@ -0,0 +1,70 @@ +# Copyright 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/>. + +# Test calling methods and accessing members via reference. + +require allow_cplus_tests + +standard_testfile .cc + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +# Set a breakpoint after the bar object is created and the reference is obtained. +gdb_breakpoint [gdb_get_line_number "breakpoint here"] +gdb_continue_to_breakpoint "after reference assignment" + +# Test that we can call the method through reference and get the expected result. +gdb_test_multiple "print b.get_foo()" "print method call returning reference" { + -re "\\$\[0-9\]+ = \\(const foo &\\) @$hex: \\{m_a = 42\\}\r\n$gdb_prompt $" { + pass $gdb_test_name + } + -re "Could not validate memory tag: Value can't be converted to integer\\." { + fail "$gdb_test_name" + } +} + +# Test accessing the member through the reference. +gdb_test "print b.get_foo ().m_a" \ + "\\$\[0-9\]+ = 42" \ + "print member access through reference" + +# Test calling method on the referenced object. +gdb_test "print b.get_foo ().get_a()" \ + "\\$\[0-9\]+ = 42" \ + "print method call on referenced object" + +# Test that the stored reference works correctly. +gdb_test_multiple "print ref" "print stored reference" { + -re "\\$\[0-9\]+ = \\(const foo &\\) @$hex: \\{m_a = 42\\}\r\n$gdb_prompt $" { + pass $gdb_test_name + } + -re "Could not validate memory tag: Value can't be converted to integer\\." { + fail "$gdb_test_name" + } +} + +gdb_test "print ref.m_a" \ + "\\$\[0-9\]+ = 42" \ + "print member through stored reference" + +gdb_test "print ref.get_a()" \ + "\\$\[0-9\]+ = 42" \ + "print method call through stored reference"
\ No newline at end of file diff --git a/gdb/testsuite/gdb.dap/scopes.c b/gdb/testsuite/gdb.dap/scopes.c index d8929f1..2a1d76c 100644 --- a/gdb/testsuite/gdb.dap/scopes.c +++ b/gdb/testsuite/gdb.dap/scopes.c @@ -27,6 +27,8 @@ int main () static int scalar = 23; + void *ptr = (void *) &scalar; + { const char *inner = "inner block"; diff --git a/gdb/testsuite/gdb.dap/scopes.exp b/gdb/testsuite/gdb.dap/scopes.exp index 59d344b..52efa68 100644 --- a/gdb/testsuite/gdb.dap/scopes.exp +++ b/gdb/testsuite/gdb.dap/scopes.exp @@ -70,7 +70,8 @@ lassign $scopes scope reg_scope gdb_assert {[dict get $scope name] == "Locals"} "scope is locals" gdb_assert {[dict get $scope presentationHint] == "locals"} \ "locals presentation hint" -gdb_assert {[dict get $scope namedVariables] == 3} "three vars in scope" +set count [dict get $scope namedVariables] +gdb_assert {$count == 4} "four vars in scope" gdb_assert {[dict get $reg_scope name] == "Registers"} \ "second scope is registers" @@ -89,8 +90,8 @@ set refs1 [lindex [dap_check_request_and_response "fetch variables 0,1" \ set refs2 [lindex [dap_check_request_and_response "fetch variables 2" \ "variables" \ [format {o variablesReference [i %d] \ - start [i 2] count [i 1]} \ - $num]] \ + start [i 2] count [i %d]} \ + $num [expr {$count - 2}]]] \ 0] set vars [concat [dict get $refs1 body variables] \ @@ -115,6 +116,10 @@ foreach var $vars { "scalar" { gdb_assert {[dict get $var value] == 23} "check value of scalar" } + "ptr" { + gdb_assert {[dict get $var memoryReference] != ""} \ + "check memoryReference of ptr" + } default { fail "unknown variable $name" } @@ -128,11 +133,38 @@ set refs [lindex [dap_check_request_and_response "fetch contents of dei" \ set deivals [dict get $refs body variables] gdb_assert {[llength $deivals] == 2} "dei has two members" +# Request more children than exist. See PR dap/33228. +set seq [dap_send_request variables \ + [format {o variablesReference [i %d] count [i 100]} $dei_ref]] +lassign [dap_read_response variables $seq] response ignore +gdb_assert {[dict get $response success] == "false"} \ + "variables with invalid count" + set num [dict get $reg_scope variablesReference] -# The request succeeding is sufficient. -set val [dap_check_request_and_response "fetch first register" \ +lassign [dap_check_request_and_response "fetch all registers" \ "variables" \ - [format {o variablesReference [i %d] count [i 1]} $num]] + [format {o variablesReference [i %d] count [i %d]} $num\ + [dict get $reg_scope namedVariables]]] \ + val events + +# If any register has children, try to fetch those as well. This is a +# regression test for part of PR dap/33228. +foreach var [dict get $val body variables] { + set regvar [dict get $var variablesReference] + if {$regvar > 0} { + # If variablesReference is non-zero, then there must be either + # named or indexed children. + if {[dict exists $var namedVariables]} { + set n [dict get $var namedVariables] + } else { + set n [dict get $var indexedVariables] + } + + dap_check_request_and_response "fetch register children for $regvar" \ + "variables" \ + [format {o variablesReference [i %d] count [i %d]} $regvar $n] + } +} set num [dict get $scope variablesReference] set refs [lindex [dap_check_request_and_response "set variable scalar" \ diff --git a/gdb/testsuite/gdb.gdb/python-helper.exp b/gdb/testsuite/gdb.gdb/python-helper.exp index 8126740..7f91278 100644 --- a/gdb/testsuite/gdb.gdb/python-helper.exp +++ b/gdb/testsuite/gdb.gdb/python-helper.exp @@ -291,4 +291,4 @@ proc test_python_helper {} { } # Use the self-test framework to run the test. -do_self_tests captured_main test_python_helper +do_self_tests main test_python_helper diff --git a/gdb/testsuite/gdb.gdb/selftest.exp b/gdb/testsuite/gdb.gdb/selftest.exp index 1cf9265..3948fce 100644 --- a/gdb/testsuite/gdb.gdb/selftest.exp +++ b/gdb/testsuite/gdb.gdb/selftest.exp @@ -167,5 +167,5 @@ proc test_with_self { } { save_vars { INTERNAL_GDBFLAGS } { set INTERNAL_GDBFLAGS [string map {"-q" ""} $INTERNAL_GDBFLAGS] - do_self_tests captured_main test_with_self + do_self_tests main test_with_self } diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp index e35428a..0ee11e6 100644 --- a/gdb/testsuite/gdb.guile/scm-parameter.exp +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp @@ -565,47 +565,50 @@ rename scm_param_test_maybe_no_output "" # Test a color parameter. -with_ansi_styling_terminal { - # This enables 256 colors support and disables colors approximation. - setenv TERM xterm-256color - setenv COLORTERM truecolor - - # Start with a fresh gdb. - gdb_exit - gdb_start - gdb_reinitialize_dir $srcdir/$subdir - - gdb_install_guile_utils - gdb_install_guile_module - - # We use "." here instead of ":" so that this works on win32 too. - set escaped_directory [string_to_regexp "$srcdir/$subdir"] - - gdb_test_multiline "color gdb parameter" \ - "guile" "" \ - "(define test-color-param" "" \ - " (make-parameter \"print test-color-param\"" "" \ - " #:command-class COMMAND_DATA" "" \ - " #:parameter-type PARAM_COLOR" "" \ - " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \ - " #:show-doc \"Show the state of the test-color-param.\"" "" \ - " #:set-doc \"Set the state of the test-color-param.\"" "" \ - " #:show-func (lambda (self value)" "" \ - " (format #f \"The state of the test-color-param is ~a.\" value))" "" \ - " #:initial-value (make-color \"green\")))" "" \ - "(register-parameter! test-color-param)" "" \ - "end" - - with_test_prefix "test-color-param" { - with_test_prefix "initial-value" { - gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)" - gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value" - gdb_test_no_output "set print test-color-param 255" - } - with_test_prefix "new-value" { - gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value" - gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)" - gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter" +if { ![is_remote host] } { + with_ansi_styling_terminal { + + # This enables 256 colors support and disables colors approximation. + setenv TERM xterm-256color + setenv COLORTERM truecolor + + # Start with a fresh gdb. + gdb_exit + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + + gdb_install_guile_utils + gdb_install_guile_module + + # We use "." here instead of ":" so that this works on win32 too. + set escaped_directory [string_to_regexp "$srcdir/$subdir"] + + gdb_test_multiline "color gdb parameter" \ + "guile" "" \ + "(define test-color-param" "" \ + " (make-parameter \"print test-color-param\"" "" \ + " #:command-class COMMAND_DATA" "" \ + " #:parameter-type PARAM_COLOR" "" \ + " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \ + " #:show-doc \"Show the state of the test-color-param.\"" "" \ + " #:set-doc \"Set the state of the test-color-param.\"" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of the test-color-param is ~a.\" value))" "" \ + " #:initial-value (make-color \"green\")))" "" \ + "(register-parameter! test-color-param)" "" \ + "end" + + with_test_prefix "test-color-param" { + with_test_prefix "initial-value" { + gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)" + gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value" + gdb_test_no_output "set print test-color-param 255" + } + with_test_prefix "new-value" { + gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value" + gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)" + gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter" + } } } } diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index 214c570..e676925 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -45,6 +45,9 @@ proc_with_prefix test_directories { } { } proc_with_prefix test_data_directory { } { + # Proc assumes local host. + require {!is_remote host} + clean_restart # Check we can correctly read the data-directory parameter. First, @@ -187,6 +190,8 @@ proc_with_prefix test_enum_parameter { } { # Test an color parameter. proc_with_prefix test_color_parameter { } { + require {!is_remote host} + global env with_ansi_styling_terminal { # This enables 256 colors support and disables colors approximation. diff --git a/gdb/testsuite/gdb.tui/basic.exp b/gdb/testsuite/gdb.tui/basic.exp index 35c99bd..8ecc91a 100644 --- a/gdb/testsuite/gdb.tui/basic.exp +++ b/gdb/testsuite/gdb.tui/basic.exp @@ -112,5 +112,9 @@ set re_noattr "\[^<\]" set status_window_line 15 set status [Term::get_line_with_attrs $status_window_line] -gdb_assert { [regexp "^<reverse:1>$re_noattr*<reverse:0>$" $status] == 1} \ +verbose -log "status line: '$status'" + +# The status line uses standout, which may translate to different attributes +# depending on the terminal settings. Just check for at least one attribute. +gdb_assert { [regexp "^<.*>(exec|extended-r)" $status] == 1 } \ "status window: reverse" diff --git a/gdb/testsuite/gdb.tui/color-prompt.exp b/gdb/testsuite/gdb.tui/color-prompt.exp index a95b24a..af6e467 100644 --- a/gdb/testsuite/gdb.tui/color-prompt.exp +++ b/gdb/testsuite/gdb.tui/color-prompt.exp @@ -15,18 +15,16 @@ # Check using a prompt with color in TUI. +require allow_tui_tests + tuiterm_env Term::clean_restart 24 80 -# Set colored prompt. if {![Term::enter_tui]} { unsupported "TUI not supported" return } -Term::command "set prompt \\033\[31m(gdb) \\033\[0m" - -set line [Term::get_line_with_attrs $Term::_cur_row] -gdb_assert { [regexp "^<fg:red>$gdb_prompt <fg:default> *$" $line] } \ - "prompt with color" +set tui 1 +source $srcdir/$subdir/color-prompt.exp.tcl diff --git a/gdb/testsuite/gdb.tui/color-prompt.exp.tcl b/gdb/testsuite/gdb.tui/color-prompt.exp.tcl new file mode 100644 index 0000000..e6f4d3b --- /dev/null +++ b/gdb/testsuite/gdb.tui/color-prompt.exp.tcl @@ -0,0 +1,80 @@ +# Copyright 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 using a prompt with color in TUI ($tui == 0) or CLI ($tui == 0). + +set csi [string cat {\033} "\["] +set rl_prompt_start_ignore {\001} +set rl_prompt_end_ignore {\002} + +foreach_with_prefix rl_prompt_start_end_ignore { 0 1 } { + set color_on [string cat $csi 31m] + set color_off [string cat $csi 0m] + + if { $rl_prompt_start_end_ignore } { + set color_on \ + [string cat \ + $rl_prompt_start_ignore \ + $color_on \ + $rl_prompt_end_ignore] + set color_off \ + [string cat \ + $rl_prompt_start_ignore \ + $color_off \ + $rl_prompt_end_ignore] + } + + # Set prompt with color. + set prompt "${color_on}(gdb) $color_off" + Term::command "set prompt $prompt" + + # Check the color. + set line [Term::get_line_with_attrs $Term::_cur_row] + gdb_assert { [regexp "^<fg:red>$gdb_prompt <fg:default> *$" $line] } \ + "prompt with color" + + # Type a string. + set cmd "some long command" + send_gdb $cmd + Term::wait_for_line ^[string_to_regexp "(gdb) $cmd"] 23 + + # Send ^A, aka C-a, trigger beginning-of-line. + send_gdb "\001" + if { $tui || $rl_prompt_start_end_ignore } { + Term::wait_for_line ^[string_to_regexp "(gdb) $cmd"] 6 + } else { + # Without the markers, readline may get the cursor position wrong, so + # match less strict. + Term::wait_for_line ^[string_to_regexp "(gdb) $cmd"] + } + Term::dump_screen + + # Type something else to flush out the effect of the ^A. + set prefix "A" + send_gdb $prefix + if { $tui || $rl_prompt_start_end_ignore } { + Term::wait_for_line ^[string_to_regexp "(gdb) $prefix$cmd"] 7 + } else { + # Without the markers, readline may get the cursor position wrong, so + # match less strict. + Term::wait_for_line [string_to_regexp "$prefix"] + } + + # Abort command line editing, and regenerate prompt. + send_gdb "\003" + + # Reset prompt to default prompt. + Term::command "set prompt (gdb) " +} diff --git a/gdb/testsuite/gdb.tui/gdb.tcl b/gdb/testsuite/gdb.tui/gdb.tcl new file mode 100755 index 0000000..ca207ed --- /dev/null +++ b/gdb/testsuite/gdb.tui/gdb.tcl @@ -0,0 +1,20 @@ +#!/usr/bin/env tclsh + +# Copyright 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/>. + +puts "foo\033(%5" + +gets stdin diff --git a/gdb/testsuite/gdb.tui/main-2.exp b/gdb/testsuite/gdb.tui/main-2.exp index 71ad03b..14a7cb1 100644 --- a/gdb/testsuite/gdb.tui/main-2.exp +++ b/gdb/testsuite/gdb.tui/main-2.exp @@ -41,7 +41,7 @@ if {![Term::enter_tui]} { set line " return 0;" set nr [gdb_get_line_number $line] -set screen_line [Term::get_string_with_attrs 6 1 79] +set screen_line [Term::get_string_with_attrs 6 11 79] verbose -log "screen line 6: '$screen_line'" -gdb_assert { [regexp "$nr <reverse:1>$line<reverse:0>" $screen_line] } \ +gdb_assert { [regexp "<reverse:1>$line<reverse:0>" $screen_line] } \ "highlighted line in middle of source window" diff --git a/gdb/testsuite/gdb.tui/main.exp b/gdb/testsuite/gdb.tui/main.exp index d6960c7..e49da35 100644 --- a/gdb/testsuite/gdb.tui/main.exp +++ b/gdb/testsuite/gdb.tui/main.exp @@ -43,7 +43,10 @@ if {![Term::enter_tui]} { } send_gdb "file [standard_output_file $testfile]\n" -gdb_assert { [Term::wait_for "Reading symbols from"] } "file command" +# Matching the output is difficult because it may or may not wrap. Simply +# match the resulting prompt. +gdb_assert { [Term::wait_for ""] } "file command" + Term::check_contents "show main after file" \ [string_to_regexp "|___[format %06d $nr]_$line"] diff --git a/gdb/testsuite/gdb.tui/new-layout.exp b/gdb/testsuite/gdb.tui/new-layout.exp index f517997..914e10c 100644 --- a/gdb/testsuite/gdb.tui/new-layout.exp +++ b/gdb/testsuite/gdb.tui/new-layout.exp @@ -150,4 +150,7 @@ Term::check_box "before cmd_only: src box in src layout" 0 0 80 15 Term::command "layout cmd_only" Term::command "layout src" + +# Flush out and check the resulting src box. +Term::command "print 1" Term::check_box "after cmd_only: src box in src layout" 0 0 80 15 diff --git a/gdb/testsuite/gdb.tui/pr30056.exp b/gdb/testsuite/gdb.tui/pr30056.exp index 3403033..dd3b25c 100644 --- a/gdb/testsuite/gdb.tui/pr30056.exp +++ b/gdb/testsuite/gdb.tui/pr30056.exp @@ -72,9 +72,8 @@ save_vars { env(LC_ALL) } { # Send ^C to clear the command line. send_gdb "\003" } else { - # Sending ^C currently doesn't abort the i-search. PR cli/30498 is - # open about this. - kfail cli/30498 $test + # Sending ^C currently doesn't abort the i-search. + fail $test # At this point we don't have a responsive prompt. Send ^G to abort # the i-search. diff --git a/gdb/testsuite/gdb.tui/source-search.c b/gdb/testsuite/gdb.tui/source-search.c new file mode 100644 index 0000000..2320c5c --- /dev/null +++ b/gdb/testsuite/gdb.tui/source-search.c @@ -0,0 +1,127 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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/>. */ + +int +main (void) +{ + /* Line 21 */ + /* Line 22 */ + /* Line 23 */ + /* Line 24 */ + /* Line 25 */ + /* Line 26 */ + /* Line 27 */ + /* Line 28 */ + /* Line 29 */ + /* Line 30 */ + /* Line 31 */ + /* Line 32 */ + /* Line 33 */ + /* Line 34 */ + /* Line 35 */ + /* Line 36 */ + /* Line 37 */ + /* Line 38 */ + /* Line 39 */ + /* Line 40 */ + /* Line 41 */ + /* Line 42 */ + /* Line 43 */ + /* Line 44 */ + /* Line 45 */ + /* Line 46 */ + /* Line 47 */ + /* Line 48 */ + /* Line 49 */ + /* Line 50 */ + /* Line 51 */ + /* Line 52 */ + /* Line 53 */ + /* Line 54 */ + /* Line 55 */ + /* Line 56 */ + /* Line 57 */ + /* Line 58 */ + /* Line 59 */ + /* Line 60 */ + /* Line 61 */ + /* Line 62 */ + /* Line 63 */ + /* Line 64 */ + /* Line 65 */ + /* Line 66 */ + /* Line 67 */ + /* Line 68 */ + /* Line 69 */ + /* Line 70 */ + /* Line 71 */ + /* Line 72 */ + /* Line 73 */ + /* Line 74 */ + /* Line 75 */ + /* Line 76 */ + /* Line 77 */ + /* Line 78 */ + /* Line 79 */ + /* Line 80 */ + /* Line 81 */ + /* Line 82 */ + /* Line 83 */ + /* Line 84 */ + /* Line 85 */ + /* Line 86 */ + /* Line 87 */ + /* Line 88 */ + /* Line 89 */ + /* Line 90 */ + /* Line 91 */ + /* Line 92 */ + /* Line 93 */ + /* Line 94 */ + /* Line 95 */ + /* Line 96 */ + /* Line 97 */ + /* Line 98 */ + /* Line 99 */ + /* Line 100 */ + /* Line 101 */ + /* Line 102 */ + /* Line 103 */ + /* Line 104 */ + /* Line 105 */ + /* Line 106 */ + /* Line 107 */ + /* Line 108 */ + /* Line 109 */ + /* Line 110 */ + /* Line 111 */ + /* Line 112 */ + /* Line 113 */ + /* Line 114 */ + /* Line 115 */ + /* Line 116 */ + /* Line 117 */ + /* Line 118 */ + /* Line 119 */ + /* Line 120 */ + /* Line 121 */ + /* Line 122 */ + /* Line 123 */ + /* Line 124 */ + /* Line 125 */ + return 0; +} /* Last line. */ diff --git a/gdb/testsuite/gdb.tui/source-search.exp b/gdb/testsuite/gdb.tui/source-search.exp new file mode 100644 index 0000000..6865db4 --- /dev/null +++ b/gdb/testsuite/gdb.tui/source-search.exp @@ -0,0 +1,71 @@ +# Copyright 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/>. + +# Test forward-search and reverse-search within the TUI src window. + +tuiterm_env + +standard_testfile .c + +if {[build_executable "failed to build" ${testfile} ${srcfile}] == -1} { + return +} + +Term::clean_restart 24 80 $testfile +if {![Term::enter_tui]} { + unsupported "TUI not supported" + return +} + +proc check_src_window { testname first_line } { + set last_line [expr $first_line + 12] + Term::check_box_contents $testname 0 0 80 15 \ + "^\\s+${first_line}\\s+.*\\s+${last_line}\\s+/\\* Line ${last_line} \\*/\\s+$" + +} + +set first_line 15 + +# Check that the window contents are as expected. +check_src_window "initial src contents" $first_line + +# Search forward. Searches are from the last line displayed, so this +# will move the next source line onto the screen each time. +for { set i 1 } { $i < 4 } { incr i } { + incr first_line + Term::command "forward-search Line" + check_src_window "src windows after forward-search $i" $first_line +} + +# Reverse search. Like forward-search, but move backward through the +# source. +for { set i 1 } { $i < 3 } { incr i } { + incr first_line -1 + Term::command "reverse-search Line" + check_src_window "src windows after reverse-search $i" $first_line +} + +# Until there are no matching lines left. +Term::command "reverse-search Line" +gdb_assert {[regexp -- "^Expression not found\\s+$" [Term::get_line 22]]} \ + "check start of source was reached" + +Term::command "forward-search Last line" +Term::check_box_contents "forward-search to end of file" 0 0 80 15 \ + "^\\s+122\\s+.*/\\* Last line\\. \\*/\\s+$" + +Term::command "reverse-search This testcase is part" +Term::check_box_contents "reverse-search to start of file" 0 0 80 15 \ + "^\\s+1\\s+.*\\s+13\\s+GNU General Public License for more details\\.\\s+$" diff --git a/gdb/testsuite/gdb.tui/tui-disasm-styling.exp b/gdb/testsuite/gdb.tui/tui-disasm-styling.exp index 513d787..43b8ecf 100644 --- a/gdb/testsuite/gdb.tui/tui-disasm-styling.exp +++ b/gdb/testsuite/gdb.tui/tui-disasm-styling.exp @@ -43,6 +43,9 @@ if {![Term::enter_tui]} { return } +# Proc enter_tui switches off styling, re-enable it. +Term::command "set style enabled on" + Term::command "layout asm" Term::check_box "asm box" 0 0 80 15 diff --git a/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp b/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp index 51623e8..47482a2 100644 --- a/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp +++ b/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp @@ -20,9 +20,13 @@ tuiterm_env standard_testfile tui-layout-asm-short-prog.S -if {[build_executable "failed to prepare" ${testfile} ${srcfile} \ - {debug additional_flags=-nostdlib \ - additional_flags=-nostartfiles}] == -1} { +set opts {} +lappend opts debug +lappend opts additional_flags=-static +lappend opts additional_flags=-nostdlib +lappend opts additional_flags=-nostartfiles + +if { [build_executable "failed to prepare" $testfile $srcfile $opts] == -1 } { return -1 } diff --git a/gdb/testsuite/gdb.tui/tui-mode-switch.exp b/gdb/testsuite/gdb.tui/tui-mode-switch.exp new file mode 100644 index 0000000..c605962 --- /dev/null +++ b/gdb/testsuite/gdb.tui/tui-mode-switch.exp @@ -0,0 +1,57 @@ +require allow_tui_tests + +tuiterm_env + +if { [ishost *-*-*bsd*] } { + # We need support for alternate screen, and xterm doesn't have it. + set term xterm-clear +} else { + set term xterm +} + +Term::with_term $term { + Term::clean_restart 12 40 +} + +if {![Term::prepare_for_tui]} { + unsupported "TUI not supported" + return 0 +} + +# Generate prompt. +Term::gen_prompt + +# Move to last line. +for { set i 1 } { $i <= 11 } { incr i } { + send_gdb "\n" + Term::wait_for "" +} + +# Type "foo". +send_gdb "foo" +set line { 0 11 40 1 } +gdb_assert { [Term::wait_for_region_contents {*}$line "^$gdb_prompt foo"] } \ + "type foo" + +# Enter TUI. +send_gdb "\030\001" +gdb_assert { [Term::wait_for ""] } "enter TUI" + +# Exit TUI. +send_gdb "\030\001" +gdb_assert { [Term::wait_for ""] } "exit TUI" + +# Type b. +send_gdb "b" +gdb_assert { [Term::wait_for_region_contents {*}$line "^$gdb_prompt b"] } \ + "type b" + +# Check that we don't see "boo". +gdb_assert { ![Term::check_region_contents_p {*}$line "^$gdb_prompt boo"] } \ + "no boo" +Term::dump_screen + +# We need an empty prompt here, to deal with the "monitor exit" that +# native-extended-gdbserver will send. Send a backspace. +send_gdb "\010" +Term::wait_for "" diff --git a/gdb/testsuite/gdb.tui/tuiterm-2.exp b/gdb/testsuite/gdb.tui/tuiterm-2.exp index 5992271..a451834 100644 --- a/gdb/testsuite/gdb.tui/tuiterm-2.exp +++ b/gdb/testsuite/gdb.tui/tuiterm-2.exp @@ -106,6 +106,76 @@ with_override Term::accept_gdb_output test_accept_gdb_output { } gdb_assert { [Term::wait_for ""] } } + + with_test_prefix "Term::wait_for 2" { + Term::_setup 4 20 + set send_cnt 0 + set expect_send {} + set action_cnt 0 + set actions { + { + Term::_move_cursor 0 0 + + Term::_insert "${::border}(gdb) " + set pos $Term::_cur_col + + Term::_insert "foo" + + Term::_move_cursor 19 0 + Term::_insert "$::border" + + Term::_move_cursor $pos 0 + } + { + Term::_move_cursor 0 1 + + Term::_insert "${::border}(gdb) " + set pos $Term::_cur_col + + Term::_move_cursor 19 1 + Term::_insert "$::border" + + Term::_move_cursor $pos 1 + } + } + + # Wait for a prompt. + gdb_assert { [Term::wait_for ""] } + + # The first action sets the cursor after the prompt on the + # first line. The second action sets the cursor after the + # prompt on the second line. Check that wait_for returns + # after the second action, not the first. + gdb_assert { $Term::_cur_row == 1 } + } } } } + +with_test_prefix "Unrecognized escape sequence" { + spawn $srcdir/$subdir/gdb.tcl + switch_gdb_spawn_id $spawn_id + + Term::_setup 4 20 + + save_vars timeout { + set timeout 1 + + set line { 0 0 20 1 } + + # Parse "foo". + gdb_assert { [Term::wait_for_region_contents \ + {*}$line \ + [string_to_regexp "foo"]] } \ + "foo" + + # Parse "\033(%5". + gdb_assert { ![Term::accept_gdb_output 0] } \ + "fail to parse escape sequence" + gdb_assert { [Term::wait_for_region_contents \ + {*}$line \ + [string_to_regexp "^\[(%5"]] } \ + "echoed escape sequence" + } + Term::dump_screen +} diff --git a/gdb/testsuite/gdb.tui/tuiterm.exp b/gdb/testsuite/gdb.tui/tuiterm.exp index 6cd65f3..ed9478a 100644 --- a/gdb/testsuite/gdb.tui/tuiterm.exp +++ b/gdb/testsuite/gdb.tui/tuiterm.exp @@ -653,13 +653,21 @@ proc test_cursor_backward_tabulation { } { } proc test_repeat { } { - Term::_move_cursor 2 1 - set Term::_last_char X + Term::_move_cursor 0 1 + + Term::_insert "xxX" + gdb_assert { $Term::_last_char == "X" } + check "insert" { + "abcdefgh" + "xxXlmnop" + "qrstuvwx" + "yz01234 " + } 3 1 - Term::_csi_b 3 + Term::_csi_b 2 check "repeat" { "abcdefgh" - "ijXXXnop" + "xxXXXnop" "qrstuvwx" "yz01234 " } 5 1 diff --git a/gdb/testsuite/lib/selftest-support.exp b/gdb/testsuite/lib/selftest-support.exp index e037664..97be023 100644 --- a/gdb/testsuite/lib/selftest-support.exp +++ b/gdb/testsuite/lib/selftest-support.exp @@ -72,21 +72,39 @@ proc selftest_setup { executable function } { # run yourself set description "run until breakpoint at $function" + set re_hs {[^\r\n]+} + set re_args [string cat \ + [string_to_regexp "("] \ + $re_hs \ + [string_to_regexp ")"]] + set re_pass \ + [multi_line \ + "Starting program: $re_hs" \ + ".*" \ + [string cat "Breakpoint $::decimal, $function $re_args at" \ + " ${re_hs}gdb.c:$re_hs"] \ + ".*"] + set re_xfail \ + [multi_line \ + "Starting program: $re_hs" \ + ".*" \ + "Breakpoint $::decimal, $function $re_args$re_hs" \ + ".*"] gdb_test_multiple "run $INTERNAL_GDBFLAGS" "$description" { - -re "Starting program.*Breakpoint \[0-9\]+,.*$function \\(.*\\).* at .*main.c:.*$gdb_prompt $" { - pass "$description" - } - -re "Starting program.*Breakpoint \[0-9\]+,.*$function \\(.*\\).*$gdb_prompt $" { - xfail "$description (line numbers scrambled?)" - } - -re "vfork: No more processes.*$gdb_prompt $" { - fail "$description (out of virtual memory)" - return -1 - } - -re ".*$gdb_prompt $" { - fail "$description" - return -1 - } + -re -wrap $re_pass { + pass $description + } + -re -wrap $re_xfail { + xfail "$description (line numbers scrambled?)" + } + -re -wrap "vfork: No more processes.*" { + fail "$description (out of virtual memory)" + return -1 + } + -re -wrap "" { + fail $description + return -1 + } } return 0 diff --git a/gdb/testsuite/lib/tuiterm.exp b/gdb/testsuite/lib/tuiterm.exp index b83b8af..e1af223 100644 --- a/gdb/testsuite/lib/tuiterm.exp +++ b/gdb/testsuite/lib/tuiterm.exp @@ -33,1331 +33,1690 @@ namespace eval Term { variable _resize_count - proc _log { what } { - verbose "+++ $what" - } + variable _TERM + set _TERM "" - # Call BODY, then log WHAT along with the original and new cursor position. - proc _log_cur { what body } { - variable _cur_row - variable _cur_col + variable _alternate + variable _alternate_setup + set _alternate 0 + set _alternate_setup 0 +} - set orig_cur_row $_cur_row - set orig_cur_col $_cur_col +proc Term::_log { what } { + verbose "+++ $what" +} - set code [catch {uplevel $body} result] +# Call BODY, then log WHAT along with the original and new cursor position. +proc Term::_log_cur { what body } { + variable _cur_row + variable _cur_col - _log "$what, cursor: ($orig_cur_row, $orig_cur_col) -> ($_cur_row, $_cur_col)" + set orig_cur_row $_cur_row + set orig_cur_col $_cur_col - if { $code == 1 } { - global errorInfo errorCode - return -code $code -errorinfo $errorInfo -errorcode $errorCode $result - } else { - return -code $code $result - } - } + set code [catch {uplevel $body} result] - # If ARG is empty, return DEF: otherwise ARG. This is useful for - # defaulting arguments in CSIs. - proc _default {arg def} { - if {$arg == ""} { - return $def - } - return $arg + _log "$what, cursor: ($orig_cur_row, $orig_cur_col) -> ($_cur_row, $_cur_col)" + + if { $code == 1 } { + global errorInfo errorCode + return -code $code -errorinfo $errorInfo -errorcode $errorCode $result + } else { + return -code $code $result } +} - # Erase in the line Y from SX to just before EX. - proc _clear_in_line {sx ex y} { - variable _attrs - variable _chars - set lattr [array get _attrs] - while {$sx < $ex} { - set _chars($sx,$y) [list " " $lattr] - incr sx - } +# If ARG is empty, return DEF: otherwise ARG. This is useful for +# defaulting arguments in CSIs. +proc Term::_default {arg def} { + if {$arg == ""} { + return $def } + return $arg +} - # Erase the lines from SY to just before EY. - proc _clear_lines {sy ey} { - variable _cols - while {$sy < $ey} { - _clear_in_line 0 $_cols $sy - incr sy - } +# Erase in the line Y from SX to just before EX. +proc Term::_clear_in_line {sx ex y} { + variable _attrs + variable _chars + set lattr [array get _attrs] + while {$sx < $ex} { + set _chars($sx,$y) [list " " $lattr] + incr sx } +} - # Beep. - proc _ctl_0x07 {} { +# Erase the lines from SY to just before EY. +proc Term::_clear_lines {sy ey} { + variable _cols + while {$sy < $ey} { + _clear_in_line 0 $_cols $sy + incr sy } +} + +# Beep. +proc Term::_ctl_0x07 {} { +} + +# Return 1 if tuiterm has the bw/auto_left_margin enabled. +proc Term::_have_bw {} { + return [expr \ + [string equal $Term::_TERM "ansiw"] \ + || [string equal $Term::_TERM "ansis"]] +} - # Return 1 if tuiterm has the bw/auto_left_margin enabled. - proc _have_bw {} { - return [string equal $Term::_TERM "ansiw"] +# Backspace. +proc Term::_ctl_0x08 { {bw -1} } { + if { $bw == -1 } { + set bw [_have_bw] } + _log_cur "Backspace, bw == $bw" { + variable _cur_col + variable _cur_row + variable _cols - # Backspace. - proc _ctl_0x08 { {bw -1} } { - if { $bw == -1 } { - set bw [_have_bw] + if { $_cur_col > 0 } { + # No wrapping needed. + incr _cur_col -1 + return } - _log_cur "Backspace, bw == $bw" { - variable _cur_col - variable _cur_row - variable _cols - if { $_cur_col > 0 } { - # No wrapping needed. - incr _cur_col -1 - return - } + if { ! $bw } { + # Wrapping not enabled. + return + } - if { ! $bw } { - # Wrapping not enabled. - return - } + if { $_cur_row == 0 } { + # Can't wrap. + return + } + + # Wrap to previous line. + set _cur_col [expr $_cols - 1] + incr _cur_row -1 + } +} + +# Linefeed. +proc Term::_ctl_0x0a {} { + _log_cur "Line feed" { + variable _cur_row + variable _rows + variable _cols + variable _chars - if { $_cur_row == 0 } { - # Can't wrap. - return + incr _cur_row 1 + while {$_cur_row >= $_rows} { + # Scroll the display contents. We scroll one line at + # a time here; as _cur_row was only increased by one, + # a single line scroll should be enough to put the + # cursor back on the screen. But we wrap the + # scrolling inside a while loop just to be on the safe + # side. + for {set y 0} {$y < [expr $_rows - 1]} {incr y} { + set next_y [expr $y + 1] + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$y) $_chars($x,$next_y) + } } - # Wrap to previous line. - set _cur_col [expr $_cols - 1] incr _cur_row -1 } } +} - # Linefeed. - proc _ctl_0x0a {} { - _log_cur "Line feed" { - variable _cur_row - variable _rows - variable _cols - variable _chars - - incr _cur_row 1 - while {$_cur_row >= $_rows} { - # Scroll the display contents. We scroll one line at - # a time here; as _cur_row was only increased by one, - # a single line scroll should be enough to put the - # cursor back on the screen. But we wrap the - # scrolling inside a while loop just to be on the safe - # side. - for {set y 0} {$y < [expr $_rows - 1]} {incr y} { - set next_y [expr $y + 1] - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$y) $_chars($x,$next_y) - } - } +# Carriage return. +proc Term::_ctl_0x0d {} { + _log_cur "Carriage return" { + variable _cur_col - incr _cur_row -1 - } - } + set _cur_col 0 } +} - # Carriage return. - proc _ctl_0x0d {} { - _log_cur "Carriage return" { - variable _cur_col +# Designate G0 Character Set, USASCII (ESC ( B) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html (see "ESC ( C", case C = B) +proc Term::_esc_0x28_B {} { + _log "ignored: G0: USASCII" +} - set _cur_col 0 - } - } +# Designate G0 Character Set, DEC Special Character and Line Drawing Set (ESC ( 0) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html (see "ESC ( C", case C = 0) +proc Term::_esc_0x28_0 {} { + _log "ignored: G0: DEC Special Character and Line Drawing Set" +} + +# DECKPAM (Application Keypad, ESC =) +# +# https://vt100.net/docs/vt510-rm/DECKPAM.html +proc Term::_esc_0x3d {} { + _log "ignored: Application Keypad" +} - # Insert Character. - # - # https://vt100.net/docs/vt510-rm/ICH.html - proc _csi_@ {args} { - set n [_default [lindex $args 0] 1] +# DECKPNM (Normal Keypad, ESC >) +# +# https://vt100.net/docs/vt510-rm/DECKPNM.html +proc Term::_esc_0x3e {} { + _log "ignored: Normal Keypad" +} - _log_cur "Insert Character ($n)" { - variable _cur_col - variable _cur_row - variable _cols - variable _chars +# Insert Character. +# +# https://vt100.net/docs/vt510-rm/ICH.html +proc Term::_csi_@ {args} { + set n [_default [lindex $args 0] 1] - # Move characters right of the cursor right by N positions, - # starting with the rightmost one. - for {set in_col [expr $_cols - $n - 1]} {$in_col >= $_cur_col} {incr in_col -1} { - set out_col [expr $in_col + $n] - set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) - } + _log_cur "Insert Character ($n)" { + variable _cur_col + variable _cur_row + variable _cols + variable _chars - # Write N blank spaces starting from the cursor. - _clear_in_line $_cur_col [expr $_cur_col + $n] $_cur_row + # Move characters right of the cursor right by N positions, + # starting with the rightmost one. + for {set in_col [expr $_cols - $n - 1]} {$in_col >= $_cur_col} {incr in_col -1} { + set out_col [expr $in_col + $n] + set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) } - } - # Horizontal Position Absolute. - # - # https://vt100.net/docs/vt510-rm/HPA.html - proc _csi_` {args} { - # Same as Cursor Horizontal Absolute. - return [Term::_csi_G {*}$args] + # Write N blank spaces starting from the cursor. + _clear_in_line $_cur_col [expr $_cur_col + $n] $_cur_row } +} + +# Horizontal Position Absolute. +# +# https://vt100.net/docs/vt510-rm/HPA.html +proc Term::_csi_` {args} { + # Same as Cursor Horizontal Absolute. + return [Term::_csi_G {*}$args] +} - # Cursor Up. - # - # https://vt100.net/docs/vt510-rm/CUU.html - proc _csi_A {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Up. +# +# https://vt100.net/docs/vt510-rm/CUU.html +proc Term::_csi_A {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Up ($arg)" { - variable _cur_row + _log_cur "Cursor Up ($arg)" { + variable _cur_row - set _cur_row [expr {max ($_cur_row - $arg, 0)}] - } + set _cur_row [expr {max ($_cur_row - $arg, 0)}] } +} - # Cursor Down. - # - # https://vt100.net/docs/vt510-rm/CUD.html - proc _csi_B {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Down. +# +# https://vt100.net/docs/vt510-rm/CUD.html +proc Term::_csi_B {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Down ($arg)" { - variable _cur_row - variable _rows + _log_cur "Cursor Down ($arg)" { + variable _cur_row + variable _rows - set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] - } + set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] } +} - # Cursor Forward. - # - # https://vt100.net/docs/vt510-rm/CUF.html - proc _csi_C {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Forward. +# +# https://vt100.net/docs/vt510-rm/CUF.html +proc Term::_csi_C {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Forward ($arg)" { - variable _cur_col - variable _cols + _log_cur "Cursor Forward ($arg)" { + variable _cur_col + variable _cols - set _cur_col [expr {min ($_cur_col + $arg, $_cols - 1)}] - } + set _cur_col [expr {min ($_cur_col + $arg, $_cols - 1)}] } +} - # Cursor Backward. - # - # https://vt100.net/docs/vt510-rm/CUB.html - proc _csi_D {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Backward. +# +# https://vt100.net/docs/vt510-rm/CUB.html +proc Term::_csi_D {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Backward ($arg)" { - variable _cur_col + _log_cur "Cursor Backward ($arg)" { + variable _cur_col - set _cur_col [expr {max ($_cur_col - $arg, 0)}] - } + set _cur_col [expr {max ($_cur_col - $arg, 0)}] } +} - # Cursor Next Line. - # - # https://vt100.net/docs/vt510-rm/CNL.html - proc _csi_E {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Next Line. +# +# https://vt100.net/docs/vt510-rm/CNL.html +proc Term::_csi_E {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Next Line ($arg)" { - variable _cur_col - variable _cur_row - variable _rows + _log_cur "Cursor Next Line ($arg)" { + variable _cur_col + variable _cur_row + variable _rows - set _cur_col 0 - set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] - } + set _cur_col 0 + set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] } +} - # Cursor Previous Line. - # - # https://vt100.net/docs/vt510-rm/CPL.html - proc _csi_F {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Previous Line. +# +# https://vt100.net/docs/vt510-rm/CPL.html +proc Term::_csi_F {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Previous Line ($arg)" { - variable _cur_col - variable _cur_row - variable _rows + _log_cur "Cursor Previous Line ($arg)" { + variable _cur_col + variable _cur_row + variable _rows - set _cur_col 0 - set _cur_row [expr {max ($_cur_row - $arg, 0)}] - } + set _cur_col 0 + set _cur_row [expr {max ($_cur_row - $arg, 0)}] } +} - # Cursor Horizontal Absolute. - # - # https://vt100.net/docs/vt510-rm/CHA.html - proc _csi_G {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Horizontal Absolute. +# +# https://vt100.net/docs/vt510-rm/CHA.html +proc Term::_csi_G {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Horizontal Absolute ($arg)" { - variable _cur_col - variable _cols + _log_cur "Cursor Horizontal Absolute ($arg)" { + variable _cur_col + variable _cols - set _cur_col [expr {min ($arg, $_cols)} - 1] - } + set _cur_col [expr {min ($arg, $_cols)} - 1] } +} - # Cursor Position. - # - # https://vt100.net/docs/vt510-rm/CUP.html - proc _csi_H {args} { - set row [_default [lindex $args 0] 1] - set col [_default [lindex $args 1] 1] +# Cursor Position. +# +# https://vt100.net/docs/vt510-rm/CUP.html +proc Term::_csi_H {args} { + set row [_default [lindex $args 0] 1] + set col [_default [lindex $args 1] 1] - _log_cur "Cursor Position ($row, $col)" { - variable _cur_col - variable _cur_row + _log_cur "Cursor Position ($row, $col)" { + variable _cur_col + variable _cur_row - set _cur_row [expr {$row - 1}] - set _cur_col [expr {$col - 1}] - } + set _cur_row [expr {$row - 1}] + set _cur_col [expr {$col - 1}] } +} - # Cursor Horizontal Forward Tabulation. - # - # https://vt100.net/docs/vt510-rm/CHT.html - proc _csi_I {args} { - set n [_default [lindex $args 0] 1] +# Cursor Horizontal Forward Tabulation. +# +# https://vt100.net/docs/vt510-rm/CHT.html +proc Term::_csi_I {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Cursor Horizontal Forward Tabulation ($n)" { - variable _cur_col - variable _cols + _log_cur "Cursor Horizontal Forward Tabulation ($n)" { + variable _cur_col + variable _cols - incr _cur_col [expr {$n * 8 - $_cur_col % 8}] - if {$_cur_col >= $_cols} { - set _cur_col [expr {$_cols - 1}] - } + incr _cur_col [expr {$n * 8 - $_cur_col % 8}] + if {$_cur_col >= $_cols} { + set _cur_col [expr {$_cols - 1}] } } +} - # Erase in Display. - # - # https://vt100.net/docs/vt510-rm/ED.html - proc _csi_J {args} { - set arg [_default [lindex $args 0] 0] - - _log_cur "Erase in Display ($arg)" { - variable _cur_col - variable _cur_row - variable _rows - variable _cols - - if {$arg == 0} { - # Cursor (inclusive) to end of display. - _clear_in_line $_cur_col $_cols $_cur_row - _clear_lines [expr {$_cur_row + 1}] $_rows - } elseif {$arg == 1} { - # Beginning of display to cursor (inclusive). - _clear_lines 0 $_cur_row - _clear_in_line 0 [expr $_cur_col + 1] $_cur_row - } elseif {$arg == 2} { - # Entire display. - _clear_lines 0 $_rows - } +# Erase in Display. +# +# https://vt100.net/docs/vt510-rm/ED.html +proc Term::_csi_J {args} { + set arg [_default [lindex $args 0] 0] + + _log_cur "Erase in Display ($arg)" { + variable _cur_col + variable _cur_row + variable _rows + variable _cols + + if {$arg == 0} { + # Cursor (inclusive) to end of display. + _clear_in_line $_cur_col $_cols $_cur_row + _clear_lines [expr {$_cur_row + 1}] $_rows + } elseif {$arg == 1} { + # Beginning of display to cursor (inclusive). + _clear_lines 0 $_cur_row + _clear_in_line 0 [expr $_cur_col + 1] $_cur_row + } elseif {$arg == 2} { + # Entire display. + _clear_lines 0 $_rows } } +} - # Erase in Line. - # - # https://vt100.net/docs/vt510-rm/EL.html - proc _csi_K {args} { - set arg [_default [lindex $args 0] 0] +# Erase in Line. +# +# https://vt100.net/docs/vt510-rm/EL.html +proc Term::_csi_K {args} { + set arg [_default [lindex $args 0] 0] - _log_cur "Erase in Line ($arg)" { - variable _cur_col - variable _cur_row - variable _cols + _log_cur "Erase in Line ($arg)" { + variable _cur_col + variable _cur_row + variable _cols - if {$arg == 0} { - # Cursor (inclusive) to end of line. - _clear_in_line $_cur_col $_cols $_cur_row - } elseif {$arg == 1} { - # Beginning of line to cursor (inclusive). - _clear_in_line 0 [expr $_cur_col + 1] $_cur_row - } elseif {$arg == 2} { - # Entire line. - _clear_in_line 0 $_cols $_cur_row - } + if {$arg == 0} { + # Cursor (inclusive) to end of line. + _clear_in_line $_cur_col $_cols $_cur_row + } elseif {$arg == 1} { + # Beginning of line to cursor (inclusive). + _clear_in_line 0 [expr $_cur_col + 1] $_cur_row + } elseif {$arg == 2} { + # Entire line. + _clear_in_line 0 $_cols $_cur_row } } +} - # Insert Line - # - # https://vt100.net/docs/vt510-rm/IL.html - proc _csi_L {args} { - set arg [_default [lindex $args 0] 1] +# Insert Line +# +# https://vt100.net/docs/vt510-rm/IL.html +proc Term::_csi_L {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Insert Line ($arg)" { - variable _cur_col - variable _cur_row - variable _rows - variable _cols - variable _chars + _log_cur "Insert Line ($arg)" { + variable _cur_col + variable _cur_row + variable _rows + variable _cols + variable _chars - set y [expr $_rows - 2] - set next_y [expr $y + $arg] - while {$y >= $_cur_row} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$next_y) $_chars($x,$y) - } - incr y -1 - incr next_y -1 + set y [expr $_rows - 2] + set next_y [expr $y + $arg] + while {$y >= $_cur_row} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$next_y) $_chars($x,$y) } - - _clear_lines $_cur_row [expr $_cur_row + $arg] + incr y -1 + incr next_y -1 } + + _clear_lines $_cur_row [expr $_cur_row + $arg] } +} - # Delete line. - # - # https://vt100.net/docs/vt510-rm/DL.html - proc _csi_M {args} { - set count [_default [lindex $args 0] 1] +# Delete line. +# +# https://vt100.net/docs/vt510-rm/DL.html +proc Term::_csi_M {args} { + set count [_default [lindex $args 0] 1] - _log_cur "Delete line ($count)" { - variable _cur_row - variable _rows - variable _cols - variable _chars + _log_cur "Delete line ($count)" { + variable _cur_row + variable _rows + variable _cols + variable _chars - set y $_cur_row - set next_y [expr {$y + $count}] - while {$next_y < $_rows} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$y) $_chars($x,$next_y) - } - incr y - incr next_y + set y $_cur_row + set next_y [expr {$y + $count}] + while {$next_y < $_rows} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$y) $_chars($x,$next_y) } - _clear_lines $y $_rows + incr y + incr next_y } + _clear_lines $y $_rows } +} - # Delete Character. - # - # https://vt100.net/docs/vt510-rm/DCH.html - proc _csi_P {args} { - set count [_default [lindex $args 0] 1] - - _log_cur "Delete character ($count)" { - variable _cur_row - variable _cur_col - variable _chars - variable _cols +# Delete Character. +# +# https://vt100.net/docs/vt510-rm/DCH.html +proc Term::_csi_P {args} { + set count [_default [lindex $args 0] 1] - # Move all characters right of the cursor N positions left. - set out_col [expr $_cur_col] - set in_col [expr $_cur_col + $count] + _log_cur "Delete character ($count)" { + variable _cur_row + variable _cur_col + variable _chars + variable _cols - while {$in_col < $_cols} { - set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) - incr in_col - incr out_col - } + # Move all characters right of the cursor N positions left. + set out_col [expr $_cur_col] + set in_col [expr $_cur_col + $count] - # Clear the rest of the line. - _clear_in_line $out_col $_cols $_cur_row + while {$in_col < $_cols} { + set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) + incr in_col + incr out_col } + + # Clear the rest of the line. + _clear_in_line $out_col $_cols $_cur_row } +} - # Pan Down - # - # https://vt100.net/docs/vt510-rm/SU.html - proc _csi_S {args} { - set count [_default [lindex $args 0] 1] +# Pan Down +# +# https://vt100.net/docs/vt510-rm/SU.html +proc Term::_csi_S {args} { + set count [_default [lindex $args 0] 1] - _log_cur "Pan Down ($count)" { - variable _cur_col - variable _cur_row - variable _cols - variable _rows - variable _chars + _log_cur "Pan Down ($count)" { + variable _cur_col + variable _cur_row + variable _cols + variable _rows + variable _chars - # The following code is written without consideration for - # the scroll margins. At this time this comment was - # written the tuiterm library doesn't support the scroll - # margins. If/when that changes, then the following will - # need to be updated. + # The following code is written without consideration for + # the scroll margins. At this time this comment was + # written the tuiterm library doesn't support the scroll + # margins. If/when that changes, then the following will + # need to be updated. - set dy 0 - set y $count + set dy 0 + set y $count - while {$y < $_rows} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$dy) $_chars($x,$y) - } - incr y 1 - incr dy 1 + while {$y < $_rows} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$dy) $_chars($x,$y) } - - _clear_lines $dy $_rows + incr y 1 + incr dy 1 } + + _clear_lines $dy $_rows } +} - # Pan Up - # - # https://vt100.net/docs/vt510-rm/SD.html - proc _csi_T {args} { - set count [_default [lindex $args 0] 1] +# Pan Up +# +# https://vt100.net/docs/vt510-rm/SD.html +proc Term::_csi_T {args} { + set count [_default [lindex $args 0] 1] - _log_cur "Pan Up ($count)" { - variable _cur_col - variable _cur_row - variable _cols - variable _rows - variable _chars + _log_cur "Pan Up ($count)" { + variable _cur_col + variable _cur_row + variable _cols + variable _rows + variable _chars - # The following code is written without consideration for - # the scroll margins. At this time this comment was - # written the tuiterm library doesn't support the scroll - # margins. If/when that changes, then the following will - # need to be updated. + # The following code is written without consideration for + # the scroll margins. At this time this comment was + # written the tuiterm library doesn't support the scroll + # margins. If/when that changes, then the following will + # need to be updated. - set y [expr $_rows - $count] - set dy $_rows + set y [expr $_rows - $count] + set dy $_rows - while {$dy >= $count} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$dy) $_chars($x,$y) - } - incr y -1 - incr dy -1 + while {$dy >= $count} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$dy) $_chars($x,$y) } - - _clear_lines 0 $count + incr y -1 + incr dy -1 } + + _clear_lines 0 $count } +} - # Erase chars. - # - # https://vt100.net/docs/vt510-rm/ECH.html - proc _csi_X {args} { - set n [_default [lindex $args 0] 1] +# Erase chars. +# +# https://vt100.net/docs/vt510-rm/ECH.html +proc Term::_csi_X {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Erase chars ($n)" { - # Erase characters but don't move cursor. - variable _cur_col - variable _cur_row - variable _attrs - variable _chars + _log_cur "Erase chars ($n)" { + # Erase characters but don't move cursor. + variable _cur_col + variable _cur_row + variable _attrs + variable _chars - set lattr [array get _attrs] - set x $_cur_col - for {set i 0} {$i < $n} {incr i} { - set _chars($x,$_cur_row) [list " " $lattr] - incr x - } + set lattr [array get _attrs] + set x $_cur_col + for {set i 0} {$i < $n} {incr i} { + set _chars($x,$_cur_row) [list " " $lattr] + incr x } } +} - # Cursor Backward Tabulation. - # - # https://vt100.net/docs/vt510-rm/CBT.html - proc _csi_Z {args} { - set n [_default [lindex $args 0] 1] +# Cursor Backward Tabulation. +# +# https://vt100.net/docs/vt510-rm/CBT.html +proc Term::_csi_Z {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Cursor Backward Tabulation ($n)" { - variable _cur_col + _log_cur "Cursor Backward Tabulation ($n)" { + variable _cur_col - set _cur_col [expr {max (int (($_cur_col - 1) / 8) * 8 - ($n - 1) * 8, 0)}] - } + set _cur_col [expr {max (int (($_cur_col - 1) / 8) * 8 - ($n - 1) * 8, 0)}] } +} - # Repeat. - # - # https://www.xfree86.org/current/ctlseqs.html (See `(REP)`) - proc _csi_b {args} { - set n [_default [lindex $args 0] 1] +# Repeat. +# +# https://www.xfree86.org/current/ctlseqs.html (See `(REP)`) +proc Term::_csi_b {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Repeat ($n)" { - variable _last_char + _log_cur "Repeat ($n)" { + variable _last_char - _insert [string repeat $_last_char $n] - } + _insert [string repeat $_last_char $n] } +} - # Vertical Line Position Absolute. - # - # https://vt100.net/docs/vt510-rm/VPA.html - proc _csi_d {args} { - set row [_default [lindex $args 0] 1] +# Vertical Line Position Absolute. +# +# https://vt100.net/docs/vt510-rm/VPA.html +proc Term::_csi_d {args} { + set row [_default [lindex $args 0] 1] - _log_cur "Vertical Line Position Absolute ($row)" { - variable _cur_row - variable _rows + _log_cur "Vertical Line Position Absolute ($row)" { + variable _cur_row + variable _rows - set _cur_row [expr min ($row - 1, $_rows - 1)] - } + set _cur_row [expr min ($row - 1, $_rows - 1)] } +} - # Reset the attributes in attributes array UPVAR_NAME to the default values. - proc _reset_attrs { upvar_name } { - upvar $upvar_name var - array set var { - intensity normal - fg default - bg default - underline 0 - reverse 0 - invisible 0 - blinking 0 +# Set Mode (SM, CSI h) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_h { args } { + foreach item $args { + switch -exact -- $item { + 4 { + # Insert Mode (IRM) + _log "ignored: insert mode" + } + default { + error unsupported + } } } +} - # Translate the color numbers as used in proc _csi_m to a name. - proc _color_attr { n } { - switch -exact -- $n { - 0 { - return black +# Reset Mode (RM, CSI l) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_l { args } { + foreach item $args { + switch -exact -- $item { + 4 { + # Replace Mode (IRM) + _log "ignored: replace mode" + } + default { + error unsupported } + } + } +} + +# Set Scrolling Region (DECSTBM, CSI Ps ; Ps r) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_r { top bottom } { + _log "ignored: set scrolling region" +} + +# Window manipulation (XTWINOPS, CSI Ps ; Ps ; Ps t) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_t { arg1 arg2 arg3 } { + if { $arg1 == 22 && $arg2 == 0 && $arg3 == 0 } { + _log "ignored: Save xterm icon and window title on stack" + return + } + + if { $arg1 == 23 && $arg2 == 0 && $arg3 == 0 } { + _log "ignored: Restore xterm icon and window title from stack" + return + } + + error unsupported +} + +# DECSET (CSI ? h) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +proc Term::_csi_0x3f_h { args } { + foreach item $args { + switch -exact -- $item { 1 { - return red + _log "ignored: Application Cursor Keys" } - 2 { - return green + 7 { + _log "ignored: autowrap mode" } - 3 { - return yellow + 1000 { + _log "ignored: Send Mouse X & Y on button press and release" } - 4 { - return blue + 1006 { + _log "ignored: Enable SGR Mouse Mode" } - 5 { - return magenta + 1049 { + _log "switch to alternate screen" + _set_alternate 1 } - 6 { - return cyan + default { + error unsupported + } + } + } +} + +# DECRST (CSI ? l) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +proc Term::_csi_0x3f_l { args } { + foreach item $args { + switch -exact -- $item { + 1 { + _log "ignored: Normal Cursor Keys" } 7 { - return white + _log "ignored: no autowrap mode" + } + 1000 { + _log "ignored: Don't send Mouse X & Y on button press and release" + } + 1006 { + _log "ignored: Disable SGR Mouse Mode" + } + 1049 { + _log "switch from alternate screen" + _set_alternate 0 + } + default { + error "unsupported" } - default { error "unsupported color number: $n" } } } +} - # Select Graphic Rendition. - # - # https://vt100.net/docs/vt510-rm/SGR.html - proc _csi_m {args} { - if { [llength $args] == 0 } { - # Apply default. - set args [list 0] +# Reset the attributes in attributes array UPVAR_NAME to the default values. +proc Term::_reset_attrs { upvar_name } { + upvar $upvar_name var + array set var { + intensity normal + fg default + bg default + underline 0 + reverse 0 + invisible 0 + blinking 0 + } +} + +# Translate the color numbers as used in proc _csi_m to a name. +proc Term::_color_attr { n } { + switch -exact -- $n { + 0 { + return black + } + 1 { + return red + } + 2 { + return green } + 3 { + return yellow + } + 4 { + return blue + } + 5 { + return magenta + } + 6 { + return cyan + } + 7 { + return white + } + default { error "unsupported color number: $n" } + } +} - _log_cur "Select Graphic Rendition ([join $args {, }])" { - variable _attrs +# Select Graphic Rendition. +# +# https://vt100.net/docs/vt510-rm/SGR.html +proc Term::_csi_m {args} { + if { [llength $args] == 0 } { + # Apply default. + set args [list 0] + } - foreach item $args { - switch -exact -- $item { - "" - 0 { - _reset_attrs _attrs - } - 1 { - set _attrs(intensity) bold - } - 2 { - set _attrs(intensity) dim - } - 4 { - set _attrs(underline) 1 - } - 5 { - set _attrs(blinking) 1 - } - 7 { - set _attrs(reverse) 1 - } - 8 { - set _attrs(invisible) 1 - } - 22 { - set _attrs(intensity) normal - } - 24 { - set _attrs(underline) 0 - } - 25 { - set _attrs(blinking) 0 - } - 27 { - set _attrs(reverse) 0 - } - 28 { - set _attrs(invisible) 0 - } - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 { - set _attrs(fg) [_color_attr [expr $item - 30]] - } - 39 { - set _attrs(fg) default - } - 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 { - set _attrs(bg) [_color_attr [expr $item - 40]] - } - 49 { - set _attrs(bg) default - } + _log_cur "Select Graphic Rendition ([join $args {, }])" { + variable _attrs + + foreach item $args { + switch -exact -- $item { + "" - 0 { + _reset_attrs _attrs + } + 1 { + set _attrs(intensity) bold + } + 2 { + set _attrs(intensity) dim + } + 4 { + set _attrs(underline) 1 + } + 5 { + set _attrs(blinking) 1 + } + 7 { + set _attrs(reverse) 1 + } + 8 { + set _attrs(invisible) 1 + } + 22 { + set _attrs(intensity) normal + } + 24 { + set _attrs(underline) 0 + } + 25 { + set _attrs(blinking) 0 + } + 27 { + set _attrs(reverse) 0 + } + 28 { + set _attrs(invisible) 0 + } + 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 { + set _attrs(fg) [_color_attr [expr $item - 30]] + } + 39 { + set _attrs(fg) default + } + 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 { + set _attrs(bg) [_color_attr [expr $item - 40]] + } + 49 { + set _attrs(bg) default } } } } +} + +# Request Terminal Parameters (DECREQTPARM) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +# https://vt100.net/docs/vt100-ug/chapter3.html +proc Term::_csi_x {} { + # Ignore. +} + +# Insert string at the cursor location. +proc Term::_insert {str} { + _log_cur "Inserted string '$str'" { + _log "Inserting string '$str'" - # Insert string at the cursor location. - proc _insert {str} { - _log_cur "Inserted string '$str'" { - _log "Inserting string '$str'" - - variable _cur_col - variable _cur_row - variable _rows - variable _cols - variable _attrs - variable _chars - set lattr [array get _attrs] - foreach char [split $str {}] { - _log_cur " Inserted char '$char'" { - set _chars($_cur_col,$_cur_row) [list $char $lattr] - incr _cur_col - if {$_cur_col >= $_cols} { - set _cur_col 0 - incr _cur_row - if {$_cur_row >= $_rows} { - error "FIXME scroll" - } + variable _cur_col + variable _cur_row + variable _rows + variable _cols + variable _attrs + variable _chars + set lattr [array get _attrs] + foreach char [split $str {}] { + _log_cur " Inserted char '$char'" { + set _chars($_cur_col,$_cur_row) [list $char $lattr] + incr _cur_col + if {$_cur_col >= $_cols} { + set _cur_col 0 + incr _cur_row + if {$_cur_row >= $_rows} { + error "FIXME scroll" } } } } + + variable _last_char + set _last_char [string index $str end] } +} - # Move the cursor to the (0-based) COL and ROW positions. - proc _move_cursor { col row } { - variable _cols - variable _rows - variable _cur_col - variable _cur_row +# Move the cursor to the (0-based) COL and ROW positions. +proc Term::_move_cursor { col row } { + variable _cols + variable _rows + variable _cur_col + variable _cur_row - if { $col < 0 || $col >= $_cols } { - error "_move_cursor: invalid col value: $col" - } + if { $col < 0 || $col >= $_cols } { + error "_move_cursor: invalid col value: $col" + } - if { $row < 0 || $row >= $_rows } { - error "_move_cursor: invalid row value: $row" - } + if { $row < 0 || $row >= $_rows } { + error "_move_cursor: invalid row value: $row" + } - set _cur_col $col - set _cur_row $row - } + set _cur_col $col + set _cur_row $row +} - # Initialize. - proc _setup {rows cols} { - global stty_init - set stty_init "rows $rows columns $cols" +# Enable or disable alternate screen. +proc Term::_set_alternate { enable } { + variable _alternate + if { $enable == $_alternate } { + return + } + set _alternate $enable - variable _rows - variable _cols - variable _cur_col - variable _cur_row - variable _attrs - variable _resize_count + variable _attrs + variable _chars + variable _cur_col + variable _cur_row - set _rows $rows - set _cols $cols - set _cur_col 0 - set _cur_row 0 - set _resize_count 0 - _reset_attrs _attrs - - _clear_lines 0 $_rows - } - - # Accept some output from gdb and update the screen. - # Return 1 if successful, or 0 if a timeout occurred. - proc accept_gdb_output { } { - global expect_out - gdb_expect { - -re "^\[\x07\x08\x0a\x0d\]" { - scan $expect_out(0,string) %c val - set hexval [format "%02x" $val] - _log "wait_for: _ctl_0x${hexval}" - _ctl_0x${hexval} - } - -re "^\x1b(\[0-9a-zA-Z\])" { - _log "wait_for: unsupported escape" - error "unsupported escape" - } - -re "^\x1b\\\[(\[0-9;\]*)(\[a-zA-Z@`\])" { - set cmd $expect_out(2,string) - set params [split $expect_out(1,string) ";"] - _log "wait_for: _csi_$cmd <<<$expect_out(1,string)>>>" - eval _csi_$cmd $params - } - -re "^\[^\x07\x08\x0a\x0d\x1b\]+" { - _insert $expect_out(0,string) - variable _last_char - set _last_char [string index $expect_out(0,string) end] - } + variable _save_attrs + variable _save_chars + variable _save_cur_col + variable _save_cur_row - timeout { - # Assume a timeout means we somehow missed the - # expected result, and carry on. - warning "timeout in accept_gdb_output" - dump_screen - return 0 - } - } + variable _alternate_setup - return 1 + if { $_alternate_setup } { + set tmp $_save_chars + } + set _save_chars [array get _chars] + if { $_alternate_setup } { + array set _chars $tmp } - # Print arg using "verbose -log" if DEBUG_TUI_MATCHING == 1. - proc debug_tui_matching { arg } { - set debug 0 - if { [info exists ::DEBUG_TUI_MATCHING] } { - set debug $::DEBUG_TUI_MATCHING - } + if { $_alternate_setup } { + set tmp $_save_attrs + } + set _save_attrs [array get _attrs] + if { $_alternate_setup } { + array set _attrs $tmp + } - if { ! $debug } { - return - } + if { $_alternate_setup } { + set tmp $_save_cur_col + } + set _save_cur_col $_cur_col + if { $_alternate_setup } { + set _cur_col $tmp + } - verbose -log "$arg" + if { $_alternate_setup } { + set tmp $_save_cur_row + } + set _save_cur_row $_cur_row + if { $_alternate_setup } { + set _cur_row $tmp } - # Accept some output from gdb and update the screen. WAIT_FOR is - # a regexp matching the line to wait for. Return 0 on timeout, 1 - # on success. - proc wait_for {wait_for} { - global gdb_prompt - variable _cur_col - variable _cur_row + if { ! $_alternate_setup } { + variable _rows + variable _cols + _setup $_rows $_cols + set _alternate_setup 1 + } +} - set fn "wait_for" +# Initialize. +proc Term::_setup {rows cols} { + global stty_init + set stty_init "rows $rows columns $cols" - set prompt_wait_for "(^|\\|)$gdb_prompt \$" - if { $wait_for == "" } { - set wait_for $prompt_wait_for - } + variable _rows + variable _cols + variable _cur_col + variable _cur_row + variable _attrs + variable _resize_count - debug_tui_matching "$fn: regexp: '$wait_for'" + set _rows $rows + set _cols $cols + set _cur_col 0 + set _cur_row 0 + set _resize_count 0 + _reset_attrs _attrs - while 1 { - if { [accept_gdb_output] == 0 } { - return 0 - } + _clear_lines 0 $_rows +} - # If the cursor appears just after the prompt, return. It - # isn't reliable to check this only after an insertion, - # because curses may make "unusual" redrawing decisions. - if {$wait_for == "$prompt_wait_for"} { - set prev [get_line $_cur_row $_cur_col] - } else { - set prev [get_line $_cur_row] - } - if {[regexp -- $wait_for $prev]} { - debug_tui_matching "$fn: match: '$prev'" - if {$wait_for == "$prompt_wait_for"} { - break - } - set wait_for $prompt_wait_for - debug_tui_matching "$fn: regexp prompt: '$wait_for'" - } else { - debug_tui_matching "$fn: mismatch: '$prev'" - } +# Accept some output from gdb and update the screen. +# Return 1 if successful, or 0 if a timeout occurred. +proc Term::accept_gdb_output { {warn 1} } { + global expect_out + + set ctls "\x07\x08\x0a\x0d" + set esc "\x1b" + set re_ctls "\[$ctls\]" + set re_others "\[^$esc$ctls\]" + set have_esc 0 + gdb_expect { + -re ^$re_ctls { + scan $expect_out(0,string) %c val + set hexval [format "%02x" $val] + _log "wait_for: _ctl_0x${hexval}" + _ctl_0x${hexval} + } + -re "^$esc" { + _log "wait_for: ESC" + set have_esc 1 + } + -re "^$re_others+" { + _insert $expect_out(0,string) + } + + timeout { + # Assume a timeout means we somehow missed the + # expected result, and carry on. + warning "timeout in accept_gdb_output" + dump_screen + return 0 } + } + if { !$have_esc } { return 1 } - # Accept some output from gdb and update the screen. Wait for the screen - # region X/Y/WIDTH/HEIGTH to matches REGEXP. Return 0 on timeout, 1 on - # success. - proc wait_for_region_contents {x y width height regexp} { - while 1 { - if { [accept_gdb_output] == 0 } { - return 0 - } + set re_csi [string_to_regexp "\["] + set have_csi 0 + gdb_expect { + -re "^(\[0-9a-zA-Z\])" { + _log "wait_for: unsupported escape" + error "unsupported escape" + } + -re "^(\[\\(\])(\[a-zA-Z\])" { + scan $expect_out(1,string) %c val + set hexval [format "%02x" $val] + set cmd $expect_out(2,string) + eval _esc_0x${hexval}_$cmd + } + -re "^(\[=>\])" { + scan $expect_out(1,string) %c val + set hexval [format "%02x" $val] + _esc_0x$hexval + } + -re "^$re_csi" { + _log "wait_for: CSI" + set have_csi 1 + } - if { [check_region_contents_p $x $y $width $height $regexp] } { - break + timeout { + # Assume a timeout means we somehow missed the + # expected result, and carry on. + if { $warn } { + warning "timeout in accept_gdb_output following ESC" + dump_screen } + _insert "^\[" + return 0 } + } + if { !$have_csi } { return 1 } - # Setup the terminal with dimensions ROWSxCOLS, TERM=ansi, and execute - # BODY. - proc with_tuiterm {rows cols body} { - global env stty_init - variable _TERM - save_vars {env(TERM) env(NO_COLOR) stty_init} { - if { [ishost *-*-*bsd*] } { - setenv TERM ansiw - } else { - setenv TERM ansi + set re_csi_prefix {[?]} + set re_csi_args {[0-9;]} + set re_csi_cmd {[a-zA-Z@`]} + gdb_expect { + -re "^($re_csi_cmd)" { + set cmd $expect_out(1,string) + _log "wait_for: _csi_$cmd" + eval _csi_$cmd + } + -re "^($re_csi_args*)($re_csi_cmd)" { + set params [split $expect_out(1,string) ";"] + set cmd $expect_out(2,string) + _log "wait_for: _csi_$cmd <<<$params>>>" + eval _csi_$cmd $params + } + -re "^($re_csi_prefix?)($re_csi_args*)($re_csi_cmd)" { + set prefix $expect_out(1,string) + set params [split $expect_out(2,string) ";"] + set cmd $expect_out(3,string) + scan $prefix %c val + set hexval [format "%02x" $val] + _log "wait_for: _csi_0x${hexval}_$cmd <<<$expect_out(1,string)>>>" + eval _csi_0x${hexval}_$cmd $params + } + + timeout { + # Assume a timeout means we somehow missed the + # expected result, and carry on. + if { $warn } { + warning "timeout in accept_gdb_output following CSI" + dump_screen } - # Save active TERM variable. - set Term::_TERM $env(TERM) + _insert "^\[\[" + return 0 + } + } - setenv NO_COLOR "" - _setup $rows $cols + return 1 +} - uplevel $body - } +# Print arg using "verbose -log" if DEBUG_TUI_MATCHING == 1. +proc Term::debug_tui_matching { arg } { + set debug 0 + if { [info exists ::DEBUG_TUI_MATCHING] } { + set debug $::DEBUG_TUI_MATCHING } - # Like ::clean_restart, but ensures that gdb starts in an - # environment where the TUI can work. ROWS and COLS are the size - # of the terminal. EXECUTABLE, if given, is passed to - # clean_restart. - proc clean_restart {rows cols {executable {}}} { - with_tuiterm $rows $cols { - save_vars { ::GDBFLAGS } { - # Make GDB not print the directory names. Use this setting to - # remove the differences in test runs due to varying directory - # names. - append ::GDBFLAGS " -ex \"set filename-display basename\"" + if { ! $debug } { + return + } - if {$executable == ""} { - ::clean_restart - } else { - ::clean_restart $executable - } - } + verbose -log "$arg" +} - ::gdb_test_no_output "set pagination off" - } +# Accept some output from gdb and update the screen. WAIT_FOR is +# a regexp matching the line to wait for. Return 0 on timeout, 1 +# on success. +proc Term::wait_for {wait_for} { + global gdb_prompt + variable _cur_col + variable _cur_row + + set fn "wait_for" + + set prompt_wait_for "(^|\\|)$gdb_prompt \$" + if { $wait_for == "" } { + set wait_for $prompt_wait_for } - # Generate prompt on TUIterm. - proc gen_prompt {} { - # Generate a prompt. - send_gdb "echo\n" + debug_tui_matching "$fn: regexp: '$wait_for'" + + while 1 { + if { [accept_gdb_output] == 0 } { + return 0 + } + + # If the cursor appears just after the prompt, return. It + # isn't reliable to check this only after an insertion, + # because curses may make "unusual" redrawing decisions. + if {$wait_for == "$prompt_wait_for"} { + set prev [get_line $_cur_row $_cur_col] + } else { + set prev [get_line $_cur_row] + } + + if { ![regexp -- $wait_for $prev] } { + debug_tui_matching "$fn: mismatch: '$prev'" + continue + } - # Drain the output before the prompt. - gdb_expect { - -re "echo\r\n" { + if {$wait_for == "$prompt_wait_for"} { + # We've detected that the cursor is just after the prompt. + # Now check that there's nothing else on the line. + set prev [get_line $_cur_row] + if { ![regexp -- "(^|\\|)$gdb_prompt +($|\\||\\+)" $prev] } { + debug_tui_matching "$fn: mismatch: '$prev'" + continue } } - # Interpret prompt using TUIterm. - wait_for "" - } + debug_tui_matching "$fn: match: '$prev'" - # Setup ready for starting the tui, but don't actually start it. - # Returns 1 on success, 0 if TUI tests should be skipped. - proc prepare_for_tui {} { - if { [is_remote host] } { - # In clean_restart, we're using "setenv TERM ansi", which has - # effect on build. If we have [is_remote host] == 0, so - # build == host, then it also has effect on host. But for - # [is_remote host] == 1, it has no effect on host. - return 0 + if {$wait_for == "$prompt_wait_for"} { + # Matched the prompt, we're done. + break } - if {![allow_tui_tests]} { + # Now try to match the prompt. + set wait_for $prompt_wait_for + debug_tui_matching "$fn: regexp prompt: '$wait_for'" + } + + return 1 +} + +# Accept some output from gdb and update the screen. Wait for the screen +# region X/Y/WIDTH/HEIGTH to matches REGEXP. Return 0 on timeout, 1 on +# success. +proc Term::wait_for_region_contents {x y width height regexp} { + while 1 { + if { [accept_gdb_output] == 0 } { return 0 } - gdb_test_no_output "set tui border-kind ascii" - gdb_test_no_output "maint set tui-resize-message on" - return 1 + if { [check_region_contents_p $x $y $width $height $regexp] } { + break + } } - # Start the TUI. Returns 1 on success, 0 if TUI tests should be - # skipped. - proc enter_tui {} { - if {![prepare_for_tui]} { + return 1 +} + +# Accept some output from gdb and update the screen. Wait for the current +# screen line to match REGEXP and cursor position POS, unless POS is empty. +# Return 0 on timeout, 1 on success. +proc Term::wait_for_line { regexp {pos ""} } { + variable _cur_row + variable _cur_col + variable _cols + + while 1 { + if { [accept_gdb_output] == 0 } { return 0 } - command_no_prompt_prefix "tui enable" - return 1 - } + if { ![check_region_contents_p 0 $_cur_row $_cols 1 $regexp] } { + continue + } - # Send the command CMD to gdb, then wait for a gdb prompt to be - # seen in the TUI. CMD should not end with a newline -- that will - # be supplied by this function. - proc command {cmd} { - global gdb_prompt - send_gdb "$cmd\n" - set str [string_to_regexp $cmd] - set str "(^|\\|)$gdb_prompt $str" - wait_for $str - } - - # As proc command, but don't wait for an initial prompt. This is used for - # initial terminal commands, where there's no prompt yet. - proc command_no_prompt_prefix {cmd} { - gen_prompt - command $cmd - } - - # Apply the attribute list in ATTRS to attributes array UPVAR_NAME. - # Return a string annotating the changed attributes. - proc apply_attrs { upvar_name attrs } { - set res "" - upvar $upvar_name var - foreach { attr val } $attrs { - if { $var($attr) != $val } { - append res "<$attr:$val>" - set var($attr) $val - } + if { $pos == "" || $_cur_col == $pos } { + break } + } + + return 1 +} + +# In BODY, when using Term::with_tuiterm, use TERM instead of the default. - return $res +proc Term::with_term { term body } { + save_vars { Term::_TERM } { + set Term::_TERM $term + uplevel $body } +} - # Return the text of screen line N. Lines are 0-based. Start at column - # X. If C is non-empty, stop before column C. Columns are also - # zero-based. If ATTRS, annotate with attributes. - proc get_string {n x c {attrs 0}} { - variable _rows - # This can happen during resizing, if the cursor seems to - # temporarily be off-screen. - if {$n >= $_rows} { - return "" +# Setup the terminal with dimensions ROWSxCOLS, TERM=ansi, and execute +# BODY. +proc Term::with_tuiterm {rows cols body} { + global env stty_init + variable _TERM + save_vars {env(TERM) env(NO_COLOR) stty_init} { + if { $Term::_TERM != "" } { + setenv TERM $Term::_TERM + } elseif { [ishost *-*-*bsd*] } { + setenv TERM ansiw + } else { + setenv TERM ansi } + # Save active TERM variable. + set Term::_TERM $env(TERM) - set result "" - variable _cols - variable _chars - set c [_default $c $_cols] - if { $attrs } { - _reset_attrs line_attrs - } - while {$x < $c} { - if { $attrs } { - set char_attrs [lindex $_chars($x,$n) 1] - append result [apply_attrs line_attrs $char_attrs] + setenv NO_COLOR "" + _setup $rows $cols + + uplevel $body + } +} + +# Like ::clean_restart, but ensures that gdb starts in an +# environment where the TUI can work. ROWS and COLS are the size +# of the terminal. EXECUTABLE, if given, is passed to +# clean_restart. +proc Term::clean_restart {rows cols {executable {}}} { + with_tuiterm $rows $cols { + save_vars { ::GDBFLAGS } { + # Make GDB not print the directory names. Use this setting to + # remove the differences in test runs due to varying directory + # names. + append ::GDBFLAGS " -ex \"set filename-display basename\"" + + if {$executable == ""} { + ::clean_restart + } else { + ::clean_restart $executable } - append result [lindex $_chars($x,$n) 0] - incr x } - if { $attrs } { - _reset_attrs zero_attrs - set char_attrs [array get zero_attrs] - append result [apply_attrs line_attrs $char_attrs] - } - return $result - } - # Return the text of screen line N. Lines are 0-based. Start at column - # X. If C is non-empty, stop before column C. Columns are also - # zero-based. Annotate with attributes. - proc get_string_with_attrs { n x c } { - return [get_string $n $x $c 1] + ::gdb_test_no_output "set pagination off" } +} - # Return the text of screen line N. Lines are 0-based. If C is - # non-empty, stop before column C. Columns are also zero-based. If - # ATTRS, annotate with attributes. - proc get_line_1 {n c attrs} { - return [get_string $n 0 $c $attrs] +# Generate prompt on TUIterm. +proc Term::gen_prompt {} { + # Generate a prompt. + send_gdb "echo\n" + + # Drain the output before the prompt. + gdb_expect { + -re "echo\r\n" { + } } - # Return the text of screen line N, without attributes. Lines are - # 0-based. If C is given, stop before column C. Columns are also - # zero-based. - proc get_line {n {c ""} } { - return [get_line_1 $n $c 0] + # Interpret prompt using TUIterm. + wait_for "" +} + +# Setup ready for starting the tui, but don't actually start it. +# Returns 1 on success, 0 if TUI tests should be skipped. +proc Term::prepare_for_tui {} { + if { [is_remote host] } { + # In clean_restart, we're using "setenv TERM ansi", which has + # effect on build. If we have [is_remote host] == 0, so + # build == host, then it also has effect on host. But for + # [is_remote host] == 1, it has no effect on host. + return 0 } - # As get_line, but annotate with attributes. - proc get_line_with_attrs {n {c ""}} { - return [get_line_1 $n $c 1] + if {![allow_tui_tests]} { + return 0 } - # Get just the character at (X, Y). - proc get_char {x y} { - variable _chars - return [lindex $_chars($x,$y) 0] + gdb_test_no_output "set tui border-kind ascii" + gdb_test_no_output "maint set tui-resize-message on" + # When matching GDB output using Term::wait_for, the number of + # matching attempts in wait_for can be influenced by CLI styling. + # Disable it by default to avoid this. + gdb_test_no_output "set style enabled off" + return 1 +} + +# Start the TUI. Returns 1 on success, 0 if TUI tests should be +# skipped. +proc Term::enter_tui {} { + if {![prepare_for_tui]} { + return 0 } - # Get the entire screen as a string. - proc get_all_lines {} { - variable _rows - variable _cols - variable _chars + command_no_prompt_prefix "tui enable" + return 1 +} - set result "" - for {set y 0} {$y < $_rows} {incr y} { - for {set x 0} {$x < $_cols} {incr x} { - append result [lindex $_chars($x,$y) 0] - } - append result "\n" +# Send the command CMD to gdb, then wait for a gdb prompt to be +# seen in the TUI. CMD should not end with a newline -- that will +# be supplied by this function. +proc Term::command {cmd} { + global gdb_prompt + send_gdb "$cmd\n" + set str [string_to_regexp $cmd] + set str "(^|\\|)$gdb_prompt $str" + wait_for $str +} + +# As proc command, but don't wait for an initial prompt. This is used for +# initial terminal commands, where there's no prompt yet. +proc Term::command_no_prompt_prefix {cmd} { + gen_prompt + command $cmd +} + +# Apply the attribute list in ATTRS to attributes array UPVAR_NAME. +# Return a string annotating the changed attributes. +proc Term::apply_attrs { upvar_name attrs } { + set res "" + upvar $upvar_name var + foreach { attr val } $attrs { + if { $var($attr) != $val } { + append res "<$attr:$val>" + set var($attr) $val } + } - return $result + return $res +} + +# Return the text of screen line N. Lines are 0-based. Start at column +# X. If C is non-empty, stop before column C. Columns are also +# zero-based. If ATTRS, annotate with attributes. +proc Term::get_string {n x c {attrs 0}} { + variable _rows + # This can happen during resizing, if the cursor seems to + # temporarily be off-screen. + if {$n >= $_rows} { + return "" } - # Get the text just before the cursor. - proc get_current_line {} { - variable _cur_col - variable _cur_row - return [get_line $_cur_row $_cur_col] + set result "" + variable _cols + variable _chars + set c [_default $c $_cols] + if { $attrs } { + _reset_attrs line_attrs + } + while {$x < $c} { + if { $attrs } { + set char_attrs [lindex $_chars($x,$n) 1] + append result [apply_attrs line_attrs $char_attrs] + } + append result [lindex $_chars($x,$n) 0] + incr x } + if { $attrs } { + _reset_attrs zero_attrs + set char_attrs [array get zero_attrs] + append result [apply_attrs line_attrs $char_attrs] + } + return $result +} - # Helper function for check_box. Returns empty string if the box - # is found, description of why not otherwise. - proc _check_box {x y width height} { - set x2 [expr {$x + $width - 1}] - set y2 [expr {$y + $height - 1}] +# Return the text of screen line N. Lines are 0-based. Start at column +# X. If C is non-empty, stop before column C. Columns are also +# zero-based. Annotate with attributes. +proc Term::get_string_with_attrs { n x c } { + return [get_string $n $x $c 1] +} - verbose -log "_check_box x=$x, y=$y, x2=$x2, y2=$y2, width=$width, height=$height" +# Return the text of screen line N. Lines are 0-based. If C is +# non-empty, stop before column C. Columns are also zero-based. If +# ATTRS, annotate with attributes. +proc Term::get_line_1 {n c attrs} { + return [get_string $n 0 $c $attrs] +} - set c [get_char $x $y] - if {$c != "+"} { - return "ul corner is $c, not +" - } +# Return the text of screen line N, without attributes. Lines are +# 0-based. If C is given, stop before column C. Columns are also +# zero-based. +proc Term::get_line {n {c ""} } { + return [get_line_1 $n $c 0] +} - set c [get_char $x $y2] - if {$c != "+"} { - return "ll corner is $c, not +" - } +# As get_line, but annotate with attributes. +proc Term::get_line_with_attrs {n {c ""}} { + return [get_line_1 $n $c 1] +} - set c [get_char $x2 $y] - if {$c != "+"} { - return "ur corner is $c, not +" - } +# Get just the character at (X, Y). +proc Term::get_char {x y} { + variable _chars + return [lindex $_chars($x,$y) 0] +} - set c [get_char $x2 $y2] - if {$c != "+"} { - return "lr corner is $c, not +" - } +# Get the entire screen as a string. +proc Term::get_all_lines {} { + variable _rows + variable _cols + variable _chars - # Note we do not check the full horizonal borders of the box. - # The top will contain a title, and the bottom may as well, if - # it is overlapped by some other border. However, at most a - # title should appear as '+-VERY LONG TITLE-+', so we can - # check for the '+-' on the left, and '-+' on the right. - set c [get_char [expr {$x + 1}] $y] - if {$c != "-"} { - return "ul title padding is $c, not -" + set result "" + for {set y 0} {$y < $_rows} {incr y} { + for {set x 0} {$x < $_cols} {incr x} { + append result [lindex $_chars($x,$y) 0] } + append result "\n" + } - set c [get_char [expr {$x2 - 1}] $y] - if {$c != "-"} { - return "ul title padding is $c, not -" - } + return $result +} - # Now check the vertical borders. - for {set i [expr {$y + 1}]} {$i < $y2 - 1} {incr i} { - set c [get_char $x $i] - if {$c != "|"} { - return "left side $i is $c, not |" - } +# Get the text just before the cursor. +proc Term::get_current_line {} { + variable _cur_col + variable _cur_row + return [get_line $_cur_row $_cur_col] +} - set c [get_char $x2 $i] - if {$c != "|"} { - return "right side $i is $c, not |" - } - } +# Helper function for check_box. Returns empty string if the box +# is found, description of why not otherwise. +proc Term::_check_box {x y width height} { + set x2 [expr {$x + $width - 1}] + set y2 [expr {$y + $height - 1}] - return "" + verbose -log "_check_box x=$x, y=$y, x2=$x2, y2=$y2, width=$width, height=$height" + + set c [get_char $x $y] + if {$c != "+"} { + return "ul corner is $c, not +" } - # Check for a box at the given coordinates. - proc check_box {test_name x y width height} { - dump_box $x $y $width $height - set why [_check_box $x $y $width $height] - if {$why == ""} { - pass $test_name - } else { - fail "$test_name ($why)" - } + set c [get_char $x $y2] + if {$c != "+"} { + return "ll corner is $c, not +" } - # Wait until a box appears at the given coordinates. - proc wait_for_box {test_name x y width height} { - while 1 { - if { [accept_gdb_output] == 0 } { - return 0 - } + set c [get_char $x2 $y] + if {$c != "+"} { + return "ur corner is $c, not +" + } - set why [_check_box $x $y $width $height] - if {$why == ""} { - pass $test_name - break - } - } + set c [get_char $x2 $y2] + if {$c != "+"} { + return "lr corner is $c, not +" } - # Check whether the text contents of the terminal match the - # regular expression. Note that text styling is not considered. - proc check_contents {test_name regexp} { - dump_screen - set contents [get_all_lines] - gdb_assert {[regexp -- $regexp $contents]} $test_name + # Note we do not check the full horizonal borders of the box. + # The top will contain a title, and the bottom may as well, if + # it is overlapped by some other border. However, at most a + # title should appear as '+-VERY LONG TITLE-+', so we can + # check for the '+-' on the left, and '-+' on the right. + set c [get_char [expr {$x + 1}] $y] + if {$c != "-"} { + return "ul title padding is $c, not -" } - # As check_contents, but check that the text contents of the terminal does - # not match the regular expression. - proc check_contents_not {test_name regexp} { - dump_screen - set contents [get_all_lines] - gdb_assert {![regexp -- $regexp $contents]} $test_name + set c [get_char [expr {$x2 - 1}] $y] + if {$c != "-"} { + return "ul title padding is $c, not -" } - # Get the region of the screen described by X, Y, WIDTH, and - # HEIGHT, and separate the lines using SEP. If ATTRS is true then - # include attribute information in the output. - proc get_region { x y width height sep { attrs false } } { - variable _chars + # Now check the vertical borders. + for {set i [expr {$y + 1}]} {$i < $y2 - 1} {incr i} { + set c [get_char $x $i] + if {$c != "|"} { + return "left side $i is $c, not |" + } - if { $attrs } { - _reset_attrs region_attrs + set c [get_char $x2 $i] + if {$c != "|"} { + return "right side $i is $c, not |" } + } - # Grab the contents of the box, join each line together - # using $sep. - set result "" - for {set yy $y} {$yy < [expr {$y + $height}]} {incr yy} { - if {$yy > $y} { - # Add the end of line sequence only if this isn't the - # first line. - append result $sep - } - for {set xx $x} {$xx < [expr {$x + $width}]} {incr xx} { - if { $attrs } { - set char_attrs [lindex $_chars($xx,$yy) 1] - append result [apply_attrs region_attrs $char_attrs] - } + return "" +} - append result [get_char $xx $yy] - } +# Check for a box at the given coordinates. +proc Term::check_box {test_name x y width height} { + dump_box $x $y $width $height + set why [_check_box $x $y $width $height] + if {$why == ""} { + pass $test_name + } else { + fail "$test_name ($why)" + } +} + +# Wait until a box appears at the given coordinates. +proc Term::wait_for_box {test_name x y width height} { + while 1 { + if { [accept_gdb_output] == 0 } { + return 0 } - if { $attrs } { - _reset_attrs zero_attrs - set char_attrs [array get zero_attrs] - append result [apply_attrs region_attrs $char_attrs] + + set why [_check_box $x $y $width $height] + if {$why == ""} { + pass $test_name + break } - return $result } +} - # Check that the region of the screen described by X, Y, WIDTH, - # and HEIGHT match REGEXP. This is like check_contents except - # only part of the screen is checked. This can be used to check - # the contents within a box (though check_box_contents is a better - # choice for boxes with a border). Return 1 if check succeeded. - proc check_region_contents_p { x y width height regexp } { - variable _chars - dump_box $x $y $width $height +# Check whether the text contents of the terminal match the +# regular expression. Note that text styling is not considered. +proc Term::check_contents {test_name regexp} { + dump_screen + set contents [get_all_lines] + gdb_assert {[regexp -- $regexp $contents]} $test_name +} - # Now grab the contents of the box, join each line together - # with a '\r\n' sequence and match against REGEXP. - set result [get_region $x $y $width $height "\r\n"] - return [regexp -- $regexp $result] - } +# As check_contents, but check that the text contents of the terminal does +# not match the regular expression. +proc Term::check_contents_not {test_name regexp} { + dump_screen + set contents [get_all_lines] + gdb_assert {![regexp -- $regexp $contents]} $test_name +} - # Check that the region of the screen described by X, Y, WIDTH, - # and HEIGHT match REGEXP. As check_region_contents_p, but produce - # a pass/fail message. - proc check_region_contents { test_name x y width height regexp } { - set ok [check_region_contents_p $x $y $width $height $regexp] - gdb_assert {$ok} $test_name - } +# Get the region of the screen described by X, Y, WIDTH, and +# HEIGHT, and separate the lines using SEP. If ATTRS is true then +# include attribute information in the output. +proc Term::get_region { x y width height sep { attrs false } } { + variable _chars - # Check the contents of a box on the screen. This is a little - # like check_contents, but doesn't check the whole screen - # contents, only the contents of a single box. This procedure - # includes (effectively) a call to check_box to ensure there is a - # box where expected, if there is then the contents of the box are - # matched against REGEXP. - proc check_box_contents {test_name x y width height regexp} { - variable _chars + if { $attrs } { + _reset_attrs region_attrs + } - dump_box $x $y $width $height - set why [_check_box $x $y $width $height] - if {$why != ""} { - fail "$test_name (box check: $why)" - return + # Grab the contents of the box, join each line together + # using $sep. + set result "" + for {set yy $y} {$yy < [expr {$y + $height}]} {incr yy} { + if {$yy > $y} { + # Add the end of line sequence only if this isn't the + # first line. + append result $sep } + for {set xx $x} {$xx < [expr {$x + $width}]} {incr xx} { + if { $attrs } { + set char_attrs [lindex $_chars($xx,$yy) 1] + append result [apply_attrs region_attrs $char_attrs] + } - check_region_contents $test_name [expr {$x + 1}] [expr {$y + 1}] \ - [expr {$width - 2}] [expr {$height - 2}] $regexp + append result [get_char $xx $yy] + } + } + if { $attrs } { + _reset_attrs zero_attrs + set char_attrs [array get zero_attrs] + append result [apply_attrs region_attrs $char_attrs] } + return $result +} - # A debugging function to dump the current screen, with line - # numbers. If ATTRS, annotate with attributes. - proc dump_screen { {attrs 0} } { - variable _rows - variable _cols - variable _cur_row - variable _cur_col +# Check that the region of the screen described by X, Y, WIDTH, +# and HEIGHT match REGEXP. This is like check_contents except +# only part of the screen is checked. This can be used to check +# the contents within a box (though check_box_contents is a better +# choice for boxes with a border). Return 1 if check succeeded. +proc Term::check_region_contents_p { x y width height regexp } { + variable _chars + dump_box $x $y $width $height - verbose -log "Screen Dump (size $_cols columns x $_rows rows, cursor at column $_cur_col, row $_cur_row):" + # Now grab the contents of the box, join each line together + # with a '\r\n' sequence and match against REGEXP. + set result [get_region $x $y $width $height "\r\n"] + return [regexp -- $regexp $result] +} - for {set y 0} {$y < $_rows} {incr y} { - set fmt [format %5d $y] - verbose -log "$fmt [get_line_1 $y {} $attrs]" - } +# Check that the region of the screen described by X, Y, WIDTH, +# and HEIGHT match REGEXP. As check_region_contents_p, but produce +# a pass/fail message. +proc Term::check_region_contents { test_name x y width height regexp } { + set ok [check_region_contents_p $x $y $width $height $regexp] + gdb_assert {$ok} $test_name +} + +# Check the contents of a box on the screen. This is a little +# like check_contents, but doesn't check the whole screen +# contents, only the contents of a single box. This procedure +# includes (effectively) a call to check_box to ensure there is a +# box where expected, if there is then the contents of the box are +# matched against REGEXP. +proc Term::check_box_contents {test_name x y width height regexp} { + variable _chars + + dump_box $x $y $width $height + set why [_check_box $x $y $width $height] + if {$why != ""} { + fail "$test_name (box check: $why)" + return } - # As dump_screen, but with attributes annotation. - proc dump_screen_with_attrs {} { - return [dump_screen 1] + check_region_contents $test_name [expr {$x + 1}] [expr {$y + 1}] \ + [expr {$width - 2}] [expr {$height - 2}] $regexp +} + +# A debugging function to dump the current screen, with line +# numbers. If ATTRS, annotate with attributes. +proc Term::dump_screen { {attrs 0} } { + variable _rows + variable _cols + variable _cur_row + variable _cur_col + + verbose -log "Screen Dump (size $_cols columns x $_rows rows, cursor at column $_cur_col, row $_cur_row):" + + for {set y 0} {$y < $_rows} {incr y} { + set fmt [format %5d $y] + verbose -log "$fmt [get_line_1 $y {} $attrs]" } +} - # A debugging function to dump a box from the current screen, with line - # numbers. - proc dump_box { x y width height } { - verbose -log "Box Dump ($width x $height) @ ($x, $y):" - set region [get_region $x $y $width $height "\n"] - set lines [split $region "\n"] - set nr $y - foreach line $lines { - set fmt [format %5d $nr] - verbose -log "$fmt $line" - incr nr - } +# As dump_screen, but with attributes annotation. +proc Term::dump_screen_with_attrs {} { + return [dump_screen 1] +} + +# A debugging function to dump a box from the current screen, with line +# numbers. +proc Term::dump_box { x y width height } { + verbose -log "Box Dump ($width x $height) @ ($x, $y):" + set region [get_region $x $y $width $height "\n"] + set lines [split $region "\n"] + set nr $y + foreach line $lines { + set fmt [format %5d $nr] + verbose -log "$fmt $line" + incr nr } +} - # Resize the terminal. - proc _do_resize {rows cols} { - variable _chars - variable _rows - variable _cols +# Resize the terminal. +proc Term::_do_resize {rows cols} { + variable _chars + variable _rows + variable _cols - set old_rows [expr {min ($_rows, $rows)}] - set old_cols [expr {min ($_cols, $cols)}] + set old_rows [expr {min ($_rows, $rows)}] + set old_cols [expr {min ($_cols, $cols)}] - # Copy locally. - array set local_chars [array get _chars] - unset _chars + # Copy locally. + array set local_chars [array get _chars] + unset _chars - set _rows $rows - set _cols $cols - _clear_lines 0 $_rows + set _rows $rows + set _cols $cols + _clear_lines 0 $_rows - for {set x 0} {$x < $old_cols} {incr x} { - for {set y 0} {$y < $old_rows} {incr y} { - set _chars($x,$y) $local_chars($x,$y) - } + for {set x 0} {$x < $old_cols} {incr x} { + for {set y 0} {$y < $old_rows} {incr y} { + set _chars($x,$y) $local_chars($x,$y) } } +} - proc resize {rows cols {wait_for_msg 1}} { - variable _rows - variable _cols - variable _resize_count +proc Term::resize {rows cols {wait_for_msg 1}} { + variable _rows + variable _cols + variable _resize_count - # expect handles each argument to stty separately. This means - # that gdb will see SIGWINCH twice. Rather than rely on this - # behavior (which, after all, could be changed), we make it - # explicit here. This also simplifies waiting for the redraw. - _do_resize $rows $_cols - stty rows $_rows < $::gdb_tty_name - if { $wait_for_msg } { - wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" - } - incr _resize_count - _do_resize $_rows $cols - stty columns $_cols < $::gdb_tty_name - if { $wait_for_msg } { - wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" - } - incr _resize_count - } + # expect handles each argument to stty separately. This means + # that gdb will see SIGWINCH twice. Rather than rely on this + # behavior (which, after all, could be changed), we make it + # explicit here. This also simplifies waiting for the redraw. + _do_resize $rows $_cols + stty rows $_rows < $::gdb_tty_name + if { $wait_for_msg } { + wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" + } + incr _resize_count + _do_resize $_rows $cols + stty columns $_cols < $::gdb_tty_name + if { $wait_for_msg } { + wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" + } + incr _resize_count } @@ -1312,7 +1312,7 @@ print_gdb_version (struct ui_file *stream, bool interactive) /* Second line is a copyright notice. */ gdb_printf (stream, - "Copyright (C) 2024 Free Software Foundation, Inc.\n"); + "Copyright (C) 2025 Free Software Foundation, Inc.\n"); /* Following the copyright is a brief statement that the program is free software, that users are free to copy and change it on @@ -1596,6 +1596,11 @@ This GDB was configured as follows:\n\ --with-system-gdbinit-dir=%s%s\n\ "), SYSTEM_GDBINIT_DIR, SYSTEM_GDBINIT_DIR_RELOCATABLE ? " (relocatable)" : ""); +#ifdef SUPPORTED_BINARY_FILE_FORMATS + gdb_printf (stream, _("\ + --enable-binary-file-formats=%s\n"), SUPPORTED_BINARY_FILE_FORMATS); +#endif + /* We assume "relocatable" will be printed at least once, thus we always print this text. It's a reasonably safe assumption for now. */ gdb_printf (stream, _("\n\ diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index c0f1eae..89f7414 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -208,16 +208,16 @@ set_tracepoint_num (int num) static void set_traceframe_context (const frame_info_ptr &trace_frame) { - CORE_ADDR trace_pc; + std::optional<CORE_ADDR> trace_pc; struct symbol *traceframe_fun; symtab_and_line traceframe_sal; /* Save as globals for internal use. */ if (trace_frame != NULL - && get_frame_pc_if_available (trace_frame, &trace_pc)) + && (trace_pc = get_frame_pc_if_available (trace_frame))) { - traceframe_sal = find_pc_line (trace_pc, 0); - traceframe_fun = find_pc_function (trace_pc); + traceframe_sal = find_pc_line (*trace_pc, 0); + traceframe_fun = find_pc_function (*trace_pc); /* Save linenumber as "$trace_line", a debugger variable visible to users. */ diff --git a/gdb/tui/tui-disasm.c b/gdb/tui/tui-disasm.c index 627f71c..07453b4 100644 --- a/gdb/tui/tui-disasm.c +++ b/gdb/tui/tui-disasm.c @@ -100,6 +100,9 @@ tui_disassemble (struct gdbarch *gdbarch, { bool term_out = disassembler_styling && gdb_stdout->can_emit_style_escape (); string_file gdb_dis_out (term_out); + struct ui_file *stream = (addr_size == nullptr + ? (decltype (stream))&null_stream + : (decltype (stream))&gdb_dis_out); /* Must start with an empty list. */ asm_lines.clear (); @@ -108,11 +111,13 @@ tui_disassemble (struct gdbarch *gdbarch, for (int i = 0; i < count; ++i) { tui_asm_line tal; - CORE_ADDR orig_pc = pc; + + /* Save the instruction address. */ + tal.addr = pc; try { - pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL); + pc += gdb_print_insn (gdbarch, pc, stream, NULL); } catch (const gdb_exception_error &except) { @@ -124,25 +129,24 @@ tui_disassemble (struct gdbarch *gdbarch, return pc; } + /* If that's all we need, continue. */ + if (addr_size == nullptr) + { + asm_lines.push_back (std::move (tal)); + continue; + } + /* Capture the disassembled instruction. */ tal.insn = gdb_dis_out.release (); /* And capture the address the instruction is at. */ - tal.addr = orig_pc; - print_address (gdbarch, orig_pc, &gdb_dis_out); + print_address (gdbarch, tal.addr, &gdb_dis_out); tal.addr_string = gdb_dis_out.release (); + tal.addr_size = (term_out + ? len_without_escapes (tal.addr_string) + : tal.addr_string.size ()); - if (addr_size != nullptr) - { - size_t new_size; - - if (term_out) - new_size = len_without_escapes (tal.addr_string); - else - new_size = tal.addr_string.size (); - *addr_size = std::max (*addr_size, new_size); - tal.addr_size = new_size; - } + *addr_size = std::max (*addr_size, tal.addr_size); asm_lines.push_back (std::move (tal)); } diff --git a/gdb/tui/tui-status.c b/gdb/tui/tui-status.c index c2d3873..1e09975 100644 --- a/gdb/tui/tui-status.c +++ b/gdb/tui/tui-status.c @@ -271,10 +271,14 @@ tui_show_frame_info (const frame_info_ptr &fi) symtab_and_line sal = find_frame_sal (fi); const char *func_name; + std::optional<CORE_ADDR> tmp_pc = get_frame_pc_if_available (fi); /* find_frame_sal does not always set PC, but we want to ensure that it is available in the SAL. */ - if (get_frame_pc_if_available (fi, &sal.pc)) - func_name = tui_get_function_from_frame (fi); + if (tmp_pc.has_value ()) + { + sal.pc = *tmp_pc; + func_name = tui_get_function_from_frame (fi); + } else func_name = _("<unavailable>"); diff --git a/gdb/tui/tui-winsource.c b/gdb/tui/tui-winsource.c index a545c48..618d72b 100644 --- a/gdb/tui/tui-winsource.c +++ b/gdb/tui/tui-winsource.c @@ -461,7 +461,9 @@ tui_source_window_base::rerender () /* find_frame_sal does not always set SAL.PC, but we want to ensure that it is available in the SAL before updating the window. */ - get_frame_pc_if_available (frame, &sal.pc); + std::optional<CORE_ADDR> tmp_pc = get_frame_pc_if_available (frame); + if (tmp_pc.has_value ()) + sal.pc = *tmp_pc; maybe_update (get_frame_arch (frame), sal); update_exec_info (false); diff --git a/gdb/tui/tui.c b/gdb/tui/tui.c index 5883d6c..01aee2f 100644 --- a/gdb/tui/tui.c +++ b/gdb/tui/tui.c @@ -125,6 +125,19 @@ tui_rl_switch_mode (int notused1, int notused2) } else { + /* If we type "foo", entering it into the readline buffer + + (gdb) foo + ^ + and then switch to TUI and back, we may get back + + (gdb) foo + ^ + which is confusing because "foo" is no longer part of the + readline buffer. Fix this by clearing it before switching to + TUI. */ + rl_clear_visible_line (); + /* If tui_enable throws, we'll re-prep below. */ rl_deprep_terminal (); tui_enable (); diff --git a/gdb/valops.c b/gdb/valops.c index 88f3e32..c260a79 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -45,9 +45,6 @@ /* Local functions. */ -static int typecmp (bool staticp, bool varargs, int nargs, - struct field t1[], const gdb::array_view<value *> t2); - static struct value *search_struct_field (const char *, struct value *, struct type *, int); @@ -1766,7 +1763,7 @@ value_string (const gdb_byte *ptr, ssize_t count, struct type *char_type) /* See if we can pass arguments in T2 to a function which takes arguments - of types T1. T1 is a list of NARGS arguments, and T2 is an array_view + of types T1. T1 is an array_view of arguments, and T2 is an array_view of the values we're trying to pass. If some arguments need coercion of some sort, then the coerced values are written into T2. Return value is 0 if the arguments could be matched, or the position at which they @@ -1783,8 +1780,8 @@ value_string (const gdb_byte *ptr, ssize_t count, struct type *char_type) requested operation is type secure, shouldn't we? FIXME. */ static int -typecmp (bool staticp, bool varargs, int nargs, - struct field t1[], gdb::array_view<value *> t2) +typecmp (bool staticp, bool varargs, + gdb::array_view<struct field> t1, gdb::array_view<value *> t2) { int i; @@ -1794,7 +1791,7 @@ typecmp (bool staticp, bool varargs, int nargs, t2 = t2.slice (1); for (i = 0; - (i < nargs) && t1[i].type ()->code () != TYPE_CODE_VOID; + (i < t1.size ()) && t1[i].type ()->code () != TYPE_CODE_VOID; i++) { struct type *tt1, *tt2; @@ -2227,7 +2224,6 @@ search_struct_method (const char *name, struct value **arg1p, gdb_assert (args.has_value ()); if (!typecmp (TYPE_FN_FIELD_STATIC_P (f, j), TYPE_FN_FIELD_TYPE (f, j)->has_varargs (), - TYPE_FN_FIELD_TYPE (f, j)->num_fields (), TYPE_FN_FIELD_ARGS (f, j), *args)) { if (TYPE_FN_FIELD_VIRTUAL_P (f, j)) |