1 /* Read and display shared object profiling data.
2    Copyright (C) 1997-2022 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
main(int argc,char * argv[])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
parse_opt(int key,char * arg,struct argp_state * state)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 *
more_help(int key,const char * text,void * input)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
print_version(FILE * stream,struct argp_state * state)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 	   "2022");
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 *
load_shobj(const char * name)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
unload_shobj(struct shobj * shobj)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 *
load_profdata(const char * name,struct shobj * shobj)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
unload_profdata(struct profdata * profdata)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
count_total_ticks(struct shobj * shobj,struct profdata * profdata)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
find_symbol(uintptr_t addr)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
count_calls(struct shobj * shobj,struct profdata * profdata)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
symorder(const void * o1,const void * o2)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
printsym(const void * node,VISIT value,int level)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
read_symbols(struct shobj * shobj)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
add_arcs(struct profdata * profdata)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
countorder(const void * p1,const void * p2)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
printflat(const void * node,VISIT value,int level)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
freenoop(void * p)1304 freenoop (void *p)
1305 {
1306 }
1307 
1308 
1309 static void
generate_flat_profile(struct profdata * profdata)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
generate_call_graph(struct profdata * profdata)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
generate_call_pair_list(struct profdata * profdata)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 }
1436