[PATCH 2/2] Put more info in NT_PRPSINFO Linux notes

Pedro Alves
2013-01-31T23:05:10Z

Currently only a few fields of prpsinfo_t on Linux (and elsewhere too)
are filled in when generating NT_PRPSINFO notes on core files --
namely, fname, psargs. E.g., ppid, pgrg and sid are missing. We
could do better. The previous bfd patch added a new interface that
allows passing in the full prpsinfo_t struct to bfd. This patch makes
GDB use it.

Most Linux kernel ports have the same prpsinfo_t type (layed out in
memory as per their ABIs, naturally). Be default, for Linux, we'll
call these two new functions:

/* Linux/most 32-bit archs. */
extern char *elfcore_write_linux_prpsinfo32
(bfd *, char *, int *, const struct elf_internal_linux_prpsinfo *);

/* Linux/most 64-bit archs. */
extern char *elfcore_write_linux_prpsinfo64
(bfd *, char *, int *, const struct elf_internal_linux_prpsinfo *);

GDB will select which depending on the target's pointer width.

PowerPC 32-bit however has a slightly different structure, so it gets
its own entry point:

/* Linux/PPC32 uses different layout compared to most archs. */
extern char *elfcore_write_ppc_linux_prpsinfo32
(bfd *, char *, int *, const struct elf_internal_linux_prpsinfo *);

We add add a new gdbarch hook that PPC taps into, to override the
default bfd elfcore hook selection.

Tested on x86_64 Fedora 17 by building GDB with --enable-targets=all,
in both 64-bit, and -m32 modes, and by running GDBs testsuite with
each build. No regressions.

I haven't audited all Linux ports to see if like PPC their prpsinfo_t
types are not like the others'.

I just noticed, right before pushing send, that linux_fill_prpsinfo
does a "stat" call on the host, while that should be a call on the
target. Unfortunately, there's no target_fileio_stat method.
Fortunately, the RSP bits are already in place, and just we need to
add the target method and hook it to remote_fileio_stat on the remote
target (and to the host stat for native targets).

Meanwhile, comments on the whole approach?

2013-01-31 Sergio Durigan Junior <sergiodj@redhat.com>
Denys Vlasenko <dvlasenk@redhat.com>
Pedro Alves <palves@redhat.com>

