This is the mail archive of the binutils@sourceware.org mailing list for the binutils 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] Fix objdump -S with unit-at-a-time


Hallo,

I finally got tired of objdump -S not working anymore with -funit-at-a-time 
which is default in newer gccs. The problem was that the old code
can only print source files linearly with increasing line numbers,
but the unit-at-a-time gcc tends to output the functions in a different order. 
This means typically only the first function was dumped and then nothing.

This patch changes objdump to read all source files completely
(using mmap if available) and index their source lines and then
dump them in any order described by the debugging information.

This will use more memory than before, but that doesn't seem
to be an big issue anymore.

Now it just dumps a few lines context for each line that
is not directly following a previously dumped lines. This mean
it sometimes dump a little too much, but gives reasonable
context to make the output readable.

It is still not perfect (e.g. it might be possible to dump
less context in some cases with an additional map to remember which
lines were already dumped), but definitely much better than it was
before.

I also removed the old heuristics trying to handle #line type
files -- i couldn't get it to work well in the new scheme.

Since it doesn't use stdio anymore it has to deal with DOS 
line ending by itself. This has the minor benefit that DOS lined
files should work correctly even on Linux/Unix where the
stdio won't handle them.

I'm not 100% sure the autoconf mmap test tests the shared
file mmap case the way the code uses it, but read only
shared mmap should be relatively portable and if it 
fails it will fallback to malloc+read anyways

It uses strcspn() but since that's C89 I assume it's ok
for all platforms.

I bet there are PRs open for this too, but I was too lazy to search
for them.

-Andi

Against CVS mainline as of Jun 1 07, but the patch also works with 
some offsets against 2.6.17

2007-06-01  Andi Kleen <ak@suse.de>

	* objdump.c: Include sys/mman.h 
	(print_file_list): Remove f, add map, mapsize, linemap, maxline,
	last_line, first fields.
	(slurp_file): Add.
	(line_map_decrease): Add.
	(index_file): Add 
	(print_file_open): Call slurp_file and index_file. Initialize new
	fields.
	(skip_to_line): Rename to print_line and write only single line.
	(dump_line): Add.
	(show_line): Change to new algorithm.

diff -u binutils/objdump.c-o binutils/objdump.c
--- binutils/objdump.c-o	2007-05-29 13:19:49.000000000 +0200
+++ binutils/objdump.c	2007-06-01 20:20:51.000000000 +0200
@@ -61,6 +61,10 @@
 #include "debug.h"
 #include "budbg.h"
 
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
 /* Internal headers for the ELF .stab-dump code - sorry.  */
 #define	BYTES_IN_WORD	32
 #include "aout/aout64.h"
@@ -927,8 +931,12 @@
   struct print_file_list *next;
   const char *filename;
   const char *modname;
-  unsigned int line;
-  FILE *f;
+  const char *map; 
+  size_t mapsize;
+  const char **linemap; 
+  unsigned maxline;
+  unsigned last_line;
+  int first;
 };
 
 static struct print_file_list *print_files;
@@ -938,6 +946,94 @@
 
 #define SHOW_PRECEDING_CONTEXT_LINES (5)
 
+/* Read a complete file into memory. */
+
+static const char *
+slurp_file(const char *fn, size_t *size)
+{
+#ifdef HAVE_MMAP
+  int ps = getpagesize ();
+  size_t msize;
+#endif
+  const char *map;
+  struct stat st;
+  int fd = open (fn, O_RDONLY);
+  if (fd < 0)
+    return NULL;
+  if (fstat (fd, &st) < 0)
+    return NULL;
+  *size = st.st_size;
+#ifdef HAVE_MMAP
+  msize = (*size + ps - 1) & ~(ps - 1);
+  map = mmap (NULL, msize, PROT_READ, MAP_SHARED, fd, 0);
+  if (map != (char*)-1L)
+    {
+      close(fd);
+      return map; 
+    }
+#endif
+  map = malloc(*size);
+  if (!map || (size_t)read (fd, (char *)map, *size) != *size) 
+    { 
+      free((void *)map);
+      map = NULL;
+    }
+  close(fd);
+  return map; 
+}
+
+#define line_map_decrease 5
+
+/* Precompute array of lines for a mapped file. */
+
+static const char ** 
+index_file (const char *map, size_t size, unsigned int *maxline) 
+{
+  const char *p, *lstart;;
+  int chars_per_line = 45; /* first iteration will use 40 */
+  unsigned int lineno;
+  const char **linemap = NULL; 
+  unsigned long line_map_size = 0;
+ 
+  lineno = 0;
+  lstart = map;
+  for (p = map; p < map + size - 1; p++) 
+    { 
+      if (*p == '\n') 
+	{ 
+	  if (p[1] == '\r') 
+	    p++;  
+	} 
+      else if (*p == '\r') 
+	{ 
+	  if (p[1] == '\n')
+	    p++;
+	}
+      else
+	continue;
+      
+      /* end of line found */
+
+      if (linemap == NULL || line_map_size < lineno + 1) 
+	{ 
+	  unsigned long newsize;
+
+	  chars_per_line -= line_map_decrease;
+	  if (chars_per_line <= 1)
+	    chars_per_line = 1;
+	  line_map_size = size / chars_per_line + 1;
+	  newsize = line_map_size * sizeof(char *);
+	  linemap = xrealloc (linemap, newsize);
+	}
+
+      linemap[lineno++] = lstart; 
+      lstart = p + 1; 
+    }
+  
+  *maxline = lineno; 
+  return linemap;
+}
+
 /* Tries to open MODNAME, and if successful adds a node to print_files
    linked list and returns that node.  Returns NULL on failure.  */
 
