Back to index

glibc  2.9
sprof.c
Go to the documentation of this file.
00001 /* Read and display shared object profiling data.
00002    Copyright (C) 1997-2007, 2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <argp.h>
00022 #include <dlfcn.h>
00023 #include <elf.h>
00024 #include <error.h>
00025 #include <fcntl.h>
00026 #include <inttypes.h>
00027 #include <libintl.h>
00028 #include <locale.h>
00029 #include <obstack.h>
00030 #include <search.h>
00031 #include <stdbool.h>
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <unistd.h>
00036 #include <ldsodefs.h>
00037 #include <sys/gmon.h>
00038 #include <sys/gmon_out.h>
00039 #include <sys/mman.h>
00040 #include <sys/param.h>
00041 #include <sys/stat.h>
00042 
00043 /* Get libc version number.  */
00044 #include "../version.h"
00045 
00046 #define PACKAGE _libc_intl_domainname
00047 
00048 
00049 #include <endian.h>
00050 #if BYTE_ORDER == BIG_ENDIAN
00051 # define byteorder ELFDATA2MSB
00052 # define byteorder_name "big-endian"
00053 #elif BYTE_ORDER == LITTLE_ENDIAN
00054 # define byteorder ELFDATA2LSB
00055 # define byteorder_name "little-endian"
00056 #else
00057 # error "Unknown BYTE_ORDER " BYTE_ORDER
00058 # define byteorder ELFDATANONE
00059 #endif
00060 
00061 #ifndef PATH_MAX
00062 # define PATH_MAX 1024
00063 #endif
00064 
00065 
00066 extern int __profile_frequency (void);
00067 
00068 /* Name and version of program.  */
00069 static void print_version (FILE *stream, struct argp_state *state);
00070 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
00071 
00072 #define OPT_TEST     1
00073 
00074 /* Definitions of arguments for argp functions.  */
00075 static const struct argp_option options[] =
00076 {
00077   { NULL, 0, NULL, 0, N_("Output selection:") },
00078   { "call-pairs", 'c', NULL, 0,
00079     N_("print list of count paths and their number of use") },
00080   { "flat-profile", 'p', NULL, 0,
00081     N_("generate flat profile with counts and ticks") },
00082   { "graph", 'q', NULL, 0, N_("generate call graph") },
00083 
00084   { "test", OPT_TEST, NULL, OPTION_HIDDEN, NULL },
00085   { NULL, 0, NULL, 0, NULL }
00086 };
00087 
00088 /* Short description of program.  */
00089 static const char doc[] = N_("Read and display shared object profiling data.\v\
00090 For bug reporting instructions, please see:\n\
00091 <http://www.gnu.org/software/libc/bugs.html>.\n");
00092 
00093 /* Strings for arguments in help texts.  */
00094 static const char args_doc[] = N_("SHOBJ [PROFDATA]");
00095 
00096 /* Prototype for option handler.  */
00097 static error_t parse_opt (int key, char *arg, struct argp_state *state);
00098 
00099 /* Data structure to communicate with argp functions.  */
00100 static struct argp argp =
00101 {
00102   options, parse_opt, args_doc, doc
00103 };
00104 
00105 
00106 /* Operation modes.  */
00107 static enum
00108 {
00109   NONE = 0,
00110   FLAT_MODE = 1 << 0,
00111   CALL_GRAPH_MODE = 1 << 1,
00112   CALL_PAIRS = 1 << 2,
00113 
00114   DEFAULT_MODE = FLAT_MODE | CALL_GRAPH_MODE
00115 } mode;
00116 
00117 /* Nozero for testing.  */
00118 static int do_test;
00119 
00120 /* Strcuture describing calls.  */
00121 struct here_fromstruct
00122 {
00123   struct here_cg_arc_record volatile *here;
00124   uint16_t link;
00125 };
00126 
00127 /* We define a special type to address the elements of the arc table.
00128    This is basically the `gmon_cg_arc_record' format but it includes
00129    the room for the tag and it uses real types.  */
00130 struct here_cg_arc_record
00131 {
00132   uintptr_t from_pc;
00133   uintptr_t self_pc;
00134   uint32_t count;
00135 } __attribute__ ((packed));
00136 
00137 
00138 struct known_symbol;
00139 struct arc_list
00140 {
00141   size_t idx;
00142   uintmax_t count;
00143 
00144   struct arc_list *next;
00145 };
00146 
00147 static struct obstack ob_list;
00148 
00149 
00150 struct known_symbol
00151 {
00152   const char *name;
00153   uintptr_t addr;
00154   size_t size;
00155   bool weak;
00156   bool hidden;
00157 
00158   uintmax_t ticks;
00159   uintmax_t calls;
00160 
00161   struct arc_list *froms;
00162   struct arc_list *tos;
00163 };
00164 
00165 
00166 struct shobj
00167 {
00168   const char *name;         /* User-provided name.  */
00169 
00170   struct link_map *map;
00171   const char *dynstrtab;    /* Dynamic string table of shared object.  */
00172   const char *soname;              /* Soname of shared object.  */
00173 
00174   uintptr_t lowpc;
00175   uintptr_t highpc;
00176   unsigned long int kcountsize;
00177   size_t expected_size;            /* Expected size of profiling file.  */
00178   size_t tossize;
00179   size_t fromssize;
00180   size_t fromlimit;
00181   unsigned int hashfraction;
00182   int s_scale;
00183 
00184   void *symbol_map;
00185   size_t symbol_mapsize;
00186   const ElfW(Sym) *symtab;
00187   size_t symtab_size;
00188   const char *strtab;
00189 
00190   struct obstack ob_str;
00191   struct obstack ob_sym;
00192 };
00193 
00194 
00195 struct profdata
00196 {
00197   void *addr;
00198   off_t size;
00199 
00200   char *hist;
00201   struct gmon_hist_hdr *hist_hdr;
00202   uint16_t *kcount;
00203   uint32_t narcs;           /* Number of arcs in toset.  */
00204   struct here_cg_arc_record *data;
00205   uint16_t *tos;
00206   struct here_fromstruct *froms;
00207 };
00208 
00209 /* Search tree for symbols.  */
00210 static void *symroot;
00211 static struct known_symbol **sortsym;
00212 static size_t symidx;
00213 static uintmax_t total_ticks;
00214 
00215 /* Prototypes for local functions.  */
00216 static struct shobj *load_shobj (const char *name);
00217 static void unload_shobj (struct shobj *shobj);
00218 static struct profdata *load_profdata (const char *name, struct shobj *shobj);
00219 static void unload_profdata (struct profdata *profdata);
00220 static void count_total_ticks (struct shobj *shobj, struct profdata *profdata);
00221 static void count_calls (struct shobj *shobj, struct profdata *profdata);
00222 static void read_symbols (struct shobj *shobj);
00223 static void add_arcs (struct profdata *profdata);
00224 static void generate_flat_profile (struct profdata *profdata);
00225 static void generate_call_graph (struct profdata *profdata);
00226 static void generate_call_pair_list (struct profdata *profdata);
00227 
00228 
00229 int
00230 main (int argc, char *argv[])
00231 {
00232   const char *shobj;
00233   const char *profdata;
00234   struct shobj *shobj_handle;
00235   struct profdata *profdata_handle;
00236   int remaining;
00237 
00238   setlocale (LC_ALL, "");
00239 
00240   /* Initialize the message catalog.  */
00241   textdomain (_libc_intl_domainname);
00242 
00243   /* Parse and process arguments.  */
00244   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
00245 
00246   if (argc - remaining == 0 || argc - remaining > 2)
00247     {
00248       /* We need exactly two non-option parameter.  */
00249       argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
00250                  program_invocation_short_name);
00251       exit (1);
00252     }
00253 
00254   /* Get parameters.  */
00255   shobj = argv[remaining];
00256   if (argc - remaining == 2)
00257     profdata = argv[remaining + 1];
00258   else
00259     /* No filename for the profiling data given.  We will determine it
00260        from the soname of the shobj, later.  */
00261     profdata = NULL;
00262 
00263   /* First see whether we can load the shared object.  */
00264   shobj_handle = load_shobj (shobj);
00265   if (shobj_handle == NULL)
00266     exit (1);
00267 
00268   /* We can now determine the filename for the profiling data, if
00269      nececessary.  */
00270   if (profdata == NULL)
00271     {
00272       char *newp;
00273       const char *soname;
00274       size_t soname_len;
00275 
00276       soname = shobj_handle->soname ?: basename (shobj);
00277       soname_len = strlen (soname);
00278       newp = (char *) alloca (soname_len + sizeof ".profile");
00279       stpcpy (mempcpy (newp, soname, soname_len), ".profile");
00280       profdata = newp;
00281     }
00282 
00283   /* Now see whether the profiling data file matches the given object.   */
00284   profdata_handle = load_profdata (profdata, shobj_handle);
00285   if (profdata_handle == NULL)
00286     {
00287       unload_shobj (shobj_handle);
00288 
00289       exit (1);
00290     }
00291 
00292   read_symbols (shobj_handle);
00293 
00294   /* Count the ticks.  */
00295   count_total_ticks (shobj_handle, profdata_handle);
00296 
00297   /* Count the calls.  */
00298   count_calls (shobj_handle, profdata_handle);
00299 
00300   /* Add the arc information.  */
00301   add_arcs (profdata_handle);
00302 
00303   /* If no mode is specified fall back to the default mode.  */
00304   if (mode == NONE)
00305     mode = DEFAULT_MODE;
00306 
00307   /* Do some work.  */
00308   if (mode & FLAT_MODE)
00309     generate_flat_profile (profdata_handle);
00310 
00311   if (mode & CALL_GRAPH_MODE)
00312     generate_call_graph (profdata_handle);
00313 
00314   if (mode & CALL_PAIRS)
00315     generate_call_pair_list (profdata_handle);
00316 
00317   /* Free the resources.  */
00318   unload_shobj (shobj_handle);
00319   unload_profdata (profdata_handle);
00320 
00321   return 0;
00322 }
00323 
00324 
00325 /* Handle program arguments.  */
00326 static error_t
00327 parse_opt (int key, char *arg, struct argp_state *state)
00328 {
00329   switch (key)
00330     {
00331     case 'c':
00332       mode |= CALL_PAIRS;
00333       break;
00334     case 'p':
00335       mode |= FLAT_MODE;
00336       break;
00337     case 'q':
00338       mode |= CALL_GRAPH_MODE;
00339       break;
00340     case OPT_TEST:
00341       do_test = 1;
00342       break;
00343     default:
00344       return ARGP_ERR_UNKNOWN;
00345     }
00346   return 0;
00347 }
00348 
00349 
00350 /* Print the version information.  */
00351 static void
00352 print_version (FILE *stream, struct argp_state *state)
00353 {
00354   fprintf (stream, "sprof (GNU %s) %s\n", PACKAGE, VERSION);
00355   fprintf (stream, gettext ("\
00356 Copyright (C) %s Free Software Foundation, Inc.\n\
00357 This is free software; see the source for copying conditions.  There is NO\n\
00358 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
00359 "),
00360           "2008");
00361   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
00362 }
00363 
00364 
00365 /* Note that we must not use `dlopen' etc.  The shobj object must not
00366    be loaded for use.  */
00367 static struct shobj *
00368 load_shobj (const char *name)
00369 {
00370   struct link_map *map = NULL;
00371   struct shobj *result;
00372   ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
00373   ElfW(Addr) mapend = 0;
00374   const ElfW(Phdr) *ph;
00375   size_t textsize;
00376   unsigned int log_hashfraction;
00377   ElfW(Ehdr) *ehdr;
00378   int fd;
00379   ElfW(Shdr) *shdr;
00380   size_t pagesize = getpagesize ();
00381 
00382   /* Since we use dlopen() we must be prepared to work around the sometimes
00383      strange lookup rules for the shared objects.  If we have a file foo.so
00384      in the current directory and the user specfies foo.so on the command
00385      line (without specifying a directory) we should load the file in the
00386      current directory even if a normal dlopen() call would read the other
00387      file.  We do this by adding a directory portion to the name.  */
00388   if (strchr (name, '/') == NULL)
00389     {
00390       char *load_name = (char *) alloca (strlen (name) + 3);
00391       stpcpy (stpcpy (load_name, "./"), name);
00392 
00393       map = (struct link_map *) dlopen (load_name, RTLD_LAZY | __RTLD_SPROF);
00394     }
00395   if (map == NULL)
00396     {
00397       map = (struct link_map *) dlopen (name, RTLD_LAZY | __RTLD_SPROF);
00398       if (map == NULL)
00399        {
00400          error (0, errno, _("failed to load shared object `%s'"), name);
00401          return NULL;
00402        }
00403     }
00404 
00405   /* Prepare the result.  */
00406   result = (struct shobj *) calloc (1, sizeof (struct shobj));
00407   if (result == NULL)
00408     {
00409       error (0, errno, _("cannot create internal descriptors"));
00410       dlclose (map);
00411       return NULL;
00412     }
00413   result->name = name;
00414   result->map = map;
00415 
00416   /* Compute the size of the sections which contain program code.
00417      This must match the code in dl-profile.c (_dl_start_profile).  */
00418   for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
00419     if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
00420       {
00421        ElfW(Addr) start = (ph->p_vaddr & ~(pagesize - 1));
00422        ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
00423                        & ~(pagesize - 1));
00424 
00425        if (start < mapstart)
00426          mapstart = start;
00427        if (end > mapend)
00428          mapend = end;
00429       }
00430 
00431   result->lowpc = ROUNDDOWN ((uintptr_t) (mapstart + map->l_addr),
00432                           HISTFRACTION * sizeof (HISTCOUNTER));
00433   result->highpc = ROUNDUP ((uintptr_t) (mapend + map->l_addr),
00434                          HISTFRACTION * sizeof (HISTCOUNTER));
00435   if (do_test)
00436     printf ("load addr: %0#*" PRIxPTR "\n"
00437            "lower bound PC: %0#*" PRIxPTR "\n"
00438            "upper bound PC: %0#*" PRIxPTR "\n",
00439            __ELF_NATIVE_CLASS == 32 ? 10 : 18, map->l_addr,
00440            __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->lowpc,
00441            __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->highpc);
00442 
00443   textsize = result->highpc - result->lowpc;
00444   result->kcountsize = textsize / HISTFRACTION;
00445   result->hashfraction = HASHFRACTION;
00446   if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
00447     /* If HASHFRACTION is a power of two, mcount can use shifting
00448        instead of integer division.  Precompute shift amount.  */
00449     log_hashfraction = __builtin_ffs (result->hashfraction
00450                                   * sizeof (struct here_fromstruct)) - 1;
00451   else
00452     log_hashfraction = -1;
00453   if (do_test)
00454     printf ("hashfraction = %d\ndivider = %Zu\n",
00455            result->hashfraction,
00456            result->hashfraction * sizeof (struct here_fromstruct));
00457   result->tossize = textsize / HASHFRACTION;
00458   result->fromlimit = textsize * ARCDENSITY / 100;
00459   if (result->fromlimit < MINARCS)
00460     result->fromlimit = MINARCS;
00461   if (result->fromlimit > MAXARCS)
00462     result->fromlimit = MAXARCS;
00463   result->fromssize = result->fromlimit * sizeof (struct here_fromstruct);
00464 
00465   result->expected_size = (sizeof (struct gmon_hdr)
00466                         + 4 + sizeof (struct gmon_hist_hdr)
00467                         + result->kcountsize
00468                         + 4 + 4
00469                         + (result->fromssize
00470                            * sizeof (struct here_cg_arc_record)));
00471 
00472   if (do_test)
00473     printf ("expected size: %Zd\n", result->expected_size);
00474 
00475 #define SCALE_1_TO_1 0x10000L
00476 
00477   if (result->kcountsize < result->highpc - result->lowpc)
00478     {
00479       size_t range = result->highpc - result->lowpc;
00480       size_t quot = range / result->kcountsize;
00481 
00482       if (quot >= SCALE_1_TO_1)
00483        result->s_scale = 1;
00484       else if (quot >= SCALE_1_TO_1 / 256)
00485        result->s_scale = SCALE_1_TO_1 / quot;
00486       else if (range > ULONG_MAX / 256)
00487        result->s_scale = ((SCALE_1_TO_1 * 256)
00488                         / (range / (result->kcountsize / 256)));
00489       else
00490        result->s_scale = ((SCALE_1_TO_1 * 256)
00491                         / ((range * 256) / result->kcountsize));
00492     }
00493   else
00494     result->s_scale = SCALE_1_TO_1;
00495 
00496   if (do_test)
00497     printf ("s_scale: %d\n", result->s_scale);
00498 
00499   /* Determine the dynamic string table.  */
00500   if (map->l_info[DT_STRTAB] == NULL)
00501     result->dynstrtab = NULL;
00502   else
00503     result->dynstrtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
00504   if (do_test)
00505     printf ("string table: %p\n", result->dynstrtab);
00506 
00507   /* Determine the soname.  */
00508   if (map->l_info[DT_SONAME] == NULL)
00509     result->soname = NULL;
00510   else
00511     result->soname = result->dynstrtab + map->l_info[DT_SONAME]->d_un.d_val;
00512   if (do_test && result->soname != NULL)
00513     printf ("soname: %s\n", result->soname);
00514 
00515   /* Now we have to load the symbol table.
00516 
00517      First load the section header table.  */
00518   ehdr = (ElfW(Ehdr) *) map->l_map_start;
00519 
00520   /* Make sure we are on the right party.  */
00521   if (ehdr->e_shentsize != sizeof (ElfW(Shdr)))
00522     abort ();
00523 
00524   /* And we need the shared object file descriptor again.  */
00525   fd = open (map->l_name, O_RDONLY);
00526   if (fd == -1)
00527     /* Dooh, this really shouldn't happen.  We know the file is available.  */
00528     error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"),
00529           map->l_name);
00530 
00531   /* Map the section header.  */
00532   size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr));
00533   shdr = (ElfW(Shdr) *) alloca (size);
00534   if (pread (fd, shdr, size, ehdr->e_shoff) != size)
00535     error (EXIT_FAILURE, errno, _("reading of section headers failed"));
00536 
00537   /* Get the section header string table.  */
00538   char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size);
00539   if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size,
00540             shdr[ehdr->e_shstrndx].sh_offset)
00541       != shdr[ehdr->e_shstrndx].sh_size)
00542     error (EXIT_FAILURE, errno,
00543           _("reading of section header string table failed"));
00544 
00545   /* Search for the ".symtab" section.  */
00546   ElfW(Shdr) *symtab_entry = NULL;
00547   ElfW(Shdr) *debuglink_entry = NULL;
00548   for (int idx = 0; idx < ehdr->e_shnum; ++idx)
00549     if (shdr[idx].sh_type == SHT_SYMTAB
00550        && strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0)
00551       {
00552        symtab_entry = &shdr[idx];
00553        break;
00554       }
00555     else if (shdr[idx].sh_type == SHT_PROGBITS
00556             && strcmp (shstrtab + shdr[idx].sh_name, ".gnu_debuglink") == 0)
00557       debuglink_entry = &shdr[idx];
00558 
00559   /* Get the file name of the debuginfo file if necessary.  */
00560   int symfd = fd;
00561   if (symtab_entry == NULL && debuglink_entry != NULL)
00562     {
00563       size_t size = debuglink_entry->sh_size;
00564       char *debuginfo_fname = (char *) alloca (size + 1);
00565       debuginfo_fname[size] = '\0';
00566       if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset)
00567          != size)
00568        {
00569          fprintf (stderr, _("*** Cannot read debuginfo file name: %m\n"));
00570          goto no_debuginfo;
00571        }
00572 
00573       static const char procpath[] = "/proc/self/fd/%d";
00574       char origprocname[sizeof (procpath) + sizeof (int) * 3];
00575       snprintf (origprocname, sizeof (origprocname), procpath, fd);
00576       char *origlink = (char *) alloca (PATH_MAX + 1);
00577       origlink[PATH_MAX] = '\0';
00578       if (readlink (origprocname, origlink, PATH_MAX) == -1)
00579        goto no_debuginfo;
00580 
00581       /* Try to find the actual file.  There are three places:
00582         1. the same directory the DSO is in
00583         2. in a subdir named .debug of the directory the DSO is in
00584         3. in /usr/lib/debug/PATH-OF-DSO
00585       */
00586       char *realname = canonicalize_file_name (origlink);
00587       char *cp = NULL;
00588       if (realname == NULL || (cp = strrchr (realname, '/')) == NULL)
00589        error (EXIT_FAILURE, errno, _("cannot determine file name"));
00590 
00591       /* Leave the last slash in place.  */
00592       *++cp = '\0';
00593 
00594       /* First add the debuginfo file name only.  */
00595       static const char usrlibdebug[]= "/usr/lib/debug/";
00596       char *workbuf = (char *) alloca (sizeof (usrlibdebug)
00597                                    + (cp - realname)
00598                                    + strlen (debuginfo_fname));
00599       strcpy (stpcpy (workbuf, realname), debuginfo_fname);
00600 
00601       int fd2 = open (workbuf, O_RDONLY);
00602       if (fd2 == -1)
00603        {
00604          strcpy (stpcpy (stpcpy (workbuf, realname), ".debug/"),
00605                 debuginfo_fname);
00606          fd2 = open (workbuf, O_RDONLY);
00607          if (fd2 == -1)
00608            {
00609              strcpy (stpcpy (stpcpy (workbuf, usrlibdebug), realname),
00610                     debuginfo_fname);
00611              fd2 = open (workbuf, O_RDONLY);
00612            }
00613        }
00614 
00615       if (fd2 != -1)
00616        {
00617          ElfW(Ehdr) ehdr2;
00618 
00619          /* Read the ELF header.  */
00620          if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2))
00621            error (EXIT_FAILURE, errno,
00622                  _("reading of ELF header failed"));
00623 
00624          /* Map the section header.  */
00625          size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr));
00626          ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size);
00627          if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size)
00628            error (EXIT_FAILURE, errno,
00629                  _("reading of section headers failed"));
00630 
00631          /* Get the section header string table.  */
00632          shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size);
00633          if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size,
00634                    shdr2[ehdr2.e_shstrndx].sh_offset)
00635              != shdr2[ehdr2.e_shstrndx].sh_size)
00636            error (EXIT_FAILURE, errno,
00637                  _("reading of section header string table failed"));
00638 
00639          /* Search for the ".symtab" section.  */
00640          for (int idx = 0; idx < ehdr2.e_shnum; ++idx)
00641            if (shdr2[idx].sh_type == SHT_SYMTAB
00642               && strcmp (shstrtab + shdr2[idx].sh_name, ".symtab") == 0)
00643              {
00644               symtab_entry = &shdr2[idx];
00645               shdr = shdr2;
00646               symfd = fd2;
00647               break;
00648              }
00649 
00650          if  (fd2 != symfd)
00651            close (fd2);
00652        }
00653     }
00654 
00655  no_debuginfo:
00656   if (symtab_entry == NULL)
00657     {
00658       fprintf (stderr, _("\
00659 *** The file `%s' is stripped: no detailed analysis possible\n"),
00660              name);
00661       result->symtab = NULL;
00662       result->strtab = NULL;
00663     }
00664   else
00665     {
00666       ElfW(Off) min_offset, max_offset;
00667       ElfW(Shdr) *strtab_entry;
00668 
00669       strtab_entry = &shdr[symtab_entry->sh_link];
00670 
00671       /* Find the minimum and maximum offsets that include both the symbol
00672         table and the string table.  */
00673       if (symtab_entry->sh_offset < strtab_entry->sh_offset)
00674        {
00675          min_offset = symtab_entry->sh_offset & ~(pagesize - 1);
00676          max_offset = strtab_entry->sh_offset + strtab_entry->sh_size;
00677        }
00678       else
00679        {
00680          min_offset = strtab_entry->sh_offset & ~(pagesize - 1);
00681          max_offset = symtab_entry->sh_offset + symtab_entry->sh_size;
00682        }
00683 
00684       result->symbol_map = mmap (NULL, max_offset - min_offset,
00685                              PROT_READ, MAP_SHARED|MAP_FILE, symfd,
00686                              min_offset);
00687       if (result->symbol_map == MAP_FAILED)
00688        error (EXIT_FAILURE, errno, _("failed to load symbol data"));
00689 
00690       result->symtab
00691        = (const ElfW(Sym) *) ((const char *) result->symbol_map
00692                             + (symtab_entry->sh_offset - min_offset));
00693       result->symtab_size = symtab_entry->sh_size;
00694       result->strtab = ((const char *) result->symbol_map
00695                      + (strtab_entry->sh_offset - min_offset));
00696       result->symbol_mapsize = max_offset - min_offset;
00697     }
00698 
00699   /* Free the descriptor for the shared object.  */
00700   close (fd);
00701   if (symfd != fd)
00702     close (symfd);
00703 
00704   return result;
00705 }
00706 
00707 
00708 static void
00709 unload_shobj (struct shobj *shobj)
00710 {
00711   munmap (shobj->symbol_map, shobj->symbol_mapsize);
00712   dlclose (shobj->map);
00713 }
00714 
00715 
00716 static struct profdata *
00717 load_profdata (const char *name, struct shobj *shobj)
00718 {
00719   struct profdata *result;
00720   int fd;
00721   struct stat st;
00722   void *addr;
00723   struct gmon_hdr gmon_hdr;
00724   struct gmon_hist_hdr hist_hdr;
00725   uint32_t *narcsp;
00726   size_t fromlimit;
00727   struct here_cg_arc_record *data;
00728   struct here_fromstruct *froms;
00729   uint16_t *tos;
00730   size_t fromidx;
00731   size_t idx;
00732 
00733   fd = open (name, O_RDONLY);
00734   if (fd == -1)
00735     {
00736       char *ext_name;
00737 
00738       if (errno != ENOENT || strchr (name, '/') != NULL)
00739        /* The file exists but we are not allowed to read it or the
00740           file does not exist and the name includes a path
00741           specification..  */
00742        return NULL;
00743 
00744       /* A file with the given name does not exist in the current
00745         directory, try it in the default location where the profiling
00746         files are created.  */
00747       ext_name = (char *) alloca (strlen (name) + sizeof "/var/tmp/");
00748       stpcpy (stpcpy (ext_name, "/var/tmp/"), name);
00749       name = ext_name;
00750 
00751       fd = open (ext_name, O_RDONLY);
00752       if (fd == -1)
00753        {
00754          /* Even this file does not exist.  */
00755          error (0, errno, _("cannot load profiling data"));
00756          return NULL;
00757        }
00758     }
00759 
00760   /* We have found the file, now make sure it is the right one for the
00761      data file.  */
00762   if (fstat (fd, &st) < 0)
00763     {
00764       error (0, errno, _("while stat'ing profiling data file"));
00765       close (fd);
00766       return NULL;
00767     }
00768 
00769   if ((size_t) st.st_size != shobj->expected_size)
00770     {
00771       error (0, 0,
00772             _("profiling data file `%s' does not match shared object `%s'"),
00773             name, shobj->name);
00774       close (fd);
00775       return NULL;
00776     }
00777 
00778   /* The data file is most probably the right one for our shared
00779      object.  Map it now.  */
00780   addr = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
00781   if (addr == MAP_FAILED)
00782     {
00783       error (0, errno, _("failed to mmap the profiling data file"));
00784       close (fd);
00785       return NULL;
00786     }
00787 
00788   /* We don't need the file desriptor anymore.  */
00789   if (close (fd) < 0)
00790     {
00791       error (0, errno, _("error while closing the profiling data file"));
00792       munmap (addr, st.st_size);
00793       return NULL;
00794     }
00795 
00796   /* Prepare the result.  */
00797   result = (struct profdata *) calloc (1, sizeof (struct profdata));
00798   if (result == NULL)
00799     {
00800       error (0, errno, _("cannot create internal descriptor"));
00801       munmap (addr, st.st_size);
00802       return NULL;
00803     }
00804 
00805   /* Store the address and size so that we can later free the resources.  */
00806   result->addr = addr;
00807   result->size = st.st_size;
00808 
00809   /* Pointer to data after the header.  */
00810   result->hist = (char *) ((struct gmon_hdr *) addr + 1);
00811   result->hist_hdr = (struct gmon_hist_hdr *) ((char *) result->hist
00812                                           + sizeof (uint32_t));
00813   result->kcount = (uint16_t *) ((char *) result->hist + sizeof (uint32_t)
00814                              + sizeof (struct gmon_hist_hdr));
00815 
00816   /* Compute pointer to array of the arc information.  */
00817   narcsp = (uint32_t *) ((char *) result->kcount + shobj->kcountsize
00818                       + sizeof (uint32_t));
00819   result->narcs = *narcsp;
00820   result->data = (struct here_cg_arc_record *) ((char *) narcsp
00821                                           + sizeof (uint32_t));
00822 
00823   /* Create the gmon_hdr we expect or write.  */
00824   memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr));
00825   memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
00826   *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION;
00827 
00828   /* Create the hist_hdr we expect or write.  */
00829   *(char **) hist_hdr.low_pc = (char *) shobj->lowpc - shobj->map->l_addr;
00830   *(char **) hist_hdr.high_pc = (char *) shobj->highpc - shobj->map->l_addr;
00831   if (do_test)
00832     printf ("low_pc = %p\nhigh_pc = %p\n",
00833            *(char **) hist_hdr.low_pc, *(char **) hist_hdr.high_pc);
00834   *(int32_t *) hist_hdr.hist_size = shobj->kcountsize / sizeof (HISTCOUNTER);
00835   *(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
00836   strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
00837   hist_hdr.dimen_abbrev = 's';
00838 
00839   /* Test whether the header of the profiling data is ok.  */
00840   if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
00841       || *(uint32_t *) result->hist != GMON_TAG_TIME_HIST
00842       || memcmp (result->hist_hdr, &hist_hdr,
00843                sizeof (struct gmon_hist_hdr)) != 0
00844       || narcsp[-1] != GMON_TAG_CG_ARC)
00845     {
00846       error (0, 0, _("`%s' is no correct profile data file for `%s'"),
00847             name, shobj->name);
00848       if (do_test)
00849        {
00850          if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0)
00851            puts ("gmon_hdr differs");
00852          if (*(uint32_t *) result->hist != GMON_TAG_TIME_HIST)
00853            puts ("result->hist differs");
00854          if (memcmp (result->hist_hdr, &hist_hdr,
00855                     sizeof (struct gmon_hist_hdr)) != 0)
00856            puts ("hist_hdr differs");
00857          if (narcsp[-1] != GMON_TAG_CG_ARC)
00858            puts ("narcsp[-1] differs");
00859        }
00860       free (result);
00861       munmap (addr, st.st_size);
00862       return NULL;
00863     }
00864 
00865   /* We are pretty sure now that this is a correct input file.  Set up
00866      the remaining information in the result structure and return.  */
00867   result->tos = (uint16_t *) calloc (shobj->tossize + shobj->fromssize, 1);
00868   if (result->tos == NULL)
00869     {
00870       error (0, errno, _("cannot create internal descriptor"));
00871       munmap (addr, st.st_size);
00872       free (result);
00873       return NULL;
00874     }
00875 
00876   result->froms = (struct here_fromstruct *) ((char *) result->tos
00877                                          + shobj->tossize);
00878   fromidx = 0;
00879 
00880   /* Now we have to process all the arc count entries.  */
00881   fromlimit = shobj->fromlimit;
00882   data = result->data;
00883   froms = result->froms;
00884   tos = result->tos;
00885   for (idx = 0; idx < MIN (*narcsp, fromlimit); ++idx)
00886     {
00887       size_t to_index;
00888       size_t newfromidx;
00889       to_index = (data[idx].self_pc / (shobj->hashfraction * sizeof (*tos)));
00890       newfromidx = fromidx++;
00891       froms[newfromidx].here = &data[idx];
00892       froms[newfromidx].link = tos[to_index];
00893       tos[to_index] = newfromidx;
00894     }
00895 
00896   return result;
00897 }
00898 
00899 
00900 static void
00901 unload_profdata (struct profdata *profdata)
00902 {
00903   free (profdata->tos);
00904   munmap (profdata->addr, profdata->size);
00905   free (profdata);
00906 }
00907 
00908 
00909 static void
00910 count_total_ticks (struct shobj *shobj, struct profdata *profdata)
00911 {
00912   volatile uint16_t *kcount = profdata->kcount;
00913   size_t maxkidx = shobj->kcountsize;
00914   size_t factor = 2 * (65536 / shobj->s_scale);
00915   size_t kidx = 0;
00916   size_t sidx = 0;
00917 
00918   while (sidx < symidx)
00919     {
00920       uintptr_t start = sortsym[sidx]->addr;
00921       uintptr_t end = start + sortsym[sidx]->size;
00922 
00923       while (kidx < maxkidx && factor * kidx < start)
00924        ++kidx;
00925       if (kidx == maxkidx)
00926        break;
00927 
00928       while (kidx < maxkidx && factor * kidx < end)
00929        sortsym[sidx]->ticks += kcount[kidx++];
00930       if (kidx == maxkidx)
00931        break;
00932 
00933       total_ticks += sortsym[sidx++]->ticks;
00934     }
00935 }
00936 
00937 
00938 static size_t
00939 find_symbol (uintptr_t addr)
00940 {
00941   size_t sidx = 0;
00942 
00943   while (sidx < symidx)
00944     {
00945       uintptr_t start = sortsym[sidx]->addr;
00946       uintptr_t end = start + sortsym[sidx]->size;
00947 
00948       if (addr >= start && addr < end)
00949        return sidx;
00950 
00951       if (addr < start)
00952        break;
00953 
00954       ++sidx;
00955     }
00956 
00957   return (size_t) -1l;
00958 }
00959 
00960 
00961 static void
00962 count_calls (struct shobj *shobj, struct profdata *profdata)
00963 {
00964   struct here_cg_arc_record *data = profdata->data;
00965   uint32_t narcs = profdata->narcs;
00966   uint32_t cnt;
00967 
00968   for (cnt = 0; cnt < narcs; ++cnt)
00969     {
00970       uintptr_t here = data[cnt].self_pc;
00971       size_t symbol_idx;
00972 
00973       /* Find the symbol for this address.  */
00974       symbol_idx = find_symbol (here);
00975       if (symbol_idx != (size_t) -1l)
00976        sortsym[symbol_idx]->calls += data[cnt].count;
00977     }
00978 }
00979 
00980 
00981 static int
00982 symorder (const void *o1, const void *o2)
00983 {
00984   const struct known_symbol *p1 = (const struct known_symbol *) o1;
00985   const struct known_symbol *p2 = (const struct known_symbol *) o2;
00986 
00987   return p1->addr - p2->addr;
00988 }
00989 
00990 
00991 static void
00992 printsym (const void *node, VISIT value, int level)
00993 {
00994   if (value == leaf || value == postorder)
00995     sortsym[symidx++] = *(struct known_symbol **) node;
00996 }
00997 
00998 
00999 static void
01000 read_symbols (struct shobj *shobj)
01001 {
01002   int n = 0;
01003 
01004   /* Initialize the obstacks.  */
01005 #define obstack_chunk_alloc malloc
01006 #define obstack_chunk_free free
01007   obstack_init (&shobj->ob_str);
01008   obstack_init (&shobj->ob_sym);
01009   obstack_init (&ob_list);
01010 
01011   /* Process the symbols.  */
01012   if (shobj->symtab != NULL)
01013     {
01014       const ElfW(Sym) *sym = shobj->symtab;
01015       const ElfW(Sym) *sym_end
01016        = (const ElfW(Sym) *) ((const char *) sym + shobj->symtab_size);
01017       for (; sym < sym_end; sym++)
01018        if ((ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
01019             || ELFW(ST_TYPE) (sym->st_info) == STT_NOTYPE)
01020            && sym->st_size != 0)
01021          {
01022            struct known_symbol **existp;
01023            struct known_symbol *newsym
01024              = (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
01025                                                  sizeof (*newsym));
01026            if (newsym == NULL)
01027              error (EXIT_FAILURE, errno, _("cannot allocate symbol data"));
01028 
01029            newsym->name = &shobj->strtab[sym->st_name];
01030            newsym->addr = sym->st_value;
01031            newsym->size = sym->st_size;
01032            newsym->weak = ELFW(ST_BIND) (sym->st_info) == STB_WEAK;
01033            newsym->hidden = (ELFW(ST_VISIBILITY) (sym->st_other)
01034                            != STV_DEFAULT);
01035            newsym->ticks = 0;
01036            newsym->calls = 0;
01037 
01038            existp = tfind (newsym, &symroot, symorder);
01039            if (existp == NULL)
01040              {
01041               /* New function.  */
01042               tsearch (newsym, &symroot, symorder);
01043               ++n;
01044              }
01045            else
01046              {
01047               /* The function is already defined.  See whether we have
01048                  a better name here.  */
01049               if (((*existp)->hidden && !newsym->hidden)
01050                   || ((*existp)->name[0] == '_' && newsym->name[0] != '_')
01051                   || ((*existp)->name[0] != '_' && newsym->name[0] != '_'
01052                      && ((*existp)->weak && !newsym->weak)))
01053                 *existp = newsym;
01054               else
01055                 /* We don't need the allocated memory.  */
01056                 obstack_free (&shobj->ob_sym, newsym);
01057              }
01058          }
01059     }
01060   else
01061     {
01062       /* Blarg, the binary is stripped.  We have to rely on the
01063         information contained in the dynamic section of the object.  */
01064       const ElfW(Sym) *symtab = (ElfW(Sym) *) D_PTR (shobj->map,
01065                                                l_info[DT_SYMTAB]);
01066       const char *strtab = (const char *) D_PTR (shobj->map,
01067                                            l_info[DT_STRTAB]);
01068 
01069       /* We assume that the string table follows the symbol table,
01070         because there is no way in ELF to know the size of the
01071         dynamic symbol table without looking at the section headers.  */
01072       while ((void *) symtab < (void *) strtab)
01073        {
01074          if ((ELFW(ST_TYPE)(symtab->st_info) == STT_FUNC
01075               || ELFW(ST_TYPE)(symtab->st_info) == STT_NOTYPE)
01076              && symtab->st_size != 0)
01077            {
01078              struct known_symbol *newsym;
01079              struct known_symbol **existp;
01080 
01081              newsym =
01082               (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
01083                                                  sizeof (*newsym));
01084              if (newsym == NULL)
01085               error (EXIT_FAILURE, errno, _("cannot allocate symbol data"));
01086 
01087              newsym->name = &strtab[symtab->st_name];
01088              newsym->addr = symtab->st_value;
01089              newsym->size = symtab->st_size;
01090              newsym->weak = ELFW(ST_BIND) (symtab->st_info) == STB_WEAK;
01091              newsym->hidden = (ELFW(ST_VISIBILITY) (symtab->st_other)
01092                             != STV_DEFAULT);
01093              newsym->ticks = 0;
01094              newsym->froms = NULL;
01095              newsym->tos = NULL;
01096 
01097              existp = tfind (newsym, &symroot, symorder);
01098              if (existp == NULL)
01099               {
01100                 /* New function.  */
01101                 tsearch (newsym, &symroot, symorder);
01102                 ++n;
01103               }
01104              else
01105               {
01106                 /* The function is already defined.  See whether we have
01107                    a better name here.  */
01108                 if (((*existp)->hidden && !newsym->hidden)
01109                     || ((*existp)->name[0] == '_' && newsym->name[0] != '_')
01110                     || ((*existp)->name[0] != '_' && newsym->name[0] != '_'
01111                        && ((*existp)->weak && !newsym->weak)))
01112                   *existp = newsym;
01113                 else
01114                   /* We don't need the allocated memory.  */
01115                   obstack_free (&shobj->ob_sym, newsym);
01116               }
01117            }
01118 
01119          ++symtab;
01120        }
01121     }
01122 
01123   sortsym = malloc (n * sizeof (struct known_symbol *));
01124   if (sortsym == NULL)
01125     abort ();
01126 
01127   twalk (symroot, printsym);
01128 }
01129 
01130 
01131 static void
01132 add_arcs (struct profdata *profdata)
01133 {
01134   uint32_t narcs = profdata->narcs;
01135   struct here_cg_arc_record *data = profdata->data;
01136   uint32_t cnt;
01137 
01138   for (cnt = 0; cnt < narcs; ++cnt)
01139     {
01140       /* First add the incoming arc.  */
01141       size_t sym_idx = find_symbol (data[cnt].self_pc);
01142 
01143       if (sym_idx != (size_t) -1l)
01144        {
01145          struct known_symbol *sym = sortsym[sym_idx];
01146          struct arc_list *runp = sym->froms;
01147 
01148          while (runp != NULL
01149                && ((data[cnt].from_pc == 0 && runp->idx != (size_t) -1l)
01150                    || (data[cnt].from_pc != 0
01151                       && (runp->idx == (size_t) -1l
01152                           || data[cnt].from_pc < sortsym[runp->idx]->addr
01153                           || (data[cnt].from_pc
01154                              >= (sortsym[runp->idx]->addr
01155                                  + sortsym[runp->idx]->size))))))
01156            runp = runp->next;
01157 
01158          if (runp == NULL)
01159            {
01160              /* We need a new entry.  */
01161              struct arc_list *newp = (struct arc_list *)
01162               obstack_alloc (&ob_list, sizeof (struct arc_list));
01163 
01164              if (data[cnt].from_pc == 0)
01165               newp->idx = (size_t) -1l;
01166              else
01167               newp->idx = find_symbol (data[cnt].from_pc);
01168              newp->count = data[cnt].count;
01169              newp->next = sym->froms;
01170              sym->froms = newp;
01171            }
01172          else
01173            /* Increment the counter for the found entry.  */
01174            runp->count += data[cnt].count;
01175        }
01176 
01177       /* Now add it to the appropriate outgoing list.  */
01178       sym_idx = find_symbol (data[cnt].from_pc);
01179       if (sym_idx != (size_t) -1l)
01180        {
01181          struct known_symbol *sym = sortsym[sym_idx];
01182          struct arc_list *runp = sym->tos;
01183 
01184          while (runp != NULL
01185                && (runp->idx == (size_t) -1l
01186                    || data[cnt].self_pc < sortsym[runp->idx]->addr
01187                    || data[cnt].self_pc >= (sortsym[runp->idx]->addr
01188                                          + sortsym[runp->idx]->size)))
01189            runp = runp->next;
01190 
01191          if (runp == NULL)
01192            {
01193              /* We need a new entry.  */
01194              struct arc_list *newp = (struct arc_list *)
01195               obstack_alloc (&ob_list, sizeof (struct arc_list));
01196 
01197              newp->idx = find_symbol (data[cnt].self_pc);
01198              newp->count = data[cnt].count;
01199              newp->next = sym->tos;
01200              sym->tos = newp;
01201            }
01202          else
01203            /* Increment the counter for the found entry.  */
01204            runp->count += data[cnt].count;
01205        }
01206     }
01207 }
01208 
01209 
01210 static int
01211 countorder (const void *p1, const void *p2)
01212 {
01213   struct known_symbol *s1 = (struct known_symbol *) p1;
01214   struct known_symbol *s2 = (struct known_symbol *) p2;
01215 
01216   if (s1->ticks != s2->ticks)
01217     return (int) (s2->ticks - s1->ticks);
01218 
01219   if (s1->calls != s2->calls)
01220     return (int) (s2->calls - s1->calls);
01221 
01222   return strcmp (s1->name, s2->name);
01223 }
01224 
01225 
01226 static double tick_unit;
01227 static uintmax_t cumu_ticks;
01228 
01229 static void
01230 printflat (const void *node, VISIT value, int level)
01231 {
01232   if (value == leaf || value == postorder)
01233     {
01234       struct known_symbol *s = *(struct known_symbol **) node;
01235 
01236       cumu_ticks += s->ticks;
01237 
01238       printf ("%6.2f%10.2f%9.2f%9" PRIdMAX "%9.2f           %s\n",
01239              total_ticks ? (100.0 * s->ticks) / total_ticks : 0.0,
01240              tick_unit * cumu_ticks,
01241              tick_unit * s->ticks,
01242              s->calls,
01243              s->calls ? (s->ticks * 1000000) * tick_unit / s->calls : 0,
01244              /* FIXME: don't know about called functions.  */
01245              s->name);
01246     }
01247 }
01248 
01249 
01250 /* ARGUSED */
01251 static void
01252 freenoop (void *p)
01253 {
01254 }
01255 
01256 
01257 static void
01258 generate_flat_profile (struct profdata *profdata)
01259 {
01260   size_t n;
01261   void *data = NULL;
01262 
01263   tick_unit = 1.0 / *(uint32_t *) profdata->hist_hdr->prof_rate;
01264 
01265   printf ("Flat profile:\n\n"
01266          "Each sample counts as %g %s.\n",
01267          tick_unit, profdata->hist_hdr->dimen);
01268   fputs ("  %   cumulative   self              self     total\n"
01269         " time   seconds   seconds    calls  us/call  us/call  name\n",
01270         stdout);
01271 
01272   for (n = 0; n < symidx; ++n)
01273     if (sortsym[n]->calls != 0 || sortsym[n]->ticks != 0)
01274       tsearch (sortsym[n], &data, countorder);
01275 
01276   twalk (data, printflat);
01277 
01278   tdestroy (data, freenoop);
01279 }
01280 
01281 
01282 static void
01283 generate_call_graph (struct profdata *profdata)
01284 {
01285   size_t cnt;
01286 
01287   puts ("\nindex % time    self  children    called     name\n");
01288 
01289   for (cnt = 0; cnt < symidx; ++cnt)
01290     if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL)
01291       {
01292        struct arc_list *runp;
01293        size_t n;
01294 
01295        /* First print the from-information.  */
01296        runp = sortsym[cnt]->froms;
01297        while (runp != NULL)
01298          {
01299            printf ("            %8.2f%8.2f%9" PRIdMAX "/%-9" PRIdMAX "   %s",
01300                   (runp->idx != (size_t) -1l
01301                    ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
01302                   0.0, /* FIXME: what's time for the children, recursive */
01303                   runp->count, sortsym[cnt]->calls,
01304                   (runp->idx != (size_t) -1l ?
01305                    sortsym[runp->idx]->name : "<UNKNOWN>"));
01306 
01307            if (runp->idx != (size_t) -1l)
01308              printf (" [%Zd]", runp->idx);
01309            putchar_unlocked ('\n');
01310 
01311            runp = runp->next;
01312          }
01313 
01314        /* Info abount the function itself.  */
01315        n = printf ("[%Zu]", cnt);
01316        printf ("%*s%5.1f%8.2f%8.2f%9" PRIdMAX "         %s [%Zd]\n",
01317               (int) (7 - n), " ",
01318               total_ticks ? (100.0 * sortsym[cnt]->ticks) / total_ticks : 0,
01319               sortsym[cnt]->ticks * tick_unit,
01320               0.0, /* FIXME: what's time for the children, recursive */
01321               sortsym[cnt]->calls,
01322               sortsym[cnt]->name, cnt);
01323 
01324        /* Info about the functions this function calls.  */
01325        runp = sortsym[cnt]->tos;
01326        while (runp != NULL)
01327          {
01328            printf ("            %8.2f%8.2f%9" PRIdMAX "/",
01329                   (runp->idx != (size_t) -1l
01330                    ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
01331                   0.0, /* FIXME: what's time for the children, recursive */
01332                   runp->count);
01333 
01334            if (runp->idx != (size_t) -1l)
01335              printf ("%-9" PRIdMAX "   %s [%Zd]\n",
01336                     sortsym[runp->idx]->calls,
01337                     sortsym[runp->idx]->name,
01338                     runp->idx);
01339            else
01340              fputs ("???         <UNKNOWN>\n\n", stdout);
01341 
01342            runp = runp->next;
01343          }
01344 
01345        fputs ("-----------------------------------------------\n", stdout);
01346       }
01347 }
01348 
01349 
01350 static void
01351 generate_call_pair_list (struct profdata *profdata)
01352 {
01353   size_t cnt;
01354 
01355   for (cnt = 0; cnt < symidx; ++cnt)
01356     if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL)
01357       {
01358        struct arc_list *runp;
01359 
01360        /* First print the incoming arcs.  */
01361        runp = sortsym[cnt]->froms;
01362        while (runp != NULL)
01363          {
01364            if (runp->idx == (size_t) -1l)
01365              printf ("\
01366 <UNKNOWN>                          %-34s %9" PRIdMAX "\n",
01367                     sortsym[cnt]->name, runp->count);
01368            runp = runp->next;
01369          }
01370 
01371        /* Next the outgoing arcs.  */
01372        runp = sortsym[cnt]->tos;
01373        while (runp != NULL)
01374          {
01375            printf ("%-34s %-34s %9" PRIdMAX "\n",
01376                   sortsym[cnt]->name,
01377                   (runp->idx != (size_t) -1l
01378                    ? sortsym[runp->idx]->name : "<UNKNOWN>"),
01379                   runp->count);
01380            runp = runp->next;
01381          }
01382       }
01383 }