* gdbarch.sh (elfcore_write_linux_prpsinfo): New F hook.
(struct elf_internal_linux_prpsinfo): Forward declare.
* gdbarch.h, gdbarch.c: Regenerate.
* linux-tdep.c: Include `cli/cli-utils.h'.
(linux_fill_prpsinfo): New function.
(linux_make_corefile_notes): Use linux_fill_prpsinfo. If there's
an elfcore_write_linux_prpsinfo hook, use it, otherwise, use
elfcore_write_linux_prpsinfo32 or elfcore_write_linux_prpsinfo64
depending on gdbarch pointer bitness.
* ppc-linux-tdep.c: Include elf-bfd.h.
(ppc_linux_init_abi): Hook in elfcore_write_ppc_linux_prpsinfo32
on 32-bit.
---
gdb/gdbarch.c | 33 ++++++++
gdb/gdbarch.h | 13 +++
gdb/gdbarch.sh | 8 ++
gdb/linux-tdep.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++----
gdb/ppc-linux-tdep.c | 8 ++
5 files changed, 257 insertions(+), 15 deletions(-)

diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index e48c5a6..129268f 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -238,6 +238,7 @@ struct gdbarch
gdbarch_regset_from_core_section_ftype *regset_from_core_section;
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;
gdbarch_find_memory_regions_ftype *find_memory_regions;
gdbarch_core_xfer_shared_libraries_ftype *core_xfer_shared_libraries;
gdbarch_core_pid_to_str_ftype *core_pid_to_str;
@@ -408,6 +409,7 @@ struct gdbarch startup_gdbarch =
0, /* regset_from_core_section */
0, /* core_regset_sections */
0, /* make_corefile_notes */
+ 0, /* elfcore_write_linux_prpsinfo */
0, /* find_memory_regions */
0, /* core_xfer_shared_libraries */
0, /* core_pid_to_str */
@@ -709,6 +711,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of fetch_pointer_argument, has predicate. */
/* Skip verify of regset_from_core_section, has predicate. */
/* 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. */
/* Skip verify of core_xfer_shared_libraries, has predicate. */
/* Skip verify of core_pid_to_str, has predicate. */
@@ -955,6 +958,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
"gdbarch_dump: elf_make_msymbol_special = <%s>\n",
host_address_to_string (gdbarch->elf_make_msymbol_special));
fprintf_unfiltered (file,
+ "gdbarch_dump: gdbarch_elfcore_write_linux_prpsinfo_p() = %d\n",
+ gdbarch_elfcore_write_linux_prpsinfo_p (gdbarch));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: elfcore_write_linux_prpsinfo = <%s>\n",
+ host_address_to_string (gdbarch->elfcore_write_linux_prpsinfo));
+ fprintf_unfiltered (file,
"gdbarch_dump: fast_tracepoint_valid_at = <%s>\n",
host_address_to_string (gdbarch->fast_tracepoint_valid_at));
fprintf_unfiltered (file,
@@ -3368,6 +3377,30 @@ set_gdbarch_make_corefile_notes (struct gdbarch *gdbarch,
}

int
+gdbarch_elfcore_write_linux_prpsinfo_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ return gdbarch->elfcore_write_linux_prpsinfo != NULL;
+}
+
+char *
+gdbarch_elfcore_write_linux_prpsinfo (struct gdbarch *gdbarch, bfd *obfd, char *note_data, int *note_size, const struct elf_internal_linux_prpsinfo *info)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->elfcore_write_linux_prpsinfo != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_elfcore_write_linux_prpsinfo called\n");
+ return gdbarch->elfcore_write_linux_prpsinfo (obfd, note_data, note_size, info);
+}
+
+void
+set_gdbarch_elfcore_write_linux_prpsinfo (struct gdbarch *gdbarch,
+ gdbarch_elfcore_write_linux_prpsinfo_ftype elfcore_write_linux_prpsinfo)
+{
+ gdbarch->elfcore_write_linux_prpsinfo = elfcore_write_linux_prpsinfo;
+}
+
+int
gdbarch_find_memory_regions_p (struct gdbarch *gdbarch)
{
gdb_assert (gdbarch != NULL);
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 844653b..464c4b6 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -57,6 +57,7 @@ struct agent_expr;
struct axs_value;
struct stap_parse_info;
struct ravenscar_arch_ops;
+struct elf_internal_linux_prpsinfo;

/* The architecture associated with the inferior through the
connection to the target.
@@ -736,6 +737,18 @@ typedef char * (gdbarch_make_corefile_notes_ftype) (struct gdbarch *gdbarch, bfd
extern char * gdbarch_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size);
extern void set_gdbarch_make_corefile_notes (struct gdbarch *gdbarch, gdbarch_make_corefile_notes_ftype *make_corefile_notes);

+/* The elfcore writer hook to use to write Linux prpsinfo notes to core
+ files. Most Linux architectures use the same prpsinfo32 or
+ prpsinfo64 layouts, and so won't need to provide this hook, as we
+ call the Linux generic routines in bfd to write prpsinfo notes by
+ default. */
+
+extern int gdbarch_elfcore_write_linux_prpsinfo_p (struct gdbarch *gdbarch);
+
+typedef char * (gdbarch_elfcore_write_linux_prpsinfo_ftype) (bfd *obfd, char *note_data, int *note_size, const struct elf_internal_linux_prpsinfo *info);
+extern char * gdbarch_elfcore_write_linux_prpsinfo (struct gdbarch *gdbarch, bfd *obfd, char *note_data, int *note_size, const struct elf_internal_linux_prpsinfo *info);
+extern void set_gdbarch_elfcore_write_linux_prpsinfo (struct gdbarch *gdbarch, gdbarch_elfcore_write_linux_prpsinfo_ftype *elfcore_write_linux_prpsinfo);
+
/* Find core file memory regions */

extern int gdbarch_find_memory_regions_p (struct gdbarch *gdbarch);
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 030bd8b..92d4f0f 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -641,6 +641,13 @@ v:struct core_regset_section *:core_regset_sections:const char *name, int len:::
# Create core file notes
M:char *:make_corefile_notes:bfd *obfd, int *note_size:obfd, note_size

+# The elfcore writer hook to use to write Linux prpsinfo notes to core
+# files. Most Linux architectures use the same prpsinfo32 or
+# prpsinfo64 layouts, and so won't need to provide this hook, as we
+# call the Linux generic routines in bfd to write prpsinfo notes by
+# default.
+F:char *:elfcore_write_linux_prpsinfo:bfd *obfd, char *note_data, int *note_size, const struct elf_internal_linux_prpsinfo *info:obfd, note_data, note_size, info
+
# Find core file memory regions
M:int:find_memory_regions:find_memory_region_ftype func, void *data:func, data

@@ -1082,6 +1089,7 @@ struct agent_expr;
struct axs_value;
struct stap_parse_info;
struct ravenscar_arch_ops;
+struct elf_internal_linux_prpsinfo;

/* The architecture associated with the inferior through the
connection to the target.
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 836da62..c1ae539 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -32,6 +32,7 @@
#include "cli/cli-utils.h"
#include "arch-utils.h"
#include "gdb_obstack.h"
+#include "cli/cli-utils.h"

#include <ctype.h>

@@ -1153,6 +1154,181 @@ linux_corefile_thread_callback (struct thread_info *info, void *data)
return !args->note_data;
}

+/* Fill the PRPSINFO structure with information about the process being
+ debugged. Returns 1 in case of success, 0 for failures. Please note that
+ even if the structure cannot be entirely filled (e.g., GDB was unable to
+ gather information about the process UID/GID), this function will still
+ return 1 since some information was already recorded. It will only return
+ 0 iff nothing can be gathered. */
+
+static int
+linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
+{
+ /* The filename which we will use to obtain some info about the process.
+ We will basically use this to store the `/proc/PID/FILENAME' file. */
+ char filename[100];
+ /* The full name of the program which generated the corefile. */
+ char *fname;
+ /* The basename of the executable. */
+ const char *basename;
+ /* The arguments of the program. */
+ char *psargs;
+ char *infargs;
+ /* The contents of `/proc/PID/stat' file. */
+ char *proc_stat;
+ /* The valid states of a process, according to the Linux kernel. */
+ const char valid_states[] = "RSDTZW";
+ /* The program state. */
+ const char *prog_state;
+ /* The state of the process. */
+ char pr_sname;
+ /* The PID of the program which generated the corefile. */
+ pid_t pid;
+ /* Process flags. */
+ unsigned int pr_flag;
+ /* Process nice value. */
+ long pr_nice;
+ /* The stat of the `/proc/PID/stat' file. */
+ struct stat proc_st;
+ /* The number of fields read by `sscanf'. */
+ int n_fields = 0;
+ /* Cleanups. */
+ struct cleanup *c;
+ int i;
+
+ gdb_assert (p != NULL);
+
+ /* Obtaining PID and filename. */
+ pid = ptid_get_pid (inferior_ptid);
+ xsnprintf (filename, sizeof (filename), "/proc/%u/cmdline", pid);
+ fname = target_fileio_read_stralloc (filename);
+
+ if (fname == NULL || *fname == '\0')
+ {
+ /* No program name was read, so we won't be able to retrieve more
+ information about the process. */
+ xfree (fname);
+ return 0;
+ }
+
+ c = make_cleanup (xfree, fname);
+ memset (p, 0, sizeof (*p));
+
+ /* Obtaining the file stat as well. */
+ if (stat (filename, &proc_st) != 0)
+ {
+ warning (_("Could not stat file `%s': %s"), filename,
+ safe_strerror (errno));
+ p->pr_uid = 0;
+ p->pr_gid = 0;
+ }
+ else
+ {
+ p->pr_uid = proc_st.st_uid;
+ p->pr_gid = proc_st.st_gid;
+ }
+
+ /* Defining the PID. */
+ p->pr_pid = pid;
+
+ /* Copying the program name. Only the basename matters. */
+ basename = lbasename (fname);
+ strncpy (p->pr_fname, basename, sizeof (p->pr_fname));
+ p->pr_fname[sizeof (p->pr_fname) - 1] = '\0';
+
+ infargs = get_inferior_args ();
+
+ psargs = xstrdup (fname);
+ if (infargs != NULL)
+ psargs = reconcat (psargs, psargs, " ", infargs, NULL);
+
+ make_cleanup (xfree, psargs);
+
+ strncpy (p->pr_psargs, psargs, sizeof (p->pr_psargs));
+ p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0';
+
+ xsnprintf (filename, sizeof (filename), "/proc/%d/stat", (int) pid);
+ proc_stat = target_fileio_read_stralloc (filename);
+ make_cleanup (xfree, proc_stat);
+
+ if (proc_stat == NULL || *proc_stat == '\0')
+ {
+ /* Despite being unable to read more information about the
+ process, we return 1 here because at least we have its
+ command line, PID and arguments. */
+ do_cleanups (c);
+ return 1;
+ }
+
+ /* Ok, we have the stats. It's time to do a little parsing of the
+ contents of the buffer, so that we end up reading what we want.
+
+ The following parsing mechanism is strongly based on the
+ information generated by the `fs/proc/array.c' file, present in
+ the Linux kernel tree. More details about how the information is
+ displayed can be obtained by seeing the manpage of proc(5),
+ specifically under the entry of `/proc/[pid]/stat'. */
+
+ /* Getting rid of the PID, since we already have it. */
+ while (isdigit (*proc_stat))
+ ++proc_stat;
+
+ proc_stat = skip_spaces (proc_stat);
+
+ /* Getting rid of the executable name, since we already have it. We
+ know that this name will be in parentheses, so we can safely look
+ for the close-paren. */
+ while (*proc_stat != ')')
+ ++proc_stat;
+ ++proc_stat;
+
+ proc_stat = skip_spaces (proc_stat);
+
+ n_fields = sscanf (proc_stat,
+ "%c" /* Process state. */
+ "%d%d%d" /* Parent PID, group ID, session ID. */
+ "%*d%*d" /* tty_nr, tpgid (not used). */
+ "%u" /* Flags. */
+ "%*s%*s%*s%*s" /* minflt, cminflt, majflt,
+ cmajflt (not used). */
+ "%*s%*s%*s%*s" /* utime, stime, cutime,
+ cstime (not used). */
+ "%*s" /* Priority (not used). */
+ "%ld", /* Nice. */
+ &pr_sname,
+ &p->pr_ppid, &p->pr_pgrp, &p->pr_sid,
+ &pr_flag,
+ &pr_nice);
+
+ if (n_fields != 6)
+ {
+ /* Again, we couldn't read the complementary information about
+ the process state. However, we already have minimal
+ information, so we just return 1 here. */
+ do_cleanups (c);
+ return 1;
+ }
+
+ /* Filling the structure fields. */
+ prog_state = strchr (valid_states, pr_sname);
+ if (prog_state != NULL)
+ p->pr_state = prog_state - valid_states;
+ else
+ {
+ /* Zero means "Running". */
+ p->pr_state = 0;
+ }
+
+ p->pr_sname = p->pr_state > 5 ? '.' : pr_sname;
+ p->pr_zomb = p->pr_sname == 'Z';
+ p->pr_nice = pr_nice;
+ p->pr_flag = pr_flag;
+
+ do_cleanups (c);
+
+ return 1;
+}
+
/* Fills the "to_make_corefile_note" target vector. Builds the note
section for a corefile, and returns it in a malloc buffer. */

