[RFA][PATCH v4 3/5] Dynamic core regset sections support

Andreas Arnez
2013-07-03T17:04:22Z

Introduce a new way of specifying core file register note sections on
GNU/Linux targets. So far the target-dependent code provides a static
list of supported register note sections. This change enables the use
of an iterator function instead. The existing "static list" interface
is preserved.

A dynamic selection of regset sections is needed on z/Architecture
systems with transactional-execution support. With the static
approach, gcore always writes the TDB regset into the core file, even
if the inferior was stopped outside transactions and thus the TDB
registers are currently invalid. This results in writing zeroes,
which is wrong.

The use of an iterator function can also reduce complexity in the tdep
code for targets with many different register set combinations.

2013-07-03 Andreas Arnez <arnez@linux.vnet.ibm.com>

* regset.h (regset_iterate_over_sections): New function
declaration.
* regset.c (regset_iterate_over_sections): New function.
* gdbarch.sh: Add typedef for iterate_over_regset_sections_cb.
Support predicate functions with invalid_p.
(function_list): Add iterate_over_regset_sections.
* gdbarch.h: Regenerate.
* gdbarch.c: Regenerate.
* linux-tdep.c (linux_collect_regset_section_cb_data): New
structure.
(linux_collect_regset_section_cb): New function.
(linux_collect_thread_registers): Replace loop by iteration
through gdbarch_iterate_over_regset_sections.
(linux_make_corefile_notes_1): Adjust check.
* corelow.c (get_core_registers_cb): New function.
(get_core_registers): Instead of looping through the list returned
by gdbarch_core_regset_sections, iterate get_core_registers_cb via
gdbarch_iterate_over_regset_sections.

Index: gdb/gdb/gdbarch.sh
===================================================================
--- gdb.orig/gdb/gdbarch.sh
+++ gdb/gdb/gdbarch.sh
@@ -99,9 +99,7 @@ EOF
esac

case "${class}" in
- F | V | M )
- case "${invalid_p}" in
- "" )
+ F | V | M )
if test -n "${predefault}"
then
#invalid_p="gdbarch->${function} == ${predefault}"
@@ -114,12 +112,6 @@ EOF
predicate="gdbarch->${function} != NULL"
fi
;;
- * )
- echo "Predicate function ${function} with invalid_p." 1>&2
- kill $$
- exit 1
- ;;
- esac
esac

# PREDEFAULT is a valid fallback definition of MEMBER when
@@ -635,7 +627,15 @@ F:CORE_ADDR:fetch_pointer_argument:struc
# name SECT_NAME and size SECT_SIZE.
M:const struct regset *:regset_from_core_section:const char *sect_name, size_t sect_size:sect_name, sect_size

-# Supported register notes in a core file.
+# Iterate over all supported register notes in a core file. For each
+# supported register note section, the iterator must call CB and pass
+# CB_DATA unchanged. If CB returns non-zero, the iterator must stop.
+# If REGCACHE is not NULL, the iterator can limit the supported
+# register note sections based on the current register values.
+# Otherwise it should enumerate all supported register note sections.
+M:void:iterate_over_regset_sections:iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache:cb, cb_data, regcache:::regset_iterate_over_sections:gdbarch->core_regset_sections && !gdbarch->iterate_over_regset_sections
+# Old way of specifying supported register notes in a core file, as a
+# static list. This overrides the iterator function above.
v:struct core_regset_section *:core_regset_sections:const char *name, int len::::::host_address_to_string (gdbarch->core_regset_sections)

# Create core file notes
@@ -992,12 +992,6 @@ EOF
do
eval echo \"\ \ \ \ ${r}=\${${r}}\"
done
- if class_is_predicate_p && fallback_default_p
- then
- echo "Error: predicate function ${function} can not have a non- multi-arch default" 1>&2
- kill $$
- exit 1
- fi
if [ "x${invalid_p}" = "x0" -a -n "${postdefault}" ]
then
echo "Error: postdefault is useless when invalid_p=0" 1>&2
@@ -1120,6 +1114,9 @@ extern struct gdbarch startup_gdbarch;

typedef int (iterate_over_objfiles_in_search_order_cb_ftype)
(struct objfile *objfile, void *cb_data);
+
+typedef int (iterate_over_regset_sections_cb)
+ (const char *sect_name, int size, const char *human_name, void *cb_data);
EOF

# function typedef's
@@ -1432,6 +1429,7 @@ cat <<EOF
#include "gdb_obstack.h"
#include "observer.h"
#include "regcache.h"
+#include "regset.h"
#include "objfiles.h"

