RFC: "info proc map" for corefiles

Sérgio Durigan Júnior
2008-12-18T17:19:12Z

[-- Attachment #1: Type: text/plain, Size: 2743 bytes --]

Hi guys and girls,

This little patch o' mine implements the command "info proc map" for
corefiles in GDB. Initially, this feature is only provided for live
process being debugged. We decided that it would be good to have such
functionality for corefiles as well, since this information (the memory
mappings) is available inside them.

This was something that Thiago and I have discussed for some time, and
it took a little while for me to figure out how things worked in this
part of the code. I really don't know if this is the right way to
implement this, so I'd be glad if you could take a look and give your
opinions.

It's not architecture-dependend so this time you'll be able to test
without problems. Comments and reviews are welcome, as always.

This is a little example of this feature working:

sergio@miki ~/work/gdb/build-32/gdb $ ./gdb /tmp/teste core.13754
GNU gdb (GDB) 6.8.50.20081218-cvs
...
(gdb) info proc map
exe = '/tmp/teste'
Mapped address spaces:

Start Addr End Addr Size objfile
0x8048000 0x8049000 0x1000 /tmp/teste
0x8049000 0x804a000 0x1000 /tmp/teste
0x804a000 0x804b000 0x1000 /tmp/teste
0xb7e93000 0xb7e94000 0x1000
0xb7e94000 0xb7fbe000 0x12a000
0xb7fbe000 0xb7fc0000 0x2000
0xb7fc0000 0xb7fc1000 0x1000
0xb7fc1000 0xb7fc5000 0x4000
0xb7fe4000 0xb7ffe000 0x1a000 /lib/ld-linux.so.2
0xb7ffe000 0xb7fff000 0x1000 /lib/ld-linux.so.2
0xb7fff000 0xb8000000 0x1000 /lib/ld-linux.so.2
0xbffeb000 0xc0000000 0x15000
0xffffe000 0xfffff000 0x1000 system-supplied DSO at
0xffffe000

The corefile used here (core.13754) was generated using the "gcore"
command inside GDB.

Regards,

--
SÃ©rgio Durigan JÃºnior
Linux on Power Toolchain - Software Engineer
Linux Technology Center - LTC
IBM Brazil

2008-12-18 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>

* corelow.c: Including extra header files in order to be able
to use proper data structures for the "info proc map".
(print_proc_map_iter): New function.
(core_print_proc_map): Likewise.
(init_core_ops): Initializing to_print_proc_map method for
corefile target_ops.
* linux-nat.c (linux_nat_print_proc_map): New function.
(linux_nat_info_proc_cmd): Handling the "info proc map"
for corefiles.
(linux_nat_add_target): Initializing to_print_proc_map method
for Linux target_ops.
* target.c (update_current_target): Inherit to_print_proc_map.
(dummy_print_proc_map): New function.
(init_dummy_target): Initializing to_print_proc_map method
for dummy target_ops.
* target.h (struct target_ops): Adding method to_print_proc_map.
(target_print_proc_map): New define.

[-- Attachment #2: corefile.patch --]
[-- Type: text/x-patch, Size: 14933 bytes --]

diff --git a/gdb/corelow.c b/gdb/corelow.c
index 35c998c..c9b22e5 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -33,6 +33,8 @@
#include "symtab.h"
#include "command.h"
#include "bfd.h"
+#include "elf-bfd.h"
+#include "elf/internal.h"
#include "target.h"
#include "gdbcore.h"
#include "gdbthread.h"
@@ -45,6 +47,7 @@
#include "exceptions.h"
#include "solib.h"
#include "filenames.h"
+#include "objfiles.h"

#ifndef O_LARGEFILE
@@ -692,6 +695,161 @@ core_pid_to_str (ptid_t ptid)
return buf;
}

+/* Helper function for 'core_print_proc_map'. It is used to iterate
+ over the corefile's sections and print proper information about
+ memory-mappings.
+
+ BFD is the bfd used to get the sections.
+ SECT is the current section being "visited".
+ OBJ is not used. */
+
+static void
+print_proc_map_iter (bfd *bfd, asection *sect, void *obj)
+{
+ /* We're interested in matching sections' names beginning with
+ "load", because they are the sections containing information
+ about the process' memory regions. */
+ static const char *proc_map_match = "load";
+ int proc_map_match_size = strlen (proc_map_match);
+ /* A little flag to indicate whether we have found something. */
+ int found = 0;
+ /* The section's size. */
+ bfd_size_type secsize;
+ /* We have to know the bitness of this architecture. */
+ struct gdbarch *gdbarch = gdbarch_from_bfd (bfd);
+ int bitness = gdbarch_addr_bit (gdbarch);
+ /* We'll use these later. They are basically used for iterating
+ over every objfile in the system so that we can find needed
+ information about the memory region being examinated. */
+ struct obj_section *s = NULL;
+ struct objfile *objfile = NULL;
+ /* Fields to be printed for the proc map. */
+ unsigned long start = 0, end = 0;
+ unsigned int size = 0;
+ char *filename = NULL;
+
+ if (strncmp (proc_map_match, sect->name, proc_map_match_size) != 0)
+ /* This section is not useful. */
+ return;
+
+ /* Unfortunately, some sections in the corefile don't have any
+ content inside. This is bad because we need to print, among
+ other things, its final address in the memory (which is
+ impossible to know if we don't have a size). That's why we
+ first need to check if the section's got anything inside it. */
+ secsize = bfd_section_size (bfd, sect);
+
+ if (secsize == 0)
+ {
+ /* Ok, the section if empty. In this case, we must look inside
+ ELF's Program Header, because (at least) there we have
+ information about the section's size. That's what we're doing
+ here. */
+ Elf_Internal_Phdr *p = elf_tdata (bfd)->phdr;
+ if (p != NULL)
+ {
+ int i;
+ unsigned int n = elf_elfheader (bfd)->e_phnum;
+ for (i = 0; i < n; i++, p++)
+ /* For each entry in the Program Header, we have to
+ check if the section's initial address is equal to
+ the entry's virtual address. If it is, then we
+ have just found the section's entry in the Program
+ Header, and can use the entry's information to
+ complete missing data from the section. */
+ if (sect->vma == p->p_vaddr)
+ {
+ found = 1;
+ break;
+ }
+ if (found)
+ secsize = p->p_memsz;
+ }
+ }
+
+ size = secsize;
+ start = sect->vma;
+ end = (unsigned long) (sect->vma + size);
+
+ /* Now begins a new part of the work. We still don't have complete
+ information about the memory region. For example, we still need
+ to know the filename which is represented by the region. Such
+ info can be gathered from the objfile's data structure, and for
+ that we must iterate over all the objsections and check if the
+ objsection's initial address is inside the section we have at hand.
+ If it is, then we can use this specific objsection to obtain the
+ missing data. */
+ found = 0;
+ ALL_OBJSECTIONS (objfile, s)
+ if (obj_section_addr (s) >= start
+ && obj_section_addr (s) <= end)
+ {
+ found = 1;
+ goto out;
+ }
+
+out:
+ if (found)
+ filename = s->objfile->name;
+
+ if (bitness == 32)
+ {
+ printf_filtered ("\t%#10lx %#10lx %#10x %7s\n",
+ start,
+ end,
+ (int) size,
+ (filename != NULL) ? filename : "");
+ }
+ else
+ {
+ printf_filtered (" %#18lx %#18lx %#10x %7s\n",
+ start,
+ end,
+ (int) size,
+ (filename != NULL) ? filename : "");
+ }
+}
+
+/* Implements the "info proc map" command when the user has provided
+ a corefile. In this case, the PID argument will not be used. */
+
+static void
+core_print_proc_map (long long pid)
+{
+ struct gdbarch *gdbarch;
+ const char *exe;
+ int bitness;
+
+ gdb_assert (core_bfd != NULL);
+
+ gdbarch = gdbarch_from_bfd (core_bfd);
+ bitness = gdbarch_addr_bit (gdbarch);
+
+ /* Getting the executable name. */
+ exe = bfd_core_file_failing_command (core_bfd);
+
+ printf_filtered (_("exe = '%s'\n"), exe);
+ printf_filtered (_("Mapped address spaces:\n\n"));
+ if (bitness == 32)
+ {
+ printf_filtered ("\t%10s %10s %10s %7s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", "objfile");
+ }
+ else
+ {
+ printf_filtered (" %18s %18s %10s %7s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", "objfile");
+ }
+
+ bfd_map_over_sections (core_bfd,
+ print_proc_map_iter,
+ NULL);
+}
+
/* Fill in core_ops with its defined operations and properties. */

static void
@@ -714,6 +872,7 @@ init_core_ops (void)
core_ops.to_create_inferior = find_default_create_inferior;
core_ops.to_thread_alive = core_file_thread_alive;
core_ops.to_read_description = core_read_description;
+ core_ops.to_print_proc_map = core_print_proc_map;
core_ops.to_pid_to_str = core_pid_to_str;
core_ops.to_stratum = core_stratum;
core_ops.to_has_memory = 1;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index a2cb39d..2e3fcbc 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -3586,6 +3586,75 @@ linux_nat_make_corefile_notes (bfd *obfd, int *note_size)
return note_data;
}

