[patch] Add external symbol table file support for gprof

Dongsheng Xing homer.xing@yahoo.com
Fri May 1 11:31:00 GMT 2009


The attached patch adds support for external symbol table file in gprof.
"external symbol table file" is a file such as /proc/kallsyms , or a file generated by "nm a.out".

This support is useful for profiling Linux kernel modules. 
For example, somebody wants to investigate which function call schedule(), and how many times the function call schedule(). 
She modifies schedule() in Linux kernel to record the caller function's address. 
Then she runs "gprof linux-2.6.28.7/vmlinux gmon.out"
But the profiling result is not right, the calling records of the functions in dynamically loaded kernel modules are missing, since these functions' address are not in the core file "linux-2.6.28.7/vmlinux".
By the external symbol table file support,
"gprof --external-symbol-table=/proc/kallsyms linux-2.6.28.7/vmlinux gmon.out"
returns the correct profiling result.

I would like any comments you have.

Cheers,
Homer

diff -rup binutils-2.19.51.origin/gprof/corefile.c binutils-2.19.51/gprof/corefile.c
--- binutils-2.19.51.origin/gprof/corefile.c	2009-05-01 13:28:01.000000000 +0800
+++ binutils-2.19.51/gprof/corefile.c	2009-05-01 14:59:26.000000000 +0800
@@ -434,6 +434,96 @@ get_src_info (bfd_vma addr, const char *
     }
 }
 
+/* Return number of symbols in a symbol-table file.  */
+
+static int 
+num_of_syms_in (const char *sym_table_file)
+{
+  char buf[1024];
+  char address[17];
+  char type;
+  char name[100];
+  FILE *f;
+  int num = 0;
+  
+  f = fopen (sym_table_file, "r");
+  if (!f)
+    {
+      fprintf (stderr, _("%s: could not open %s.\n"), whoami, sym_table_file);
+      done (1);
+    }
+  while (!feof (f) && fgets (buf, sizeof (buf), f))
+    {
+      sscanf (buf, "%s %c %s", address, &type, name);
+      if (type == 't' || type == 'T')
+        ++num;
+    }
+  fclose (f);
+
+  return num;
+}
+
+/* Read symbol table from a file.  */
+
+void
+core_create_syms_from (const char *sym_table_file)
+{
+  bfd_vma min_vma = ~(bfd_vma) 0;
+  bfd_vma max_vma = 0;
+  char buf[1024];
+  char address[17];
+  char type;
+  char name[100];
+  FILE *f;
+
+  /* Pass 1 - determine upper bound on number of function names.  */
+  symtab.len = num_of_syms_in (sym_table_file);
+
+  if (symtab.len == 0)
+    {
+      fprintf (stderr, _("%s: file `%s' has no symbols\n"), whoami, sym_table_file);
+      done (1);
+    }
+
+  symtab.base = (Sym *) xmalloc (symtab.len * sizeof (Sym));
+
+  /* Pass 2 - create symbols.  */
+  symtab.limit = symtab.base;
+
+  f = fopen(sym_table_file, "r");
+  if (!f)
+    {
+      fprintf (stderr, _("%s: could not open %s.\n"), whoami, sym_table_file);
+      done (1);
+    }
+
+  while (!feof (f) && fgets (buf, sizeof (buf), f))
+    {
+      sscanf (buf, "%s %c %s", address, &type, name);
+      if (type != 't' && type != 'T')
+        continue;
+
+      sym_init (symtab.limit);
+
+      sscanf (address, "%lx", &(symtab.limit->addr) );
+
+      symtab.limit->name = (char *) xmalloc (strlen (name) + 1);
+      strcpy ((char *)symtab.limit->name, name);
+      symtab.limit->mapped = 0;
+      symtab.limit->is_func = TRUE;
+      symtab.limit->is_bb_head = TRUE;
+      symtab.limit->is_static = (type == 't');
+      min_vma = MIN (symtab.limit->addr, min_vma);
+      max_vma = MAX (symtab.limit->addr, max_vma);
+
+      ++symtab.limit;
+    }
+  fclose (f);
+
+  symtab.len = symtab.limit - symtab.base;
+  symtab_finalize (&symtab);
+}
+
 /* Read in symbol table from core.
    One symbol per function is entered.  */
 