/* Static function declarations */
@@ -1700,14 +1698,14 @@ do
if [ "x${invalid_p}" = "x0" ]
then
printf " /* Skip verify of ${function}, invalid_p == 0 */\n"
- elif class_is_predicate_p
- then
- printf " /* Skip verify of ${function}, has predicate. */\n"
# FIXME: See do_read for potential simplification
elif [ -n "${invalid_p}" -a -n "${postdefault}" ]
then
printf " if (${invalid_p})\n"
printf " gdbarch->${function} = ${postdefault};\n"
+ elif class_is_predicate_p
+ then
+ printf " /* Skip verify of ${function}, has predicate. */\n"
elif [ -n "${predefault}" -a -n "${postdefault}" ]
then
printf " if (gdbarch->${function} == ${predefault})\n"
Index: gdb/gdb/linux-tdep.c
===================================================================
--- gdb.orig/gdb/linux-tdep.c
+++ gdb/gdb/linux-tdep.c
@@ -1010,6 +1010,54 @@ linux_make_mappings_corefile_notes (stru
return note_data;
}

+/* Structure for passing information from
+ linux_collect_thread_registers via an iterator to
+ linux_collect_regset_section_cb. */
+
+struct linux_collect_regset_section_cb_data
+{
+ struct gdbarch *gdbarch;
+ const struct regcache *regcache;
+ bfd *obfd;
+ char *note_data;
+ int *note_size;
+ unsigned long lwp;
+ enum gdb_signal stop_signal;
+};
+
+/* Callback for iterate_over_regset_sections that records a single
+ regset in the corefile note section. */
+
+static int
+linux_collect_regset_section_cb (const char *sect_name, int size,
+ const char *human_name, void *cb_data)
+{
+ const struct regset *regset;
+ char *buf;
+ struct linux_collect_regset_section_cb_data *data = cb_data;
+
+ regset = gdbarch_regset_from_core_section (data->gdbarch, sect_name, size);
+ gdb_assert (regset && regset->collect_regset);
+
+ buf = xmalloc (size);
+ regset->collect_regset (regset, data->regcache, -1, buf, size);
+
+ /* PRSTATUS still needs to be treated specially. */
+ if (strcmp (sect_name, ".reg") == 0)
+ data->note_data = (char *) elfcore_write_prstatus
+ (data->obfd, data->note_data, data->note_size, data->lwp,
+ gdb_signal_to_host (data->stop_signal), buf);
+ else
+ data->note_data = (char *) elfcore_write_register_note
+ (data->obfd, data->note_data, data->note_size,
+ sect_name, buf, size);
+ xfree (buf);
+
+ if (!data->note_data)
+ return 1; /* Abort iteration. */
+ return 0;
+}
+
/* Records the thread's register state for the corefile note
section. */

@@ -1020,47 +1068,24 @@ linux_collect_thread_registers (const st
enum gdb_signal stop_signal)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
- struct core_regset_section *sect_list;
- unsigned long lwp;
+ struct linux_collect_regset_section_cb_data data;

- sect_list = gdbarch_core_regset_sections (gdbarch);
- gdb_assert (sect_list);
+ data.gdbarch = gdbarch;
+ data.regcache = regcache;
+ data.obfd = obfd;
+ data.note_data = note_data;
+ data.note_size = note_size;
+ data.stop_signal = stop_signal;

/* For remote targets the LWP may not be available, so use the TID. */
- lwp = ptid_get_lwp (ptid);
- if (!lwp)
- lwp = ptid_get_tid (ptid);
-
- while (sect_list->sect_name != NULL)
- {
- const struct regset *regset;
- char *buf;
-
- regset = gdbarch_regset_from_core_section (gdbarch,
- sect_list->sect_name,
- sect_list->size);
- gdb_assert (regset && regset->collect_regset);
-
- buf = xmalloc (sect_list->size);
- regset->collect_regset (regset, regcache, -1, buf, sect_list->size);
-
- /* PRSTATUS still needs to be treated specially. */
- if (strcmp (sect_list->sect_name, ".reg") == 0)
- note_data = (char *) elfcore_write_prstatus
- (obfd, note_data, note_size, lwp,
- gdb_signal_to_host (stop_signal), buf);
- else
- note_data = (char *) elfcore_write_register_note
- (obfd, note_data, note_size,
- sect_list->sect_name, buf, sect_list->size);
- xfree (buf);
- sect_list++;
-
- if (!note_data)
- return NULL;
- }
-
- return note_data;
+ data.lwp = ptid_get_lwp (ptid);
+ if (!data.lwp)
+ data.lwp = ptid_get_tid (ptid);
+
+ gdbarch_iterate_over_regset_sections (gdbarch,
+ linux_collect_regset_section_cb,
+ &data, regcache);
+ return data.note_data;
}

/* Fetch the siginfo data for the current thread, if it exists. If
@@ -1440,7 +1465,7 @@ linux_make_corefile_notes_1 (struct gdba
converted to gdbarch_core_regset_sections, we no longer need to fall back
to the target method at this point. */

