This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]