summaryrefslogtreecommitdiff
path: root/gdb/linux-proc.c
diff options
context:
space:
mode:
authorMichael Snyder <msnyder@specifix.com>2002-01-09 00:37:02 +0000
committerMichael Snyder <msnyder@specifix.com>2002-01-09 00:37:02 +0000
commit86e4f2f2510485128b796ca02e5c7bed52fc829d (patch)
tree3133c90944d240fd985e92c95f13de28aa32b541 /gdb/linux-proc.c
parent0cfbc379cb7150f7f1fa195ab5b99b00acffd059 (diff)
downloadgdb-86e4f2f2510485128b796ca02e5c7bed52fc829d.tar.gz
2002-01-03 Michael Snyder <msnyder@redhat.com>
Implement a "generate-core-file" command in gdb, save target state. * gcore.c: New file. Implement new command 'generate-core-file'. Save a corefile image of the current state of the inferior. * linux-proc.c: Add linux-specific code for saving corefiles. * target.h (struct target_ops): Add new target vectors for saving corefiles; to_find_memory_regions and to_make_corefile_notes. (target_find_memory_regions): New macro. (target_make_corefile_notes): New macro. * target.c (update_current_target): Inherit new target methods. (dummy_find_memory_regions): New place-holder method. (dummy_make_corefile_notes): New place-holder method. (init_dummy_target): Initialize new dummy target vectors. * exec.c (exec_set_find_memory_regions): New function. Allow the exec_ops vector for memory regions to be taken over. (exec_make_note_section): New function, target vector method. * defs.h (exec_set_find_memory_regions): Export prototype. * procfs.c (proc_find_memory_regions): New function, corefile method. (procfs_make_note_section): New function, corefile method. (init_procfs_ops): Set new target vector pointers. (find_memory_regions_callback): New function. (procfs_do_thread_registers): New function. (procfs_corefile_thread_callback): New function. * sol-thread.c (sol_find_memory_regions): New function. (sol_make_note_section): New function. (init_sol_thread_ops): Initialize new target vectors. * inftarg.c (inftarg_set_find_memory_regions): New function. Allow to_find_memory_regions vector to be taken over. (inftarg_set_make_corefile_notes): New function. Allow to_make_corefile_notes vector to be taken over. * thread-db.c (thread_db_new_objfile): Don't activate thread-db interface layer if not target_has_execution (may be a corefile). * config/i386/linux.mh: Add gcore.o to NATDEPFILES. * config/sparc/sun4sol2.mh: Ditto. * config/alpha/alpha-linux.mh: Ditto. * config/arm/linux.mh: Ditto. * config/i386/x86-64linux.mh: Ditto. * config/ia64/linux.mh: Ditto. * config/m68k/linux.mh: Ditto. * config/mips/linux.mh: Ditto. * config/powerpc/linux.mh: Ditto. * config/sparc/linux.mh: Ditto.
Diffstat (limited to 'gdb/linux-proc.c')
-rw-r--r--gdb/linux-proc.c243
1 files changed, 241 insertions, 2 deletions
diff --git a/gdb/linux-proc.c b/gdb/linux-proc.c
index 4e259230f93..4d48b8d16ea 100644
--- a/gdb/linux-proc.c
+++ b/gdb/linux-proc.c
@@ -1,5 +1,5 @@
-/* Generate a core file for the inferior process -- Linux version.
- Copyright 2001 Free Software Foundation, Inc.
+/* Linux-specific methods for using the /proc file system.
+ Copyright 2001, 2002 Free Software Foundation, Inc.
This file is part of GDB.
@@ -19,7 +19,20 @@
Boston, MA 02111-1307, USA. */
#include "defs.h"
+#include "inferior.h"
#include <sys/param.h> /* for MAXPATHLEN */
+#include <sys/procfs.h>
+#include "gregset.h" /* for gregset */
+#include "gdbcore.h" /* for get_exec_file */
+#include "gdbthread.h" /* for struct thread_info etc. */
+#include "elf-bfd.h"
+
+/* Function: child_pid_to_exec_file
+ *
+ * Accepts an integer pid
+ * Returns a string representing a file that can be opened
+ * to get the symbols for the child process.
+ */
char *
child_pid_to_exec_file (int pid)
@@ -30,3 +43,229 @@ child_pid_to_exec_file (int pid)
/* FIXME use readlink to get the real name. */
return fname;
}
+
+/* Function: linux_find_memory_regions
+ *
+ * Fills the "to_find_memory_regions" target vector.
+ * Lists the memory regions in the inferior for a corefile.
+ */
+
+static int
+linux_find_memory_regions (int (*func) (CORE_ADDR,
+ unsigned long,
+ int, int, int,
+ void *),
+ void *obfd)
+{
+ long long pid = PIDGET (inferior_ptid);
+ char procfilename[MAXPATHLEN];
+ FILE *procfile;
+ long long addr, endaddr, size, offset, inode;
+ char perms[8], dev[8], filename[MAXPATHLEN];
+ int read, write, exec;
+ int ret;
+
+ /* Compose the filename for the /proc memory map, and open it. */
+ sprintf (procfilename, "/proc/%lld/maps", pid);
+ if ((procfile = fopen (procfilename, "r")) == NULL)
+ error ("Could not open %s\n", procfilename);
+
+ if (info_verbose)
+ fprintf_filtered (gdb_stdout,
+ "Reading memory regions from %s\n", procfilename);
+
+ /* Read the first memory segment descriptor from the maps file. */
+ ret = fscanf (procfile, "%llx-%llx %s %llx %s %llx",
+ &addr, &endaddr, perms, &offset, dev, &inode);
+ if (inode)
+ fscanf (procfile, " %s\n", filename);
+ else
+ {
+ filename[0] = '\0';
+ fscanf (procfile, "\n");
+ }
+
+ /* Now iterate until end-of-file. */
+ while (ret > 0 && ret != EOF)
+ {
+ size = endaddr - addr;
+
+ /* Get the segment's permissions. */
+ read = (strchr (perms, 'r') != 0);
+ write = (strchr (perms, 'w') != 0);
+ exec = (strchr (perms, 'x') != 0);
+
+ if (info_verbose)
+ {
+ fprintf_filtered (gdb_stdout,
+ "Save segment, %lld bytes at 0x%s (%c%c%c)",
+ size, paddr_nz (addr),
+ read ? 'r' : ' ',
+ write ? 'w' : ' ',
+ exec ? 'x' : ' ');
+ if (filename && filename[0])
+ fprintf_filtered (gdb_stdout,
+ " for %s", filename);
+ fprintf_filtered (gdb_stdout, "\n");
+ }
+
+ /* Invoke the callback function to create the corefile segment. */
+ func (addr, size, read, write, exec, obfd);
+
+ /* Read the next memory region. */
+ filename[0] = '\0';
+ ret = fscanf (procfile, "%llx-%llx %s %llx %s %llx",
+ &addr, &endaddr, perms, &offset, dev, &inode);
+ if (inode)
+ fscanf (procfile, " %s\n", filename);
+ else
+ {
+ filename[0] = '\0';
+ fscanf (procfile, "\n");
+ }
+ }
+
+ fclose (procfile);
+ return 0;
+}
+
+/* Function: linux_do_thread_registers
+ *
+ * Records the thread's register state for the corefile note section.
+ */
+
+static char *
+linux_do_thread_registers (bfd *obfd, ptid_t ptid,
+ char *note_data, int *note_size)
+{
+ gdb_gregset_t gregs;
+ gdb_fpregset_t fpregs;
+#ifdef HAVE_PTRACE_GETFPXREGS
+ gdb_fpxregset_t fpxregs;
+#endif
+ unsigned long merged_pid = ptid_get_tid (ptid) << 16 | ptid_get_pid (ptid);
+
+ fill_gregset (&gregs, -1);
+ note_data = (char *) elfcore_write_prstatus (obfd,
+ note_data,
+ note_size,
+ merged_pid,
+ stop_signal,
+ &gregs);
+
+ fill_fpregset (&fpregs, -1);
+ note_data = (char *) elfcore_write_prfpreg (obfd,
+ note_data,
+ note_size,
+ &fpregs,
+ sizeof (fpregs));
+#ifdef HAVE_PTRACE_GETFPXREGS
+ fill_fpxregset (&fpxregs, -1);
+ note_data = (char *) elfcore_write_prxfpreg (obfd,
+ note_data,
+ note_size,
+ &fpxregs,
+ sizeof (fpxregs));
+#endif
+ return note_data;
+}
+
+struct linux_corefile_thread_data {
+ bfd *obfd;
+ char *note_data;
+ int *note_size;
+};
+
+/* Function: linux_corefile_thread_callback
+ *
+ * Called by gdbthread.c once per thread.
+ * Records the thread's register state for the corefile note section.
+ */
+
+static int
+linux_corefile_thread_callback (struct thread_info *ti, void *data)
+{
+ struct linux_corefile_thread_data *args = data;
+ ptid_t saved_ptid = inferior_ptid;
+
+ inferior_ptid = ti->ptid;
+ registers_changed ();
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ args->note_data = linux_do_thread_registers (args->obfd,
+ ti->ptid,
+ args->note_data,
+ args->note_size);
+ inferior_ptid = saved_ptid;
+ registers_changed ();
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ return 0;
+}
+
+/* Function: linux_make_note_section
+ *
+ * Fills the "to_make_corefile_note" target vector.
+ * Builds the note section for a corefile, and returns it
+ * in a malloc buffer.
+ */
+
+static char *
+linux_make_note_section (bfd *obfd, int *note_size)
+{
+ struct linux_corefile_thread_data thread_args;
+ struct cleanup *old_chain;
+ char fname[16] = {'\0'};
+ char psargs[80] = {'\0'};
+ char *note_data = NULL;
+ ptid_t current_ptid = inferior_ptid;
+
+ if (get_exec_file (0))
+ {
+ strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname));
+ strncpy (psargs, get_exec_file (0),
+ sizeof (psargs));
+ if (get_inferior_args ())
+ {
+ strncat (psargs, " ",
+ sizeof (psargs) - strlen (psargs));
+ strncat (psargs, get_inferior_args (),
+ sizeof (psargs) - strlen (psargs));
+ }
+ note_data = (char *) elfcore_write_prpsinfo (obfd,
+ note_data,
+ note_size,
+ fname,
+ psargs);
+ }
+
+ /* Dump information for threads. */
+ thread_args.obfd = obfd;
+ thread_args.note_data = note_data;
+ thread_args.note_size = note_size;
+ iterate_over_threads (linux_corefile_thread_callback, &thread_args);
+ if (thread_args.note_data == note_data)
+ {
+ /* iterate_over_threads didn't come up with any threads;
+ just use inferior_ptid. */
+ note_data = linux_do_thread_registers (obfd, inferior_ptid,
+ note_data, note_size);
+ }
+ else
+ {
+ note_data = thread_args.note_data;
+ }
+
+ make_cleanup (xfree, note_data);
+ return note_data;
+}
+
+void
+_initialize_linux_proc (void)
+{
+ extern void inftarg_set_find_memory_regions ();
+ extern void inftarg_set_make_corefile_notes ();
+
+ inftarg_set_find_memory_regions (linux_find_memory_regions);
+ inftarg_set_make_corefile_notes (linux_make_note_section);
+}