- if (!gdbarch_core_regset_sections (gdbarch))
+ if (!gdbarch_iterate_over_regset_sections_p (gdbarch))
return target_make_corefile_notes (obfd, note_size);
else
return linux_make_corefile_notes (gdbarch, obfd, note_size,
Index: gdb/gdb/gdbarch.c
===================================================================
--- gdb.orig/gdb/gdbarch.c
+++ gdb/gdb/gdbarch.c
@@ -49,6 +49,7 @@
#include "gdb_obstack.h"
#include "observer.h"
#include "regcache.h"
+#include "regset.h"
#include "objfiles.h"

/* Static function declarations */
@@ -236,6 +237,7 @@ struct gdbarch
gdbarch_register_reggroup_p_ftype *register_reggroup_p;
gdbarch_fetch_pointer_argument_ftype *fetch_pointer_argument;
gdbarch_regset_from_core_section_ftype *regset_from_core_section;
+ gdbarch_iterate_over_regset_sections_ftype *iterate_over_regset_sections;
struct core_regset_section * core_regset_sections;
gdbarch_make_corefile_notes_ftype *make_corefile_notes;
gdbarch_elfcore_write_linux_prpsinfo_ftype *elfcore_write_linux_prpsinfo;
@@ -408,6 +410,7 @@ struct gdbarch startup_gdbarch =
default_register_reggroup_p, /* register_reggroup_p */
0, /* fetch_pointer_argument */
0, /* regset_from_core_section */
+ 0, /* iterate_over_regset_sections */
0, /* core_regset_sections */
0, /* make_corefile_notes */
0, /* elfcore_write_linux_prpsinfo */
@@ -712,6 +715,8 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of register_reggroup_p, invalid_p == 0 */
/* Skip verify of fetch_pointer_argument, has predicate. */
/* Skip verify of regset_from_core_section, has predicate. */
+ if (gdbarch->core_regset_sections && !gdbarch->iterate_over_regset_sections)
+ gdbarch->iterate_over_regset_sections = regset_iterate_over_sections;
/* Skip verify of make_corefile_notes, has predicate. */
/* Skip verify of elfcore_write_linux_prpsinfo, has predicate. */
/* Skip verify of find_memory_regions, has predicate. */
@@ -1102,6 +1107,12 @@ gdbarch_dump (struct gdbarch *gdbarch, s
"gdbarch_dump: iterate_over_objfiles_in_search_order = <%s>\n",
host_address_to_string (gdbarch->iterate_over_objfiles_in_search_order));
fprintf_unfiltered (file,
+ "gdbarch_dump: gdbarch_iterate_over_regset_sections_p() = %d\n",
+ gdbarch_iterate_over_regset_sections_p (gdbarch));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: iterate_over_regset_sections = <%s>\n",
+ host_address_to_string (gdbarch->iterate_over_regset_sections));
+ fprintf_unfiltered (file,
"gdbarch_dump: long_bit = %s\n",
plongest (gdbarch->long_bit));
fprintf_unfiltered (file,
@@ -3345,6 +3356,30 @@ set_gdbarch_regset_from_core_section (st
gdbarch->regset_from_core_section = regset_from_core_section;
}

+int
+gdbarch_iterate_over_regset_sections_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ return gdbarch->iterate_over_regset_sections != NULL;
+}
+
+void
+gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->iterate_over_regset_sections != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_iterate_over_regset_sections called\n");
+ gdbarch->iterate_over_regset_sections (gdbarch, cb, cb_data, regcache);
+}
+
+void
+set_gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ gdbarch_iterate_over_regset_sections_ftype iterate_over_regset_sections)
+{
+ gdbarch->iterate_over_regset_sections = iterate_over_regset_sections;
+}
+
struct core_regset_section *
gdbarch_core_regset_sections (struct gdbarch *gdbarch)
{
Index: gdb/gdb/gdbarch.h
===================================================================
--- gdb.orig/gdb/gdbarch.h
+++ gdb/gdb/gdbarch.h
@@ -85,6 +85,9 @@ extern struct gdbarch startup_gdbarch;
typedef int (iterate_over_objfiles_in_search_order_cb_ftype)
(struct objfile *objfile, void *cb_data);

+typedef int (iterate_over_regset_sections_cb)
+ (const char *sect_name, int size, const char *human_name, void *cb_data);
+

/* The following are pre-initialized by GDBARCH. */

@@ -724,7 +727,21 @@ typedef const struct regset * (gdbarch_r
extern const struct regset * gdbarch_regset_from_core_section (struct gdbarch *gdbarch, const char *sect_name, size_t sect_size);
extern void set_gdbarch_regset_from_core_section (struct gdbarch *gdbarch, gdbarch_regset_from_core_section_ftype *regset_from_core_section);

-/* Supported register notes in a core file. */
+/* Iterate over all supported register notes in a core file. For each
+ supported register note section, the iterator must call CB and pass
+ CB_DATA unchanged. If CB returns non-zero, the iterator must stop.
+ If REGCACHE is not NULL, the iterator can limit the supported
+ register note sections based on the current register values.
+ Otherwise it should enumerate all supported register note sections. */
+
+extern int gdbarch_iterate_over_regset_sections_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_iterate_over_regset_sections_ftype) (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache);
+extern void gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache);
+extern void set_gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch, gdbarch_iterate_over_regset_sections_ftype *iterate_over_regset_sections);
+
+/* Old way of specifying supported register notes in a core file, as a
+ static list. This overrides the iterator function above. */

