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
65extern int __profile_frequency (void);
66
67/* Name and version of program. */
68static void print_version (FILE *stream, struct argp_state *state);
69void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
70
71#define OPT_TEST 1
72
73/* Definitions of arguments for argp functions. */
74static 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. */
88static 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. */
93static const char args_doc[] = N_("SHOBJ [PROFDATA]");
94
95/* Prototype for option handler. */
96static error_t parse_opt (int key, char *arg, struct argp_state *state);
97
98/* Function to print some extra text in the help message. */
99static char *more_help (int key, const char *text, void *input);
100
101/* Data structure to communicate with argp functions. */
102static struct argp argp =
103{
104 options, parse_opt, args_doc, doc, NULL, more_help
105};
106
107
108/* Operation modes. */
109static 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. */
120static int do_test;
121
122/* Strcuture describing calls. */
123struct 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. */
132struct here_cg_arc_record
133{
134 uintptr_t from_pc;
135 uintptr_t self_pc;
136 uint32_t count;
137} __attribute__ ((packed));
138
139
140struct known_symbol;
141struct arc_list
142{
143 size_t idx;
144 uintmax_t count;
145
146 struct arc_list *next;
147};
148
149static struct obstack ob_list;
150
151
152struct 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
168struct 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
197struct 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
208struct 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. */
223static void *symroot;
224static struct known_symbol **sortsym;
225static size_t symidx;
226static uintmax_t total_ticks;
227
228/* Prototypes for local functions. */
229static struct shobj *load_shobj (const char *name);
230static void unload_shobj (struct shobj *shobj);
231static struct profdata *load_profdata (const char *name, struct shobj *shobj);
232static void unload_profdata (struct profdata *profdata);
233static void count_total_ticks (struct shobj *shobj, struct profdata *profdata);
234static void count_calls (struct shobj *shobj, struct profdata *profdata);
235static void read_symbols (struct shobj *shobj);
236static void add_arcs (struct profdata *profdata);
237static void generate_flat_profile (struct profdata *profdata);
238static void generate_call_graph (struct profdata *profdata);
239static void generate_call_pair_list (struct profdata *profdata);
240
241
242int
243main (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. */
339static error_t
340parse_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
363static char *
364more_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 ("\
372For 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. */
384static void
385print_version (FILE *stream, struct argp_state *state)
386{
387 fprintf (stream, "sprof %s%s\n", PKGVERSION, VERSION);
388 fprintf (stream, gettext ("\
389Copyright (C) %s Free Software Foundation, Inc.\n\
390This is free software; see the source for copying conditions. There is NO\n\
391warranty; 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. */
400static struct shobj *
401load_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
734static void
735unload_shobj (struct shobj *shobj)
736{
737 munmap (shobj->symbol_map, shobj->symbol_mapsize);
738 dlclose (shobj->map);
739}
740
741
742static struct profdata *
743load_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
952static void
953unload_profdata (struct profdata *profdata)
954{
955 free (profdata->tos);
956 munmap (profdata->addr, profdata->size);
957 free (profdata);
958}
959
960
961static void
962count_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
990static size_t
991find_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
1013static void
1014count_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
1033static int
1034symorder (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
1043static void
1044printsym (const void *node, VISIT value, int level)
1045{
1046 if (value == leaf || value == postorder)
1047 sortsym[symidx++] = *(struct known_symbol **) node;
1048}
1049
1050
1051static void
1052read_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
1183static void
1184add_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
1262static int
1263countorder (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
1278static double tick_unit;
1279static uintmax_t cumu_ticks;
1280
1281static void
1282printflat (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 */
1303static void
1304freenoop (void *p)
1305{
1306}
1307
1308
1309static void
1310generate_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
1334static void
1335generate_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
1402static void
1403generate_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

source code of glibc/elf/sprof.c