+/* Implement the "info proc map" command when the process
+ being debugged is running (i.e., a corefile is not being
+ used. */
+static void
+linux_nat_print_proc_map (long long pid)
+{
+ char fname1[MAXPATHLEN];
+ FILE *procfile;
+
+ sprintf (fname1, "/proc/%lld/maps", pid);
+ if ((procfile = fopen (fname1, "r")) != NULL)
+ {
+ long long addr, endaddr, size, offset, inode;
+ char permissions[8], device[8], filename[MAXPATHLEN];
+ struct cleanup *cleanup;
+
+ cleanup = make_cleanup_fclose (procfile);
+ printf_filtered (_("Mapped address spaces:\n\n"));
+ if (gdbarch_addr_bit (current_gdbarch) == 32)
+ {
+ printf_filtered ("\t%10s %10s %10s %10s %7s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", " Offset", "objfile");
+ }
+ else
+ {
+ printf_filtered (" %18s %18s %10s %10s %7s\n",
+ "Start Addr",
+ " End Addr",
+ " Size", " Offset", "objfile");
+ }
+
+ while (read_mapping (procfile, &addr, &endaddr, &permissions[0],
+ &offset, &device[0], &inode, &filename[0]))
+ {
+ size = endaddr - addr;
+
+ /* FIXME: carlton/2003-08-27: Maybe the printf_filtered
+ calls here (and possibly above) should be abstracted
+ out into their own functions? Andrew suggests using
+ a generic local_address_string instead to print out
+ the addresses; that makes sense to me, too. */
+
+ if (gdbarch_addr_bit (current_gdbarch) == 32)
+ {
+ printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n",
+ (unsigned long) addr, /* FIXME: pr_addr */
+ (unsigned long) endaddr,
+ (int) size,
+ (unsigned int) offset,
+ filename[0] ? filename : "");
+ }
+ else
+ {
+ printf_filtered (" %#18lx %#18lx %#10x %#10x %7s\n",
+ (unsigned long) addr, /* FIXME: pr_addr */
+ (unsigned long) endaddr,
+ (int) size,
+ (unsigned int) offset,
+ filename[0] ? filename : "");
+ }
+ }
+ do_cleanups (cleanup);
+ }
+ else
+ warning (_("unable to open /proc file '%s'"), fname1);
+}
+
/* Implement the "info proc" command. */

