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