extern struct core_regset_section * gdbarch_core_regset_sections (struct gdbarch *gdbarch);
extern void set_gdbarch_core_regset_sections (struct gdbarch *gdbarch, struct core_regset_section * core_regset_sections);
Index: gdb/gdb/regset.h
===================================================================
--- gdb.orig/gdb/regset.h
+++ gdb/gdb/regset.h
@@ -66,4 +66,11 @@ extern struct regset *regset_alloc (stru
supply_regset_ftype *supply_regset,
collect_regset_ftype *collect_regset);

+/* Iterate over the list of regsets specified with
+ gdbarch_set_core_regset_sections. */
+extern void regset_iterate_over_sections (struct gdbarch *arch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache);
+
#endif /* regset.h */
Index: gdb/gdb/corelow.c
===================================================================
--- gdb.orig/gdb/corelow.c
+++ gdb/gdb/corelow.c
@@ -547,6 +547,24 @@ get_core_register_section (struct regcac
bfd_section_vma (core_bfd, section)));
}

+/* Callback for get_core_registers that handles a single core file
+ register note section. */
+
+static int
+get_core_registers_cb (const char *sect_name, int size,
+ const char *human_name, void *cb_data)
+{
+ struct regcache *regcache = (struct regcache *) cb_data;
+
+ if (strcmp (sect_name, ".reg") == 0)
+ get_core_register_section (regcache, sect_name, 0, human_name, 1);
+ else if (strcmp (sect_name, ".reg2") == 0)
+ get_core_register_section (regcache, sect_name, 2, human_name, 0);
+ else
+ get_core_register_section (regcache, sect_name, 3, human_name, 0);
+
+ return 0;
+}

/* Get the registers out of a core file. This is the machine-
independent part. Fetch_core_registers is the machine-dependent
@@ -559,8 +577,8 @@ static void
get_core_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
{
- struct core_regset_section *sect_list;
int i;
+ struct gdbarch *gdbarch;

if (!(core_gdbarch && gdbarch_regset_from_core_section_p (core_gdbarch))
&& (core_vec == NULL || core_vec->core_read_registers == NULL))
@@ -570,23 +588,11 @@ get_core_registers (struct target_ops *o
return;
}

- sect_list = gdbarch_core_regset_sections (get_regcache_arch (regcache));
- if (sect_list)
- while (sect_list->sect_name != NULL)
- {
- if (strcmp (sect_list->sect_name, ".reg") == 0)
- get_core_register_section (regcache, sect_list->sect_name,
- 0, sect_list->human_name, 1);
- else if (strcmp (sect_list->sect_name, ".reg2") == 0)
- get_core_register_section (regcache, sect_list->sect_name,
- 2, sect_list->human_name, 0);
- else
- get_core_register_section (regcache, sect_list->sect_name,
- 3, sect_list->human_name, 0);
-
- sect_list++;
- }
-
+ gdbarch = get_regcache_arch (regcache);
+ if (gdbarch_iterate_over_regset_sections_p (gdbarch))
+ gdbarch_iterate_over_regset_sections (gdbarch,
+ get_core_registers_cb,
+ (void *) regcache, NULL);
else
{
get_core_register_section (regcache,
Index: gdb/gdb/regset.c
===================================================================
--- gdb.orig/gdb/regset.c
+++ gdb/gdb/regset.c
@@ -42,3 +42,25 @@ regset_alloc (struct gdbarch *arch,

return regset;
}
+
+/* Iterate over the list of regsets specified with
+ gdbarch_set_core_regset_sections. This is a convenience function
+ for targets which do not need a complex iterator. */
+
+void
+regset_iterate_over_sections (struct gdbarch *arch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+ struct core_regset_section *sect_list;
+
+ sect_list = gdbarch_core_regset_sections (arch);
+ while (sect_list->sect_name != NULL)
+ {
+ if (cb (sect_list->sect_name, sect_list->size,
+ sect_list->human_name, cb_data))
+ return;
+ sect_list++;
+ }
+}