static void
@@ -3621,6 +3690,12 @@ linux_nat_info_proc_cmd (char *args, int from_tty)
else if (strncmp (argv[0], "mappings", strlen (argv[0])) == 0)
{
mappings_f = 1;
+ if (current_target.to_stratum == core_stratum)
+ {
+ cmdline_f = 0;
+ cwd_f = 0;
+ exe_f = 0;
+ }
}
else if (strcmp (argv[0], "status") == 0)
{
@@ -3652,14 +3727,17 @@ linux_nat_info_proc_cmd (char *args, int from_tty)
}
argv++;
}
- if (pid == 0)
- error (_("No current process: you must name one."));
+ if (current_target.to_stratum != core_stratum)
+ {
+ if (pid == 0)
+ error (_("No current process: you must name one."));

- sprintf (fname1, "/proc/%lld", pid);
- if (stat (fname1, &dummy) != 0)
- error (_("No /proc directory: '%s'"), fname1);
+ sprintf (fname1, "/proc/%lld", pid);
+ if (stat (fname1, &dummy) != 0)
+ error (_("No /proc directory: '%s'"), fname1);

- printf_filtered (_("process %lld\n"), pid);
+ printf_filtered (_("process %lld\n"), pid);
+ }
if (cmdline_f || all)
{
sprintf (fname1, "/proc/%lld/cmdline", pid);
@@ -3693,65 +3771,7 @@ linux_nat_info_proc_cmd (char *args, int from_tty)
}
if (mappings_f || all)
{
- sprintf (fname1, "/proc/%lld/maps", pid);
- if ((procfile = fopen (fname1, "r")) != NULL)
- {
- long long addr, endaddr, size, offset, inode;
- char permissions[8], device[8], filename[MAXPATHLEN];
- struct cleanup *cleanup;
-
- cleanup = make_cleanup_fclose (procfile);
- printf_filtered (_("Mapped address spaces:\n\n"));
- if (gdbarch_addr_bit (current_gdbarch) == 32)
- {
- printf_filtered ("\t%10s %10s %10s %10s %7s\n",
- "Start Addr",
- " End Addr",
- " Size", " Offset", "objfile");
- }
- else
- {
- printf_filtered (" %18s %18s %10s %10s %7s\n",
- "Start Addr",
- " End Addr",
- " Size", " Offset", "objfile");
- }
-
- while (read_mapping (procfile, &addr, &endaddr, &permissions[0],
- &offset, &device[0], &inode, &filename[0]))
- {
- size = endaddr - addr;
-
- /* FIXME: carlton/2003-08-27: Maybe the printf_filtered
- calls here (and possibly above) should be abstracted
- out into their own functions? Andrew suggests using
- a generic local_address_string instead to print out
- the addresses; that makes sense to me, too. */
-
- if (gdbarch_addr_bit (current_gdbarch) == 32)
- {
- printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n",
- (unsigned long) addr, /* FIXME: pr_addr */
- (unsigned long) endaddr,
- (int) size,
- (unsigned int) offset,
- filename[0] ? filename : "");
- }
- else
- {
- printf_filtered (" %#18lx %#18lx %#10x %#10x %7s\n",
- (unsigned long) addr, /* FIXME: pr_addr */
- (unsigned long) endaddr,
- (int) size,
- (unsigned int) offset,
- filename[0] ? filename : "");
- }
- }
-
- do_cleanups (cleanup);
- }
- else
- warning (_("unable to open /proc file '%s'"), fname1);
+ target_print_proc_map (pid);
}
if (status_f || all)
{
@@ -4620,6 +4640,7 @@ linux_nat_add_target (struct target_ops *t)
t->to_async_mask = linux_nat_async_mask;
t->to_terminal_inferior = linux_nat_terminal_inferior;
t->to_terminal_ours = linux_nat_terminal_ours;
+ t->to_print_proc_map = linux_nat_print_proc_map;

/* Methods for non-stop support. */
t->to_stop = linux_nat_stop;
diff --git a/gdb/target.c b/gdb/target.c
index c16b55c..ad1a611 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -484,6 +484,7 @@ update_current_target (void)
INHERIT (to_async_mask, t);
INHERIT (to_find_memory_regions, t);
INHERIT (to_make_corefile_notes, t);
+ INHERIT (to_print_proc_map, t);
INHERIT (to_get_thread_local_address, t);
INHERIT (to_can_execute_reverse, t);
/* Do not inherit to_read_description. */
@@ -2501,6 +2502,13 @@ static char * dummy_make_corefile_notes (bfd *ignore1, int *ignore2)
return NULL;
}

