[PATCH] Fix objdump -S with unit-at-a-time II

Andi Kleen ak@suse.de
Fri Jun 1 20:59:00 GMT 2007


On Friday 01 June 2007 20:48, Andi Kleen wrote:
> 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.

Small improvement to the previous patch. Previously the last line
of a source file wasn't properly indexed.

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.
	(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 src/binutils/objdump.c-o src/binutils/objdump.c
--- src/binutils/objdump.c-o	2007-05-29 13:19:49.000000000 +0200
+++ src/binutils/objdump.c	2007-06-01 22:55: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,95 @@
 
 #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, *end;
+  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;
+  end = map + size;
+  for (p = map; p < end; p++) 
+    { 
+      if (*p == '\n') 
+	{ 
+	  if (p + 1 < end && p[1] == '\r') 
+	    p++;  
+	} 
+      else if (*p == '\r') 
+	{ 
+	  if (p + 1 < end && 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 +1042,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 +1116,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 +1182,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;
 	}
     }
 



More information about the Binutils mailing list