@@ -945,24 +1041,22 @@
 try_print_file_open (const char *origname, const char *modname)
 {
   struct print_file_list *p;
-  FILE *f;
 
-  f = fopen (modname, "r");
-  if (f == NULL)
-    return NULL;
+  p = xmalloc (sizeof (struct print_file_list));
 
-  if (print_files != NULL && print_files->f != NULL)
+  p->map = slurp_file (modname, &p->mapsize);
+  if (p->map == NULL)
     {
-      fclose (print_files->f);
-      print_files->f = NULL;
+      free(p);
+      return NULL;
     }
-
-  p = xmalloc (sizeof (struct print_file_list));
+  
+  p->linemap = index_file (p->map, p->mapsize, &p->maxline);
+  p->last_line = 0;
   p->filename = origname;
   p->modname = modname;
-  p->line = 0;
-  p->f = f;
   p->next = print_files;
+  p->first = 1;
   print_files = p;
   return p;
 }
@@ -1021,29 +1115,32 @@
   return NULL;
 }
 
-/* Skip ahead to a given line in a file, optionally printing each
-   line.  */
+/* Print a source file line */
 
-static void
-skip_to_line (struct print_file_list *p, unsigned int line,
-	      bfd_boolean show)
+static void 
+print_line (struct print_file_list *p, unsigned int line)
 {
-  while (p->line < line)
-    {
-      char buf[100];
-
-      if (fgets (buf, sizeof buf, p->f) == NULL)
-	{
-	  fclose (p->f);
-	  p->f = NULL;
-	  break;
-	}
+  const char *l;
+ 
+  --line; 
+  if (line >= p->maxline)
+    return;
+  l = p->linemap [line];
+  fwrite (l, 1, strcspn (l, "\n\r"), stdout);
+  putchar ('\n');
+} 
 
-      if (show)
-	printf ("%s", buf);
+/* Print a range of source code lines. */
 
-      if (strchr (buf, '\n') != NULL)
-	++p->line;
+static void
+dump_lines (struct print_file_list *p, unsigned int start, unsigned int end)
+{
+  if (p->map == NULL)
+    return;
+  while (start <= end) 
+    {
+      print_line (p, start);
+      start++;
     }
 }
 
@@ -1084,79 +1181,31 @@
       && line > 0)
     {
       struct print_file_list **pp, *p;
+      unsigned l;
 
       for (pp = &print_files; *pp != NULL; pp = &(*pp)->next)
 	if (strcmp ((*pp)->filename, filename) == 0)
 	  break;
       p = *pp;
 
-      if (p != NULL)
-	{
-	  if (p != print_files)
-	    {
-	      int l;
-
-	      /* We have reencountered a file name which we saw
-		 earlier.  This implies that either we are dumping out
-		 code from an included file, or the same file was
-		 linked in more than once.  There are two common cases
-		 of an included file: inline functions in a header
-		 file, and a bison or flex skeleton file.  In the
-		 former case we want to just start printing (but we
-		 back up a few lines to give context); in the latter
-		 case we want to continue from where we left off.  I
-		 can't think of a good way to distinguish the cases,
-		 so I used a heuristic based on the file name.  */
-	      if (strcmp (p->filename + strlen (p->filename) - 2, ".h") != 0)
-		l = p->line;
-	      else
-		{
-		  l = line - SHOW_PRECEDING_CONTEXT_LINES;
-		  if (l < 0)
-		    l = 0;
-		}
-
-	      if (p->f == NULL)
-		{
-		  p->f = fopen (p->modname, "r");
-		  p->line = 0;
-		}
-	      if (p->f != NULL)
-		skip_to_line (p, l, FALSE);
-
-	      if (print_files->f != NULL)
-		{
-		  fclose (print_files->f);
-		  print_files->f = NULL;
-		}
-	    }
-
-	  if (p->f != NULL)
-	    {
-	      skip_to_line (p, line, TRUE);
-	      *pp = p->next;
-	      p->next = print_files;
-	      print_files = p;
-	    }
-	}
-      else
-	{
+      if (p == NULL)
 	  p = update_source_path (filename);
 
-	  if (p != NULL)
-	    {
-	      int l;
-
-	      if (file_start_context)
-		l = 0;
-	      else
-		l = line - SHOW_PRECEDING_CONTEXT_LINES;
-	      if (l < 0)
-		l = 0;
-	      skip_to_line (p, l, FALSE);
-	      if (p->f != NULL)
-		skip_to_line (p, line, TRUE);
-	    }
+      if (p != NULL && line != p->last_line)
+	{
+	  if (file_start_context && p->first) 
+	    l = 1;
+	  else 
+	    {
+	      l = line - SHOW_PRECEDING_CONTEXT_LINES;
+	      if (l >= line) 
+		l = 1;
+	      if (p->last_line >= l && p->last_line <= line)
+		l = p->last_line + 1;
+	    }
+	  dump_lines (p, l, line);
+	  p->last_line = line;
+	  p->first = 0;
 	}
     }
 


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