diff -rup binutils-2.19.51.origin/gprof/corefile.h binutils-2.19.51/gprof/corefile.h
--- binutils-2.19.51.origin/gprof/corefile.h	2009-05-01 13:28:01.000000000 +0800
+++ binutils-2.19.51/gprof/corefile.h	2009-05-01 14:17:05.000000000 +0800
@@ -41,5 +41,6 @@ extern void core_init                  (
 extern void core_get_text_space        (bfd *);
 extern void core_create_function_syms  (void);
 extern void core_create_line_syms      (void);
+extern void core_create_syms_from      (const char *);
 
 #endif /* corefile_h */
diff -rup binutils-2.19.51.origin/gprof/gprof.c binutils-2.19.51/gprof/gprof.c
--- binutils-2.19.51.origin/gprof/gprof.c	2009-05-01 13:28:01.000000000 +0800
+++ binutils-2.19.51/gprof/gprof.c	2009-05-01 18:59:31.000000000 +0800
@@ -49,6 +49,7 @@ static void usage (FILE *, int) ATTRIBUT
 
 const char *whoami;
 const char *function_mapping_file;
+const char *external_symbol_table;
 const char *a_out_name = A_OUTNAME;
 long hz = HZ_WRONG;
 
@@ -147,6 +148,7 @@ static struct option long_options[] =
   {"file-format", required_argument, 0, 'O'},
   {"traditional", no_argument, 0, 'T'},
   {"version", no_argument, 0, 'v'},
+  {"external-symbol-table", required_argument, 0, 'S'},
   {0, no_argument, 0, 0}
 };
 
@@ -155,7 +157,7 @@ static void
 usage (FILE *stream, int status)
 {
   fprintf (stream, _("\
-Usage: %s [-[abcDhilLsTvwxyz]] [-[ACeEfFJnNOpPqQZ][name]] [-I dirs]\n\
+Usage: %s [-[abcDhilLsTvwxyz]] [-[ACeEfFJnNOpPqSQZ][name]] [-I dirs]\n\
 	[-d[num]] [-k from/to] [-m min-count] [-t table-length]\n\
 	[--[no-]annotated-source[=name]] [--[no-]exec-counts[=name]]\n\
 	[--[no-]flat-profile[=name]] [--[no-]graph[=name]]\n\
@@ -166,7 +168,7 @@ Usage: %s [-[abcDhilLsTvwxyz]] [-[ACeEfF
 	[--no-static] [--print-path] [--separate-files]\n\
 	[--static-call-graph] [--sum] [--table-length=len] [--traditional]\n\
 	[--version] [--width=n] [--ignore-non-functions]\n\
-	[--demangle[=STYLE]] [--no-demangle] [@FILE]\n\
+	[--demangle[=STYLE]] [--no-demangle] [--external-symbol-table=name] [@FILE]\n\
 	[image-file] [profile-file...]\n"),
 	   whoami);
   if (REPORT_BUGS_TO[0] && status == 0)
@@ -199,7 +201,7 @@ main (int argc, char **argv)
   expandargv (&argc, &argv);
 
   while ((ch = getopt_long (argc, argv,
-	"aA::bBcC::d::De:E:f:F:hiI:J::k:lLm:n:N:O:p::P::q::Q::rR:st:Tvw:xyzZ::",
+	"aA::bBcC::d::De:E:f:F:hiI:J::k:lLm:n:N:O:p::P::q::Q::rR:sS:t:Tvw:xyzZ::",
 			    long_options, 0))
 	 != EOF)
     {
@@ -398,6 +400,10 @@ main (int argc, char **argv)
 	  output_style |= STYLE_SUMMARY_FILE;
 	  user_specified |= STYLE_SUMMARY_FILE;
 	  break;
+	case 'S':
+	  external_symbol_table = optarg;
+	  DBG (AOUTDEBUG, printf ("external-symbol-table: %s\n", optarg));
+	  break;
 	case 't':
 	  bb_table_length = atoi (optarg);
 	  if (bb_table_length < 0)
@@ -512,10 +518,16 @@ This program is free software.  This pro
     core_get_text_space (core_bfd);
 
   /* Create symbols from core image.  */
-  if (line_granularity)
-    core_create_line_syms ();
+  if (external_symbol_table)
+    core_create_syms_from (external_symbol_table);
   else
-    core_create_function_syms ();
+    {
+      /* Create symbols from core image.  */
+      if (line_granularity)
+        core_create_line_syms ();
+      else
+        core_create_function_syms ();
+    }
 
   /* Translate sym specs into syms.  */
   sym_id_parse ();


      



More information about the Binutils mailing list