* elf/dl-misc.c (_dl_sysdep_read_whole_file): We really don't need
[platform/upstream/glibc.git] / elf / sprof.c
1 /* Read and display shared object profiling data.
2    Copyright (C) 1997-2004, 2005, 2006, 2007 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <argp.h>
22 #include <dlfcn.h>
23 #include <elf.h>
24 #include <error.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <libintl.h>
28 #include <locale.h>
29 #include <obstack.h>
30 #include <search.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <ldsodefs.h>
37 #include <sys/gmon.h>
38 #include <sys/gmon_out.h>
39 #include <sys/mman.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42
43 /* Get libc version number.  */
44 #include "../version.h"
45
46 #define PACKAGE _libc_intl_domainname
47
48
49 #include <endian.h>
50 #if BYTE_ORDER == BIG_ENDIAN
51 # define byteorder ELFDATA2MSB
52 # define byteorder_name "big-endian"
53 #elif BYTE_ORDER == LITTLE_ENDIAN
54 # define byteorder ELFDATA2LSB
55 # define byteorder_name "little-endian"
56 #else
57 # error "Unknown BYTE_ORDER " BYTE_ORDER
58 # define byteorder ELFDATANONE
59 #endif
60
61 #ifndef PATH_MAX
62 # define PATH_MAX 1024
63 #endif
64
65
66 extern int __profile_frequency (void);
67
68 /* Name and version of program.  */
69 static void print_version (FILE *stream, struct argp_state *state);
70 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
71
72 #define OPT_TEST        1
73
74 /* Definitions of arguments for argp functions.  */
75 static const struct argp_option options[] =
76 {
77   { NULL, 0, NULL, 0, N_("Output selection:") },
78   { "call-pairs", 'c', NULL, 0,
79     N_("print list of count paths and their number of use") },
80   { "flat-profile", 'p', NULL, 0,
81     N_("generate flat profile with counts and ticks") },
82   { "graph", 'q', NULL, 0, N_("generate call graph") },
83
84   { "test", OPT_TEST, NULL, OPTION_HIDDEN, NULL },
85   { NULL, 0, NULL, 0, NULL }
86 };
87
88 /* Short description of program.  */
89 static const char doc[] = N_("Read and display shared object profiling data.\v\
90 For bug reporting instructions, please see:\n\
91 <http://www.gnu.org/software/libc/bugs.html>.\n");
92
93 /* Strings for arguments in help texts.  */
94 static const char args_doc[] = N_("SHOBJ [PROFDATA]");
95
96 /* Prototype for option handler.  */
97 static error_t parse_opt (int key, char *arg, struct argp_state *state);
98
99 /* Data structure to communicate with argp functions.  */
100 static struct argp argp =
101 {
102   options, parse_opt, args_doc, doc
103 };
104
105
106 /* Operation modes.  */
107 static enum
108 {
109   NONE = 0,
110   FLAT_MODE = 1 << 0,
111   CALL_GRAPH_MODE = 1 << 1,
112   CALL_PAIRS = 1 << 2,
113
114   DEFAULT_MODE = FLAT_MODE | CALL_GRAPH_MODE
115 } mode;
116
117 /* Nozero for testing.  */
118 static int do_test;
119
120 /* Strcuture describing calls.  */
121 struct here_fromstruct
122 {
123   struct here_cg_arc_record volatile *here;
124   uint16_t link;
125 };
126
127 /* We define a special type to address the elements of the arc table.
128    This is basically the `gmon_cg_arc_record' format but it includes
129    the room for the tag and it uses real types.  */
130 struct here_cg_arc_record
131 {
132   uintptr_t from_pc;
133   uintptr_t self_pc;
134   uint32_t count;
135 } __attribute__ ((packed));
136
137
138 struct known_symbol;
139 struct arc_list
140 {
141   size_t idx;
142   uintmax_t count;
143
144   struct arc_list *next;
145 };
146
147 static struct obstack ob_list;
148
149
150 struct known_symbol
151 {
152   const char *name;
153   uintptr_t addr;
154   size_t size;
155   bool weak;
156   bool hidden;
157
158   uintmax_t ticks;
159   uintmax_t calls;
160
161   struct arc_list *froms;
162   struct arc_list *tos;
163 };
164
165
166 struct shobj
167 {
168   const char *name;             /* User-provided name.  */
169
170   struct link_map *map;
171   const char *dynstrtab;        /* Dynamic string table of shared object.  */
172   const char *soname;           /* Soname of shared object.  */
173
174   uintptr_t lowpc;
175   uintptr_t highpc;
176   unsigned long int kcountsize;
177   size_t expected_size;         /* Expected size of profiling file.  */
178   size_t tossize;
179   size_t fromssize;
180   size_t fromlimit;
181   unsigned int hashfraction;
182   int s_scale;
183
184   void *symbol_map;
185   size_t symbol_mapsize;
186   const ElfW(Sym) *symtab;
187   size_t symtab_size;
188   const char *strtab;
189
190   struct obstack ob_str;
191   struct obstack ob_sym;
192 };
193
194
195 struct profdata
196 {
197   void *addr;
198   off_t size;
199
200   char *hist;
201   struct gmon_hist_hdr *hist_hdr;
202   uint16_t *kcount;
203   uint32_t narcs;               /* Number of arcs in toset.  */
204   struct here_cg_arc_record *data;
205   uint16_t *tos;
206   struct here_fromstruct *froms;
207 };
208
209 /* Search tree for symbols.  */
210 static void *symroot;
211 static struct known_symbol **sortsym;
212 static size_t symidx;
213 static uintmax_t total_ticks;
214
215 /* Prototypes for local functions.  */
216 static struct shobj *load_shobj (const char *name);
217 static void unload_shobj (struct shobj *shobj);
218 static struct profdata *load_profdata (const char *name, struct shobj *shobj);
219 static void unload_profdata (struct profdata *profdata);
220 static void count_total_ticks (struct shobj *shobj, struct profdata *profdata);
221 static void count_calls (struct shobj *shobj, struct profdata *profdata);
222 static void read_symbols (struct shobj *shobj);
223 static void add_arcs (struct profdata *profdata);
224 static void generate_flat_profile (struct profdata *profdata);
225 static void generate_call_graph (struct profdata *profdata);
226 static void generate_call_pair_list (struct profdata *profdata);
227
228
229 int
230 main (int argc, char *argv[])
231 {
232   const char *shobj;
233   const char *profdata;
234   struct shobj *shobj_handle;
235   struct profdata *profdata_handle;
236   int remaining;
237
238   setlocale (LC_ALL, "");
239
240   /* Initialize the message catalog.  */
241   textdomain (_libc_intl_domainname);
242
243   /* Parse and process arguments.  */
244   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
245
246   if (argc - remaining == 0 || argc - remaining > 2)
247     {
248       /* We need exactly two non-option parameter.  */
249       argp_help (&argp, stdout, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
250                  program_invocation_short_name);
251       exit (1);
252     }
253
254   /* Get parameters.  */
255   shobj = argv[remaining];
256   if (argc - remaining == 2)
257     profdata = argv[remaining + 1];
258   else
259     /* No filename for the profiling data given.  We will determine it
260        from the soname of the shobj, later.  */
261     profdata = NULL;
262
263   /* First see whether we can load the shared object.  */
264   shobj_handle = load_shobj (shobj);
265   if (shobj_handle == NULL)
266     exit (1);
267
268   /* We can now determine the filename for the profiling data, if
269      nececessary.  */
270   if (profdata == NULL)
271     {
272       char *newp;
273       const char *soname;
274       size_t soname_len;
275
276       soname = shobj_handle->soname ?: basename (shobj);
277       soname_len = strlen (soname);
278       newp = (char *) alloca (soname_len + sizeof ".profile");
279       stpcpy (mempcpy (newp, soname, soname_len), ".profile");
280       profdata = newp;
281     }
282
283   /* Now see whether the profiling data file matches the given object.   */
284   profdata_handle = load_profdata (profdata, shobj_handle);
285   if (profdata_handle == NULL)
286     {
287       unload_shobj (shobj_handle);
288
289       exit (1);
290     }
291
292   read_symbols (shobj_handle);
293
294   /* Count the ticks.  */
295   count_total_ticks (shobj_handle, profdata_handle);
296
297   /* Count the calls.  */
298   count_calls (shobj_handle, profdata_handle);
299
300   /* Add the arc information.  */
301   add_arcs (profdata_handle);
302
303   /* If no mode is specified fall back to the default mode.  */
304   if (mode == NONE)
305     mode = DEFAULT_MODE;
306
307   /* Do some work.  */
308   if (mode & FLAT_MODE)
309     generate_flat_profile (profdata_handle);
310
311   if (mode & CALL_GRAPH_MODE)
312     generate_call_graph (profdata_handle);
313
314   if (mode & CALL_PAIRS)
315     generate_call_pair_list (profdata_handle);
316
317   /* Free the resources.  */
318   unload_shobj (shobj_handle);
319   unload_profdata (profdata_handle);
320
321   return 0;
322 }
323
324
325 /* Handle program arguments.  */
326 static error_t
327 parse_opt (int key, char *arg, struct argp_state *state)
328 {
329   switch (key)
330     {
331     case 'c':
332       mode |= CALL_PAIRS;
333       break;
334     case 'p':
335       mode |= FLAT_MODE;
336       break;
337     case 'q':
338       mode |= CALL_GRAPH_MODE;
339       break;
340     case OPT_TEST:
341       do_test = 1;
342       break;
343     default:
344       return ARGP_ERR_UNKNOWN;
345     }
346   return 0;
347 }
348
349
350 /* Print the version information.  */
351 static void
352 print_version (FILE *stream, struct argp_state *state)
353 {
354   fprintf (stream, "sprof (GNU %s) %s\n", PACKAGE, VERSION);
355   fprintf (stream, gettext ("\
356 Copyright (C) %s Free Software Foundation, Inc.\n\
357 This is free software; see the source for copying conditions.  There is NO\n\
358 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
359 "),
360            "2007");
361   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
362 }
363
364
365 /* Note that we must not use `dlopen' etc.  The shobj object must not
366    be loaded for use.  */
367 static struct shobj *
368 load_shobj (const char *name)
369 {
370   struct link_map *map = NULL;
371   struct shobj *result;
372   ElfW(Addr) mapstart = ~((ElfW(Addr)) 0);
373   ElfW(Addr) mapend = 0;
374   const ElfW(Phdr) *ph;
375   size_t textsize;
376   unsigned int log_hashfraction;
377   ElfW(Ehdr) *ehdr;
378   int fd;
379   ElfW(Shdr) *shdr;
380   size_t pagesize = getpagesize ();
381
382   /* Since we use dlopen() we must be prepared to work around the sometimes
383      strange lookup rules for the shared objects.  If we have a file foo.so
384      in the current directory and the user specfies foo.so on the command
385      line (without specifying a directory) we should load the file in the
386      current directory even if a normal dlopen() call would read the other
387      file.  We do this by adding a directory portion to the name.  */
388   if (strchr (name, '/') == NULL)
389     {
390       char *load_name = (char *) alloca (strlen (name) + 3);
391       stpcpy (stpcpy (load_name, "./"), name);
392
393       map = (struct link_map *) dlopen (load_name, RTLD_LAZY | __RTLD_SPROF);
394     }
395   if (map == NULL)
396     {
397       map = (struct link_map *) dlopen (name, RTLD_LAZY | __RTLD_SPROF);
398       if (map == NULL)
399         {
400           error (0, errno, _("failed to load shared object `%s'"), name);
401           return NULL;
402         }
403     }
404
405   /* Prepare the result.  */
406   result = (struct shobj *) calloc (1, sizeof (struct shobj));
407   if (result == NULL)
408     {
409       error (0, errno, _("cannot create internal descriptors"));
410       dlclose (map);
411       return NULL;
412     }
413   result->name = name;
414   result->map = map;
415
416   /* Compute the size of the sections which contain program code.
417      This must match the code in dl-profile.c (_dl_start_profile).  */
418   for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
419     if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
420       {
421         ElfW(Addr) start = (ph->p_vaddr & ~(pagesize - 1));
422         ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
423                           & ~(pagesize - 1));
424
425         if (start < mapstart)
426           mapstart = start;
427         if (end > mapend)
428           mapend = end;
429       }
430
431   result->lowpc = ROUNDDOWN ((uintptr_t) (mapstart + map->l_addr),
432                              HISTFRACTION * sizeof (HISTCOUNTER));
433   result->highpc = ROUNDUP ((uintptr_t) (mapend + map->l_addr),
434                             HISTFRACTION * sizeof (HISTCOUNTER));
435   if (do_test)
436     printf ("load addr: %0#*" PRIxPTR "\n"
437             "lower bound PC: %0#*" PRIxPTR "\n"
438             "upper bound PC: %0#*" PRIxPTR "\n",
439             __ELF_NATIVE_CLASS == 32 ? 10 : 18, map->l_addr,
440             __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->lowpc,
441             __ELF_NATIVE_CLASS == 32 ? 10 : 18, result->highpc);
442
443   textsize = result->highpc - result->lowpc;
444   result->kcountsize = textsize / HISTFRACTION;
445   result->hashfraction = HASHFRACTION;
446   if ((HASHFRACTION & (HASHFRACTION - 1)) == 0)
447     /* If HASHFRACTION is a power of two, mcount can use shifting
448        instead of integer division.  Precompute shift amount.  */
449     log_hashfraction = __builtin_ffs (result->hashfraction
450                                       * sizeof (struct here_fromstruct)) - 1;
451   else
452     log_hashfraction = -1;
453   if (do_test)
454     printf ("hashfraction = %d\ndivider = %Zu\n",
455             result->hashfraction,
456             result->hashfraction * sizeof (struct here_fromstruct));
457   result->tossize = textsize / HASHFRACTION;
458   result->fromlimit = textsize * ARCDENSITY / 100;
459   if (result->fromlimit < MINARCS)
460     result->fromlimit = MINARCS;
461   if (result->fromlimit > MAXARCS)
462     result->fromlimit = MAXARCS;
463   result->fromssize = result->fromlimit * sizeof (struct here_fromstruct);
464
465   result->expected_size = (sizeof (struct gmon_hdr)
466                            + 4 + sizeof (struct gmon_hist_hdr)
467                            + result->kcountsize
468                            + 4 + 4
469                            + (result->fromssize
470                               * sizeof (struct here_cg_arc_record)));
471
472   if (do_test)
473     printf ("expected size: %Zd\n", result->expected_size);
474
475 #define SCALE_1_TO_1    0x10000L
476
477   if (result->kcountsize < result->highpc - result->lowpc)
478     {
479       size_t range = result->highpc - result->lowpc;
480       size_t quot = range / result->kcountsize;
481
482       if (quot >= SCALE_1_TO_1)
483         result->s_scale = 1;
484       else if (quot >= SCALE_1_TO_1 / 256)
485         result->s_scale = SCALE_1_TO_1 / quot;
486       else if (range > ULONG_MAX / 256)
487         result->s_scale = ((SCALE_1_TO_1 * 256)
488                            / (range / (result->kcountsize / 256)));
489       else
490         result->s_scale = ((SCALE_1_TO_1 * 256)
491                            / ((range * 256) / result->kcountsize));
492     }
493   else
494     result->s_scale = SCALE_1_TO_1;
495
496   if (do_test)
497     printf ("s_scale: %d\n", result->s_scale);
498
499   /* Determine the dynamic string table.  */
500   if (map->l_info[DT_STRTAB] == NULL)
501     result->dynstrtab = NULL;
502   else
503     result->dynstrtab = (const char *) D_PTR (map, l_info[DT_STRTAB]);
504   if (do_test)
505     printf ("string table: %p\n", result->dynstrtab);
506
507   /* Determine the soname.  */
508   if (map->l_info[DT_SONAME] == NULL)
509     result->soname = NULL;
510   else
511     result->soname = result->dynstrtab + map->l_info[DT_SONAME]->d_un.d_val;
512   if (do_test && result->soname != NULL)
513     printf ("soname: %s\n", result->soname);
514
515   /* Now we have to load the symbol table.
516
517      First load the section header table.  */
518   ehdr = (ElfW(Ehdr) *) map->l_map_start;
519
520   /* Make sure we are on the right party.  */
521   if (ehdr->e_shentsize != sizeof (ElfW(Shdr)))
522     abort ();
523
524   /* And we need the shared object file descriptor again.  */
525   fd = open (map->l_name, O_RDONLY);
526   if (fd == -1)
527     /* Dooh, this really shouldn't happen.  We know the file is available.  */
528     error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"),
529            map->l_name);
530
531   /* Map the section header.  */
532   size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr));
533   shdr = (ElfW(Shdr) *) alloca (size);
534   if (pread (fd, shdr, size, ehdr->e_shoff) != size)
535     error (EXIT_FAILURE, errno, _("reading of section headers failed"));
536
537   /* Get the section header string table.  */
538   char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size);
539   if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size,
540              shdr[ehdr->e_shstrndx].sh_offset)
541       != shdr[ehdr->e_shstrndx].sh_size)
542     error (EXIT_FAILURE, errno,
543            _("reading of section header string table failed"));
544
545   /* Search for the ".symtab" section.  */
546   ElfW(Shdr) *symtab_entry = NULL;
547   ElfW(Shdr) *debuglink_entry = NULL;
548   for (int idx = 0; idx < ehdr->e_shnum; ++idx)
549     if (shdr[idx].sh_type == SHT_SYMTAB
550         && strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0)
551       {
552         symtab_entry = &shdr[idx];
553         break;
554       }
555     else if (shdr[idx].sh_type == SHT_PROGBITS
556              && strcmp (shstrtab + shdr[idx].sh_name, ".gnu_debuglink") == 0)
557       debuglink_entry = &shdr[idx];
558
559   /* Get the file name of the debuginfo file if necessary.  */
560   int symfd = fd;
561   if (symtab_entry == NULL && debuglink_entry != NULL)
562     {
563       size_t size = debuglink_entry->sh_size;
564       char *debuginfo_fname = (char *) alloca (size + 1);
565       debuginfo_fname[size] = '\0';
566       if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset)
567           != size)
568         {
569           fprintf (stderr, _("*** Cannot read debuginfo file name: %m\n"));
570           goto no_debuginfo;
571         }
572
573       static const char procpath[] = "/proc/self/fd/%d";
574       char origprocname[sizeof (procpath) + sizeof (int) * 3];
575       snprintf (origprocname, sizeof (origprocname), procpath, fd);
576       char *origlink = (char *) alloca (PATH_MAX + 1);
577       origlink[PATH_MAX] = '\0';
578       if (readlink (origprocname, origlink, PATH_MAX) == -1)
579         goto no_debuginfo;
580
581       /* Try to find the actual file.  There are three places:
582          1. the same directory the DSO is in
583          2. in a subdir named .debug of the directory the DSO is in
584          3. in /usr/lib/debug/PATH-OF-DSO
585       */
586       char *realname = canonicalize_file_name (origlink);
587       char *cp = NULL;
588       if (realname == NULL || (cp = strrchr (realname, '/')) == NULL)
589         error (EXIT_FAILURE, errno, _("cannot determine file name"));
590
591       /* Leave the last slash in place.  */
592       *++cp = '\0';
593
594       /* First add the debuginfo file name only.  */
595       static const char usrlibdebug[]= "/usr/lib/debug/";
596       char *workbuf = (char *) alloca (sizeof (usrlibdebug)
597                                        + (cp - realname)
598                                        + strlen (debuginfo_fname));
599       strcpy (stpcpy (workbuf, realname), debuginfo_fname);
600
601       int fd2 = open (workbuf, O_RDONLY);
602       if (fd2 == -1)
603         {
604           strcpy (stpcpy (stpcpy (workbuf, realname), ".debug/"),
605                   debuginfo_fname);
606           fd2 = open (workbuf, O_RDONLY);
607           if (fd2 == -1)
608             {
609               strcpy (stpcpy (stpcpy (workbuf, usrlibdebug), realname),
610                       debuginfo_fname);
611               fd2 = open (workbuf, O_RDONLY);
612             }
613         }
614
615       if (fd2 != -1)
616         {
617           ElfW(Ehdr) ehdr2;
618
619           /* Read the ELF header.  */
620           if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2))
621             error (EXIT_FAILURE, errno,
622                    _("reading of ELF header failed"));
623
624           /* Map the section header.  */
625           size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr));
626           ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size);
627           if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size)
628             error (EXIT_FAILURE, errno,
629                    _("reading of section headers failed"));
630
631           /* Get the section header string table.  */
632           shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size);
633           if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size,
634                      shdr2[ehdr2.e_shstrndx].sh_offset)
635               != shdr2[ehdr2.e_shstrndx].sh_size)
636             error (EXIT_FAILURE, errno,
637                    _("reading of section header string table failed"));
638
639           /* Search for the ".symtab" section.  */
640           for (int idx = 0; idx < ehdr2.e_shnum; ++idx)
641             if (shdr2[idx].sh_type == SHT_SYMTAB
642                 && strcmp (shstrtab + shdr2[idx].sh_name, ".symtab") == 0)
643               {
644                 symtab_entry = &shdr2[idx];
645                 shdr = shdr2;
646                 symfd = fd2;
647                 break;
648               }
649
650           if  (fd2 != symfd)
651             close (fd2);
652         }
653     }
654
655  no_debuginfo:
656   if (symtab_entry == NULL)
657     {
658       fprintf (stderr, _("\
659 *** The file `%s' is stripped: no detailed analysis possible\n"),
660               name);
661       result->symtab = NULL;
662       result->strtab = NULL;
663     }
664   else
665     {
666       ElfW(Off) min_offset, max_offset;
667       ElfW(Shdr) *strtab_entry;
668
669       strtab_entry = &shdr[symtab_entry->sh_link];
670
671       /* Find the minimum and maximum offsets that include both the symbol
672          table and the string table.  */
673       if (symtab_entry->sh_offset < strtab_entry->sh_offset)
674         {
675           min_offset = symtab_entry->sh_offset & ~(pagesize - 1);
676           max_offset = strtab_entry->sh_offset + strtab_entry->sh_size;
677         }
678       else
679         {
680           min_offset = strtab_entry->sh_offset & ~(pagesize - 1);
681           max_offset = symtab_entry->sh_offset + symtab_entry->sh_size;
682         }
683
684       result->symbol_map = mmap (NULL, max_offset - min_offset,
685                                  PROT_READ, MAP_SHARED|MAP_FILE, symfd,
686                                  min_offset);
687       if (result->symbol_map == MAP_FAILED)
688         error (EXIT_FAILURE, errno, _("failed to load symbol data"));
689
690       result->symtab
691         = (const ElfW(Sym) *) ((const char *) result->symbol_map
692                                + (symtab_entry->sh_offset - min_offset));
693       result->symtab_size = symtab_entry->sh_size;
694       result->strtab = ((const char *) result->symbol_map
695                         + (strtab_entry->sh_offset - min_offset));
696       result->symbol_mapsize = max_offset - min_offset;
697     }
698
699   /* Free the descriptor for the shared object.  */
700   close (fd);
701   if (symfd != fd)
702     close (symfd);
703
704   return result;
705 }
706
707
708 static void
709 unload_shobj (struct shobj *shobj)
710 {
711   munmap (shobj->symbol_map, shobj->symbol_mapsize);
712   dlclose (shobj->map);
713 }
714
715
716 static struct profdata *
717 load_profdata (const char *name, struct shobj *shobj)
718 {
719   struct profdata *result;
720   int fd;
721   struct stat st;
722   void *addr;
723   struct gmon_hdr gmon_hdr;
724   struct gmon_hist_hdr hist_hdr;
725   uint32_t *narcsp;
726   size_t fromlimit;
727   struct here_cg_arc_record *data;
728   struct here_fromstruct *froms;
729   uint16_t *tos;
730   size_t fromidx;
731   size_t idx;
732
733   fd = open (name, O_RDONLY);
734   if (fd == -1)
735     {
736       char *ext_name;
737
738       if (errno != ENOENT || strchr (name, '/') != NULL)
739         /* The file exists but we are not allowed to read it or the
740            file does not exist and the name includes a path
741            specification..  */
742         return NULL;
743
744       /* A file with the given name does not exist in the current
745          directory, try it in the default location where the profiling
746          files are created.  */
747       ext_name = (char *) alloca (strlen (name) + sizeof "/var/tmp/");
748       stpcpy (stpcpy (ext_name, "/var/tmp/"), name);
749       name = ext_name;
750
751       fd = open (ext_name, O_RDONLY);
752       if (fd == -1)
753         {
754           /* Even this file does not exist.  */
755           error (0, errno, _("cannot load profiling data"));
756           return NULL;
757         }
758     }
759
760   /* We have found the file, now make sure it is the right one for the
761      data file.  */
762   if (fstat (fd, &st) < 0)
763     {
764       error (0, errno, _("while stat'ing profiling data file"));
765       close (fd);
766       return NULL;
767     }
768
769   if ((size_t) st.st_size != shobj->expected_size)
770     {
771       error (0, 0,
772              _("profiling data file `%s' does not match shared object `%s'"),
773              name, shobj->name);
774       close (fd);
775       return NULL;
776     }
777
778   /* The data file is most probably the right one for our shared
779      object.  Map it now.  */
780   addr = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
781   if (addr == MAP_FAILED)
782     {
783       error (0, errno, _("failed to mmap the profiling data file"));
784       close (fd);
785       return NULL;
786     }
787
788   /* We don't need the file desriptor anymore.  */
789   if (close (fd) < 0)
790     {
791       error (0, errno, _("error while closing the profiling data file"));
792       munmap (addr, st.st_size);
793       return NULL;
794     }
795
796   /* Prepare the result.  */
797   result = (struct profdata *) calloc (1, sizeof (struct profdata));
798   if (result == NULL)
799     {
800       error (0, errno, _("cannot create internal descriptor"));
801       munmap (addr, st.st_size);
802       return NULL;
803     }
804
805   /* Store the address and size so that we can later free the resources.  */
806   result->addr = addr;
807   result->size = st.st_size;
808
809   /* Pointer to data after the header.  */
810   result->hist = (char *) ((struct gmon_hdr *) addr + 1);
811   result->hist_hdr = (struct gmon_hist_hdr *) ((char *) result->hist
812                                                + sizeof (uint32_t));
813   result->kcount = (uint16_t *) ((char *) result->hist + sizeof (uint32_t)
814                                  + sizeof (struct gmon_hist_hdr));
815
816   /* Compute pointer to array of the arc information.  */
817   narcsp = (uint32_t *) ((char *) result->kcount + shobj->kcountsize
818                          + sizeof (uint32_t));
819   result->narcs = *narcsp;
820   result->data = (struct here_cg_arc_record *) ((char *) narcsp
821                                                 + sizeof (uint32_t));
822
823   /* Create the gmon_hdr we expect or write.  */
824   memset (&gmon_hdr, '\0', sizeof (struct gmon_hdr));
825   memcpy (&gmon_hdr.cookie[0], GMON_MAGIC, sizeof (gmon_hdr.cookie));
826   *(int32_t *) gmon_hdr.version = GMON_SHOBJ_VERSION;
827
828   /* Create the hist_hdr we expect or write.  */
829   *(char **) hist_hdr.low_pc = (char *) shobj->lowpc - shobj->map->l_addr;
830   *(char **) hist_hdr.high_pc = (char *) shobj->highpc - shobj->map->l_addr;
831   if (do_test)
832     printf ("low_pc = %p\nhigh_pc = %p\n",
833             *(char **) hist_hdr.low_pc, *(char **) hist_hdr.high_pc);
834   *(int32_t *) hist_hdr.hist_size = shobj->kcountsize / sizeof (HISTCOUNTER);
835   *(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
836   strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
837   hist_hdr.dimen_abbrev = 's';
838
839   /* Test whether the header of the profiling data is ok.  */
840   if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
841       || *(uint32_t *) result->hist != GMON_TAG_TIME_HIST
842       || memcmp (result->hist_hdr, &hist_hdr,
843                  sizeof (struct gmon_hist_hdr)) != 0
844       || narcsp[-1] != GMON_TAG_CG_ARC)
845     {
846       error (0, 0, _("`%s' is no correct profile data file for `%s'"),
847              name, shobj->name);
848       if (do_test)
849         {
850           if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0)
851             puts ("gmon_hdr differs");
852           if (*(uint32_t *) result->hist != GMON_TAG_TIME_HIST)
853             puts ("result->hist differs");
854           if (memcmp (result->hist_hdr, &hist_hdr,
855                       sizeof (struct gmon_hist_hdr)) != 0)
856             puts ("hist_hdr differs");
857           if (narcsp[-1] != GMON_TAG_CG_ARC)
858             puts ("narcsp[-1] differs");
859         }
860       free (result);
861       munmap (addr, st.st_size);
862       return NULL;
863     }
864
865   /* We are pretty sure now that this is a correct input file.  Set up
866      the remaining information in the result structure and return.  */
867   result->tos = (uint16_t *) calloc (shobj->tossize + shobj->fromssize, 1);
868   if (result->tos == NULL)
869     {
870       error (0, errno, _("cannot create internal descriptor"));
871       munmap (addr, st.st_size);
872       free (result);
873       return NULL;
874     }
875
876   result->froms = (struct here_fromstruct *) ((char *) result->tos
877                                               + shobj->tossize);
878   fromidx = 0;
879
880   /* Now we have to process all the arc count entries.  */
881   fromlimit = shobj->fromlimit;
882   data = result->data;
883   froms = result->froms;
884   tos = result->tos;
885   for (idx = 0; idx < MIN (*narcsp, fromlimit); ++idx)
886     {
887       size_t to_index;
888       size_t newfromidx;
889       to_index = (data[idx].self_pc / (shobj->hashfraction * sizeof (*tos)));
890       newfromidx = fromidx++;
891       froms[newfromidx].here = &data[idx];
892       froms[newfromidx].link = tos[to_index];
893       tos[to_index] = newfromidx;
894     }
895
896   return result;
897 }
898
899
900 static void
901 unload_profdata (struct profdata *profdata)
902 {
903   free (profdata->tos);
904   munmap (profdata->addr, profdata->size);
905   free (profdata);
906 }
907
908
909 static void
910 count_total_ticks (struct shobj *shobj, struct profdata *profdata)
911 {
912   volatile uint16_t *kcount = profdata->kcount;
913   size_t maxkidx = shobj->kcountsize;
914   size_t factor = 2 * (65536 / shobj->s_scale);
915   size_t kidx = 0;
916   size_t sidx = 0;
917
918   while (sidx < symidx)
919     {
920       uintptr_t start = sortsym[sidx]->addr;
921       uintptr_t end = start + sortsym[sidx]->size;
922
923       while (kidx < maxkidx && factor * kidx < start)
924         ++kidx;
925       if (kidx == maxkidx)
926         break;
927
928       while (kidx < maxkidx && factor * kidx < end)
929         sortsym[sidx]->ticks += kcount[kidx++];
930       if (kidx == maxkidx)
931         break;
932
933       total_ticks += sortsym[sidx++]->ticks;
934     }
935 }
936
937
938 static size_t
939 find_symbol (uintptr_t addr)
940 {
941   size_t sidx = 0;
942
943   while (sidx < symidx)
944     {
945       uintptr_t start = sortsym[sidx]->addr;
946       uintptr_t end = start + sortsym[sidx]->size;
947
948       if (addr >= start && addr < end)
949         return sidx;
950
951       if (addr < start)
952         break;
953
954       ++sidx;
955     }
956
957   return (size_t) -1l;
958 }
959
960
961 static void
962 count_calls (struct shobj *shobj, struct profdata *profdata)
963 {
964   struct here_cg_arc_record *data = profdata->data;
965   uint32_t narcs = profdata->narcs;
966   uint32_t cnt;
967
968   for (cnt = 0; cnt < narcs; ++cnt)
969     {
970       uintptr_t here = data[cnt].self_pc;
971       size_t symbol_idx;
972
973       /* Find the symbol for this address.  */
974       symbol_idx = find_symbol (here);
975       if (symbol_idx != (size_t) -1l)
976         sortsym[symbol_idx]->calls += data[cnt].count;
977     }
978 }
979
980
981 static int
982 symorder (const void *o1, const void *o2)
983 {
984   const struct known_symbol *p1 = (const struct known_symbol *) o1;
985   const struct known_symbol *p2 = (const struct known_symbol *) o2;
986
987   return p1->addr - p2->addr;
988 }
989
990
991 static void
992 printsym (const void *node, VISIT value, int level)
993 {
994   if (value == leaf || value == postorder)
995     sortsym[symidx++] = *(struct known_symbol **) node;
996 }
997
998
999 static void
1000 read_symbols (struct shobj *shobj)
1001 {
1002   int n = 0;
1003
1004   /* Initialize the obstacks.  */
1005 #define obstack_chunk_alloc malloc
1006 #define obstack_chunk_free free
1007   obstack_init (&shobj->ob_str);
1008   obstack_init (&shobj->ob_sym);
1009   obstack_init (&ob_list);
1010
1011   /* Process the symbols.  */
1012   if (shobj->symtab != NULL)
1013     {
1014       const ElfW(Sym) *sym = shobj->symtab;
1015       const ElfW(Sym) *sym_end
1016         = (const ElfW(Sym) *) ((const char *) sym + shobj->symtab_size);
1017       for (; sym < sym_end; sym++)
1018         if ((ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
1019              || ELFW(ST_TYPE) (sym->st_info) == STT_NOTYPE)
1020             && sym->st_size != 0)
1021           {
1022             struct known_symbol **existp;
1023             struct known_symbol *newsym
1024               = (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
1025                                                        sizeof (*newsym));
1026             if (newsym == NULL)
1027               error (EXIT_FAILURE, errno, _("cannot allocate symbol data"));
1028
1029             newsym->name = &shobj->strtab[sym->st_name];
1030             newsym->addr = sym->st_value;
1031             newsym->size = sym->st_size;
1032             newsym->weak = ELFW(ST_BIND) (sym->st_info) == STB_WEAK;
1033             newsym->hidden = (ELFW(ST_VISIBILITY) (sym->st_other)
1034                               != STV_DEFAULT);
1035             newsym->ticks = 0;
1036             newsym->calls = 0;
1037
1038             existp = tfind (newsym, &symroot, symorder);
1039             if (existp == NULL)
1040               {
1041                 /* New function.  */
1042                 tsearch (newsym, &symroot, symorder);
1043                 ++n;
1044               }
1045             else
1046               {
1047                 /* The function is already defined.  See whether we have
1048                    a better name here.  */
1049                 if (((*existp)->hidden && !newsym->hidden)
1050                     || ((*existp)->name[0] == '_' && newsym->name[0] != '_')
1051                     || ((*existp)->name[0] != '_' && newsym->name[0] != '_'
1052                         && ((*existp)->weak && !newsym->weak)))
1053                   *existp = newsym;
1054                 else
1055                   /* We don't need the allocated memory.  */
1056                   obstack_free (&shobj->ob_sym, newsym);
1057               }
1058           }
1059     }
1060   else
1061     {
1062       /* Blarg, the binary is stripped.  We have to rely on the
1063          information contained in the dynamic section of the object.  */
1064       const ElfW(Sym) *symtab = (ElfW(Sym) *) D_PTR (shobj->map,
1065                                                      l_info[DT_SYMTAB]);
1066       const char *strtab = (const char *) D_PTR (shobj->map,
1067                                                  l_info[DT_STRTAB]);
1068
1069       /* We assume that the string table follows the symbol table,
1070          because there is no way in ELF to know the size of the
1071          dynamic symbol table without looking at the section headers.  */
1072       while ((void *) symtab < (void *) strtab)
1073         {
1074           if ((ELFW(ST_TYPE)(symtab->st_info) == STT_FUNC
1075                || ELFW(ST_TYPE)(symtab->st_info) == STT_NOTYPE)
1076               && symtab->st_size != 0)
1077             {
1078               struct known_symbol *newsym;
1079               struct known_symbol **existp;
1080
1081               newsym =
1082                 (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
1083                                                        sizeof (*newsym));
1084               if (newsym == NULL)
1085                 error (EXIT_FAILURE, errno, _("cannot allocate symbol data"));
1086
1087               newsym->name = &strtab[symtab->st_name];
1088               newsym->addr = symtab->st_value;
1089               newsym->size = symtab->st_size;
1090               newsym->weak = ELFW(ST_BIND) (symtab->st_info) == STB_WEAK;
1091               newsym->hidden = (ELFW(ST_VISIBILITY) (symtab->st_other)
1092                                 != STV_DEFAULT);
1093               newsym->ticks = 0;
1094               newsym->froms = NULL;
1095               newsym->tos = NULL;
1096
1097               existp = tfind (newsym, &symroot, symorder);
1098               if (existp == NULL)
1099                 {
1100                   /* New function.  */
1101                   tsearch (newsym, &symroot, symorder);
1102                   ++n;
1103                 }
1104               else
1105                 {
1106                   /* The function is already defined.  See whether we have
1107                      a better name here.  */
1108                   if (((*existp)->hidden && !newsym->hidden)
1109                       || ((*existp)->name[0] == '_' && newsym->name[0] != '_')
1110                       || ((*existp)->name[0] != '_' && newsym->name[0] != '_'
1111                           && ((*existp)->weak && !newsym->weak)))
1112                     *existp = newsym;
1113                   else
1114                     /* We don't need the allocated memory.  */
1115                     obstack_free (&shobj->ob_sym, newsym);
1116                 }
1117             }
1118
1119           ++symtab;
1120         }
1121     }
1122
1123   sortsym = malloc (n * sizeof (struct known_symbol *));
1124   if (sortsym == NULL)
1125     abort ();
1126
1127   twalk (symroot, printsym);
1128 }
1129
1130
1131 static void
1132 add_arcs (struct profdata *profdata)
1133 {
1134   uint32_t narcs = profdata->narcs;
1135   struct here_cg_arc_record *data = profdata->data;
1136   uint32_t cnt;
1137
1138   for (cnt = 0; cnt < narcs; ++cnt)
1139     {
1140       /* First add the incoming arc.  */
1141       size_t sym_idx = find_symbol (data[cnt].self_pc);
1142
1143       if (sym_idx != (size_t) -1l)
1144         {
1145           struct known_symbol *sym = sortsym[sym_idx];
1146           struct arc_list *runp = sym->froms;
1147
1148           while (runp != NULL
1149                  && ((data[cnt].from_pc == 0 && runp->idx != (size_t) -1l)
1150                      || (data[cnt].from_pc != 0
1151                          && (runp->idx == (size_t) -1l
1152                              || data[cnt].from_pc < sortsym[runp->idx]->addr
1153                              || (data[cnt].from_pc
1154                                  >= (sortsym[runp->idx]->addr
1155                                      + sortsym[runp->idx]->size))))))
1156             runp = runp->next;
1157
1158           if (runp == NULL)
1159             {
1160               /* We need a new entry.  */
1161               struct arc_list *newp = (struct arc_list *)
1162                 obstack_alloc (&ob_list, sizeof (struct arc_list));
1163
1164               if (data[cnt].from_pc == 0)
1165                 newp->idx = (size_t) -1l;
1166               else
1167                 newp->idx = find_symbol (data[cnt].from_pc);
1168               newp->count = data[cnt].count;
1169               newp->next = sym->froms;
1170               sym->froms = newp;
1171             }
1172           else
1173             /* Increment the counter for the found entry.  */
1174             runp->count += data[cnt].count;
1175         }
1176
1177       /* Now add it to the appropriate outgoing list.  */
1178       sym_idx = find_symbol (data[cnt].from_pc);
1179       if (sym_idx != (size_t) -1l)
1180         {
1181           struct known_symbol *sym = sortsym[sym_idx];
1182           struct arc_list *runp = sym->tos;
1183
1184           while (runp != NULL
1185                  && (runp->idx == (size_t) -1l
1186                      || data[cnt].self_pc < sortsym[runp->idx]->addr
1187                      || data[cnt].self_pc >= (sortsym[runp->idx]->addr
1188                                               + sortsym[runp->idx]->size)))
1189             runp = runp->next;
1190
1191           if (runp == NULL)
1192             {
1193               /* We need a new entry.  */
1194               struct arc_list *newp = (struct arc_list *)
1195                 obstack_alloc (&ob_list, sizeof (struct arc_list));
1196
1197               newp->idx = find_symbol (data[cnt].self_pc);
1198               newp->count = data[cnt].count;
1199               newp->next = sym->tos;
1200               sym->tos = newp;
1201             }
1202           else
1203             /* Increment the counter for the found entry.  */
1204             runp->count += data[cnt].count;
1205         }
1206     }
1207 }
1208
1209
1210 static int
1211 countorder (const void *p1, const void *p2)
1212 {
1213   struct known_symbol *s1 = (struct known_symbol *) p1;
1214   struct known_symbol *s2 = (struct known_symbol *) p2;
1215
1216   if (s1->ticks != s2->ticks)
1217     return (int) (s2->ticks - s1->ticks);
1218
1219   if (s1->calls != s2->calls)
1220     return (int) (s2->calls - s1->calls);
1221
1222   return strcmp (s1->name, s2->name);
1223 }
1224
1225
1226 static double tick_unit;
1227 static uintmax_t cumu_ticks;
1228
1229 static void
1230 printflat (const void *node, VISIT value, int level)
1231 {
1232   if (value == leaf || value == postorder)
1233     {
1234       struct known_symbol *s = *(struct known_symbol **) node;
1235
1236       cumu_ticks += s->ticks;
1237
1238       printf ("%6.2f%10.2f%9.2f%9" PRIdMAX "%9.2f           %s\n",
1239               total_ticks ? (100.0 * s->ticks) / total_ticks : 0.0,
1240               tick_unit * cumu_ticks,
1241               tick_unit * s->ticks,
1242               s->calls,
1243               s->calls ? (s->ticks * 1000000) * tick_unit / s->calls : 0,
1244               /* FIXME: don't know about called functions.  */
1245               s->name);
1246     }
1247 }
1248
1249
1250 /* ARGUSED */
1251 static void
1252 freenoop (void *p)
1253 {
1254 }
1255
1256
1257 static void
1258 generate_flat_profile (struct profdata *profdata)
1259 {
1260   size_t n;
1261   void *data = NULL;
1262
1263   tick_unit = 1.0 / *(uint32_t *) profdata->hist_hdr->prof_rate;
1264
1265   printf ("Flat profile:\n\n"
1266           "Each sample counts as %g %s.\n",
1267           tick_unit, profdata->hist_hdr->dimen);
1268   fputs ("  %   cumulative   self              self     total\n"
1269          " time   seconds   seconds    calls  us/call  us/call  name\n",
1270          stdout);
1271
1272   for (n = 0; n < symidx; ++n)
1273     if (sortsym[n]->calls != 0 || sortsym[n]->ticks != 0)
1274       tsearch (sortsym[n], &data, countorder);
1275
1276   twalk (data, printflat);
1277
1278   tdestroy (data, freenoop);
1279 }
1280
1281
1282 static void
1283 generate_call_graph (struct profdata *profdata)
1284 {
1285   size_t cnt;
1286
1287   puts ("\nindex % time    self  children    called     name\n");
1288
1289   for (cnt = 0; cnt < symidx; ++cnt)
1290     if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL)
1291       {
1292         struct arc_list *runp;
1293         size_t n;
1294
1295         /* First print the from-information.  */
1296         runp = sortsym[cnt]->froms;
1297         while (runp != NULL)
1298           {
1299             printf ("            %8.2f%8.2f%9" PRIdMAX "/%-9" PRIdMAX "   %s",
1300                     (runp->idx != (size_t) -1l
1301                      ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
1302                     0.0, /* FIXME: what's time for the children, recursive */
1303                     runp->count, sortsym[cnt]->calls,
1304                     (runp->idx != (size_t) -1l ?
1305                      sortsym[runp->idx]->name : "<UNKNOWN>"));
1306
1307             if (runp->idx != (size_t) -1l)
1308               printf (" [%Zd]", runp->idx);
1309             putchar_unlocked ('\n');
1310
1311             runp = runp->next;
1312           }
1313
1314         /* Info abount the function itself.  */
1315         n = printf ("[%Zu]", cnt);
1316         printf ("%*s%5.1f%8.2f%8.2f%9" PRIdMAX "         %s [%Zd]\n",
1317                 (int) (7 - n), " ",
1318                 total_ticks ? (100.0 * sortsym[cnt]->ticks) / total_ticks : 0,
1319                 sortsym[cnt]->ticks * tick_unit,
1320                 0.0, /* FIXME: what's time for the children, recursive */
1321                 sortsym[cnt]->calls,
1322                 sortsym[cnt]->name, cnt);
1323
1324         /* Info about the functions this function calls.  */
1325         runp = sortsym[cnt]->tos;
1326         while (runp != NULL)
1327           {
1328             printf ("            %8.2f%8.2f%9" PRIdMAX "/",
1329                     (runp->idx != (size_t) -1l
1330                      ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
1331                     0.0, /* FIXME: what's time for the children, recursive */
1332                     runp->count);
1333
1334             if (runp->idx != (size_t) -1l)
1335               printf ("%-9" PRIdMAX "   %s [%Zd]\n",
1336                       sortsym[runp->idx]->calls,
1337                       sortsym[runp->idx]->name,
1338                       runp->idx);
1339             else
1340               fputs ("???         <UNKNOWN>\n\n", stdout);
1341
1342             runp = runp->next;
1343           }
1344
1345         fputs ("-----------------------------------------------\n", stdout);
1346       }
1347 }
1348
1349
1350 static void
1351 generate_call_pair_list (struct profdata *profdata)
1352 {
1353   size_t cnt;
1354
1355   for (cnt = 0; cnt < symidx; ++cnt)
1356     if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL)
1357       {
1358         struct arc_list *runp;
1359
1360         /* First print the incoming arcs.  */
1361         runp = sortsym[cnt]->froms;
1362         while (runp != NULL)
1363           {
1364             if (runp->idx == (size_t) -1l)
1365               printf ("\
1366 <UNKNOWN>                          %-34s %9" PRIdMAX "\n",
1367                       sortsym[cnt]->name, runp->count);
1368             runp = runp->next;
1369           }
1370
1371         /* Next the outgoing arcs.  */
1372         runp = sortsym[cnt]->tos;
1373         while (runp != NULL)
1374           {
1375             printf ("%-34s %-34s %9" PRIdMAX "\n",
1376                     sortsym[cnt]->name,
1377                     (runp->idx != (size_t) -1l
1378                      ? sortsym[runp->idx]->name : "<UNKNOWN>"),
1379                     runp->count);
1380             runp = runp->next;
1381           }
1382       }
1383 }