+/* Error-catcher for target_print_proc_map. */
+static void
+dummy_print_proc_map (long long pid)
+{
+ error (_("No target."));
+}
+
/* Set up the handful of non-empty slots needed by the dummy target
vector. */

@@ -2521,6 +2529,7 @@ init_dummy_target (void)
dummy_target.to_stratum = dummy_stratum;
dummy_target.to_find_memory_regions = dummy_find_memory_regions;
dummy_target.to_make_corefile_notes = dummy_make_corefile_notes;
+ dummy_target.to_print_proc_map = dummy_print_proc_map;
dummy_target.to_xfer_partial = default_xfer_partial;
dummy_target.to_magic = OPS_MAGIC;
}
diff --git a/gdb/target.h b/gdb/target.h
index 2722945..c763da5 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -428,6 +428,7 @@ struct target_ops
void *),
void *);
char * (*to_make_corefile_notes) (bfd *, int *);
+ void (*to_print_proc_map) (long long);

/* Return the thread-local address at OFFSET in the
thread-local storage for the thread PTID and the shared library
@@ -1057,6 +1058,12 @@ extern char *normal_pid_to_str (ptid_t ptid);
#define target_make_corefile_notes(BFD, SIZE_P) \
(current_target.to_make_corefile_notes) (BFD, SIZE_P)

+/* Print the memory-mapped information from the inferior
+ (or the corefile). */
+
+#define target_print_proc_map(pid) \
+ (current_target.to_print_proc_map) (pid)
+
/* Thread-local values. */
#define target_get_thread_local_address \
(current_target.to_get_thread_local_address)