@@ -1161,26 +1337,30 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size,
linux_collect_thread_registers_ftype collect)
{
struct linux_corefile_thread_data thread_args;
+ struct elf_internal_linux_prpsinfo prpsinfo;
char *note_data = NULL;
gdb_byte *auxv;
int auxv_len;

- /* Process information. */
- if (get_exec_file (0))
+ if (linux_fill_prpsinfo (&prpsinfo))
{
- const char *fname = lbasename (get_exec_file (0));
- char *psargs = xstrdup (fname);
-
- if (get_inferior_args ())
- psargs = reconcat (psargs, psargs, " ", get_inferior_args (),
- (char *) NULL);
-
- note_data = elfcore_write_prpsinfo (obfd, note_data, note_size,
- fname, psargs);
- xfree (psargs);
-
- if (!note_data)
- return NULL;
+ if (gdbarch_elfcore_write_linux_prpsinfo_p (gdbarch))
+ {
+ note_data = gdbarch_elfcore_write_linux_prpsinfo (gdbarch, obfd,
+ note_data, note_size,
+ &prpsinfo);
+ }
+ else
+ {
+ if (gdbarch_ptr_bit (gdbarch) == 64)
+ note_data = elfcore_write_linux_prpsinfo64 (obfd,
+ note_data, note_size,
+ &prpsinfo);
+ else
+ note_data = elfcore_write_linux_prpsinfo32 (obfd,
+ note_data, note_size,
+ &prpsinfo);
+ }
}

/* Thread register information. */
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index 135dc75..224d76b 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -56,6 +56,7 @@
#include "parser-defs.h"
#include "user-regs.h"
#include <ctype.h>
+#include "elf-bfd.h" /* for elfcore_write_* */

#include "features/rs6000/powerpc-32l.c"
#include "features/rs6000/powerpc-altivec32l.c"
@@ -1777,6 +1778,13 @@ ppc_linux_init_abi (struct gdbarch_info info,
set_gdbarch_core_regset_sections (gdbarch,
ppc64_linux_fp_regset_sections);
}
+
+ /* PPC32 uses a different prpsinfo32 compared to most other Linux
+ archs. */
+ if (tdep->wordsize == 4)
+ set_gdbarch_elfcore_write_linux_prpsinfo (gdbarch,
+ elfcore_write_ppc_linux_prpsinfo32);
+
set_gdbarch_regset_from_core_section (gdbarch,
ppc_linux_regset_from_core_section);
set_gdbarch_core_read_description (gdbarch, ppc_linux_core_read_description);