1 /* Read and display shared object profiling data.
2 Copyright (C) 1997-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
37 #include <sys/gmon_out.h>
39 #include <sys/param.h>
42 /* Get libc version number. */
43 #include "../version.h"
45 #define PACKAGE _libc_intl_domainname
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"
56 # error "Unknown BYTE_ORDER " BYTE_ORDER
57 # define byteorder ELFDATANONE
61 # define PATH_MAX 1024
65 extern int __profile_frequency (void);
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
;
73 /* Definitions of arguments for argp functions. */
74 static const struct argp_option options
[] =
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") },
83 { "test", OPT_TEST
, NULL
, OPTION_HIDDEN
, NULL
},
84 { NULL
, 0, NULL
, 0, NULL
}
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");
92 /* Strings for arguments in help texts. */
93 static const char args_doc
[] = N_("SHOBJ [PROFDATA]");
95 /* Prototype for option handler. */
96 static error_t
parse_opt (int key
, char *arg
, struct argp_state
*state
);
98 /* Function to print some extra text in the help message. */
99 static char *more_help (int key
, const char *text
, void *input
);
101 /* Data structure to communicate with argp functions. */
102 static struct argp argp
=
104 options
, parse_opt
, args_doc
, doc
, NULL
, more_help
108 /* Operation modes. */
113 CALL_GRAPH_MODE
= 1 << 1,
116 DEFAULT_MODE
= FLAT_MODE
| CALL_GRAPH_MODE
119 /* Nozero for testing. */
122 /* Structure describing calls. */
123 struct here_fromstruct
125 struct here_cg_arc_record
volatile *here
;
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
137 } __attribute__ ((packed
));
146 struct arc_list
*next
;
149 static struct obstack ob_list
;
163 struct arc_list
*froms
;
164 struct arc_list
*tos
;
170 const char *name
; /* User-provided name. */
172 struct link_map
*map
;
173 const char *dynstrtab
; /* Dynamic string table of shared object. */
174 const char *soname
; /* Soname of shared object. */
178 unsigned long int kcountsize
;
179 size_t expected_size
; /* Expected size of profiling file. */
183 unsigned int hashfraction
;
187 size_t symbol_mapsize
;
188 const ElfW(Sym
) *symtab
;
192 struct obstack ob_str
;
193 struct obstack ob_sym
;
197 struct real_gmon_hist_hdr
214 struct real_gmon_hist_hdr
*hist_hdr
;
216 uint32_t narcs
; /* Number of arcs in toset. */
217 struct here_cg_arc_record
*data
;
219 struct here_fromstruct
*froms
;
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
;
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
);
243 main (int argc
, char *argv
[])
246 const char *profdata
;
247 struct shobj
*shobj_handle
;
248 struct profdata
*profdata_handle
;
251 setlocale (LC_ALL
, "");
253 /* Initialize the message catalog. */
254 textdomain (_libc_intl_domainname
);
256 /* Parse and process arguments. */
257 argp_parse (&argp
, argc
, argv
, 0, &remaining
, NULL
);
259 if (argc
- remaining
== 0 || argc
- remaining
> 2)
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
);
267 /* Get parameters. */
268 shobj
= argv
[remaining
];
269 if (argc
- remaining
== 2)
270 profdata
= argv
[remaining
+ 1];
272 /* No filename for the profiling data given. We will determine it
273 from the soname of the shobj, later. */
276 /* First see whether we can load the shared object. */
277 shobj_handle
= load_shobj (shobj
);
278 if (shobj_handle
== NULL
)
281 /* We can now determine the filename for the profiling data, if
283 if (profdata
== NULL
)
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");
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
)
300 unload_shobj (shobj_handle
);
305 read_symbols (shobj_handle
);
307 /* Count the ticks. */
308 count_total_ticks (shobj_handle
, profdata_handle
);
310 /* Count the calls. */
311 count_calls (shobj_handle
, profdata_handle
);
313 /* Add the arc information. */
314 add_arcs (profdata_handle
);
316 /* If no mode is specified fall back to the default mode. */
321 if (mode
& FLAT_MODE
)
322 generate_flat_profile (profdata_handle
);
324 if (mode
& CALL_GRAPH_MODE
)
325 generate_call_graph (profdata_handle
);
327 if (mode
& CALL_PAIRS
)
328 generate_call_pair_list (profdata_handle
);
330 /* Free the resources. */
331 unload_shobj (shobj_handle
);
332 unload_profdata (profdata_handle
);
338 /* Handle program arguments. */
340 parse_opt (int key
, char *arg
, struct argp_state
*state
)
351 mode
|= CALL_GRAPH_MODE
;
357 return ARGP_ERR_UNKNOWN
;
364 more_help (int key
, const char *text
, void *input
)
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)
379 return (char *) text
;
383 /* Print the version information. */
385 print_version (FILE *stream
, struct argp_state
*state
)
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\
394 fprintf (stream
, gettext ("Written by %s.\n"), "Ulrich Drepper");
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
)
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
;
412 size_t pagesize
= getpagesize ();
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 specifies 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
)
422 char *load_name
= (char *) alloca (strlen (name
) + 3);
423 stpcpy (stpcpy (load_name
, "./"), name
);
425 map
= (struct link_map
*) dlopen (load_name
, RTLD_LAZY
| __RTLD_SPROF
);
429 map
= (struct link_map
*) dlopen (name
, RTLD_LAZY
| __RTLD_SPROF
);
432 error (0, errno
, _("failed to load shared object `%s'"), name
);
437 /* Prepare the result. */
438 result
= (struct shobj
*) calloc (1, sizeof (struct shobj
));
441 error (0, errno
, _("cannot create internal descriptor"));
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
))
453 ElfW(Addr
) start
= (ph
->p_vaddr
& ~(pagesize
- 1));
454 ElfW(Addr
) end
= ((ph
->p_vaddr
+ ph
->p_memsz
+ pagesize
- 1)
457 if (start
< mapstart
)
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
));
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
);
475 textsize
= result
->highpc
- result
->lowpc
;
476 result
->kcountsize
= textsize
/ HISTFRACTION
;
477 result
->hashfraction
= HASHFRACTION
;
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
);
490 result
->expected_size
= (sizeof (struct gmon_hdr
)
491 + 4 + sizeof (struct gmon_hist_hdr
)
495 * sizeof (struct here_cg_arc_record
)));
498 printf ("expected size: %zd\n", result
->expected_size
);
500 #define SCALE_1_TO_1 0x10000L
502 if (result
->kcountsize
< result
->highpc
- result
->lowpc
)
504 size_t range
= result
->highpc
- result
->lowpc
;
505 size_t quot
= range
/ result
->kcountsize
;
507 if (quot
>= SCALE_1_TO_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)));
515 result
->s_scale
= ((SCALE_1_TO_1
* 256)
516 / ((range
* 256) / result
->kcountsize
));
519 result
->s_scale
= SCALE_1_TO_1
;
522 printf ("s_scale: %d\n", result
->s_scale
);
524 /* Determine the dynamic string table. */
525 if (map
->l_info
[DT_STRTAB
] == NULL
)
526 result
->dynstrtab
= NULL
;
528 result
->dynstrtab
= (const char *) D_PTR (map
, l_info
[DT_STRTAB
]);
530 printf ("string table: %p\n", result
->dynstrtab
);
532 /* Determine the soname. */
533 if (map
->l_info
[DT_SONAME
] == NULL
)
534 result
->soname
= NULL
;
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
);
540 /* Now we have to load the symbol table.
542 First load the section header table. */
543 ehdr
= (ElfW(Ehdr
) *) map
->l_map_start
;
545 /* Make sure we are on the right party. */
546 if (ehdr
->e_shentsize
!= sizeof (ElfW(Shdr
)))
549 /* And we need the shared object file descriptor again. */
550 fd
= open (map
->l_name
, O_RDONLY
);
552 /* Dooh, this really shouldn't happen. We know the file is available. */
553 error (EXIT_FAILURE
, errno
, _("Reopening shared object `%s' failed"),
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"));
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"));
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)
577 symtab_entry
= &shdr
[idx
];
580 else if (shdr
[idx
].sh_type
== SHT_PROGBITS
581 && strcmp (shstrtab
+ shdr
[idx
].sh_name
, ".gnu_debuglink") == 0)
582 debuglink_entry
= &shdr
[idx
];
584 /* Get the file name of the debuginfo file if necessary. */
586 if (symtab_entry
== NULL
&& debuglink_entry
!= NULL
)
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
)
594 fprintf (stderr
, _("*** Cannot read debuginfo file name: %m\n"));
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);
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
612 char *realname
= canonicalize_file_name (origlink
);
614 if (realname
== NULL
|| (cp
= strrchr (realname
, '/')) == NULL
)
615 error (EXIT_FAILURE
, errno
, _("cannot determine file name"));
617 /* Leave the last slash in place. */
620 /* First add the debuginfo file name only. */
621 static const char usrlibdebug
[]= "/usr/lib/debug/";
622 char *workbuf
= (char *) alloca (sizeof (usrlibdebug
)
624 + strlen (debuginfo_fname
));
625 strcpy (stpcpy (workbuf
, realname
), debuginfo_fname
);
627 int fd2
= open (workbuf
, O_RDONLY
);
630 strcpy (stpcpy (stpcpy (workbuf
, realname
), ".debug/"),
632 fd2
= open (workbuf
, O_RDONLY
);
635 strcpy (stpcpy (stpcpy (workbuf
, usrlibdebug
), realname
),
637 fd2
= open (workbuf
, O_RDONLY
);
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"));
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"));
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"));
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)
670 symtab_entry
= &shdr2
[idx
];
682 if (symtab_entry
== NULL
)
684 fprintf (stderr
, _("\
685 *** The file `%s' is stripped: no detailed analysis possible\n"),
687 result
->symtab
= NULL
;
688 result
->strtab
= NULL
;
692 ElfW(Off
) min_offset
, max_offset
;
693 ElfW(Shdr
) *strtab_entry
;
695 strtab_entry
= &shdr
[symtab_entry
->sh_link
];
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
)
701 min_offset
= symtab_entry
->sh_offset
& ~(pagesize
- 1);
702 max_offset
= strtab_entry
->sh_offset
+ strtab_entry
->sh_size
;
706 min_offset
= strtab_entry
->sh_offset
& ~(pagesize
- 1);
707 max_offset
= symtab_entry
->sh_offset
+ symtab_entry
->sh_size
;
710 result
->symbol_map
= mmap (NULL
, max_offset
- min_offset
,
711 PROT_READ
, MAP_SHARED
|MAP_FILE
, symfd
,
713 if (result
->symbol_map
== MAP_FAILED
)
714 error (EXIT_FAILURE
, errno
, _("failed to load symbol data"));
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
;
725 /* Free the descriptor for the shared object. */
735 unload_shobj (struct shobj
*shobj
)
737 munmap (shobj
->symbol_map
, shobj
->symbol_mapsize
);
738 dlclose (shobj
->map
);
742 static struct profdata
*
743 load_profdata (const char *name
, struct shobj
*shobj
)
745 struct profdata
*result
;
751 struct here_cg_arc_record
*data
;
752 struct here_fromstruct
*froms
;
757 fd
= open (name
, O_RDONLY
);
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
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
);
775 fd
= open (ext_name
, O_RDONLY
);
778 /* Even this file does not exist. */
779 error (0, errno
, _("cannot load profiling data"));
784 /* We have found the file, now make sure it is the right one for the
786 if (fstat64 (fd
, &st
) < 0)
788 error (0, errno
, _("while stat'ing profiling data file"));
793 if ((size_t) st
.st_size
!= shobj
->expected_size
)
796 _("profiling data file `%s' does not match shared object `%s'"),
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
)
807 error (0, errno
, _("failed to mmap the profiling data file"));
812 /* We don't need the file descriptor anymore. */
815 error (0, errno
, _("error while closing the profiling data file"));
816 munmap (addr
, st
.st_size
);
820 /* Prepare the result. */
821 result
= (struct profdata
*) calloc (1, sizeof (struct profdata
));
824 error (0, errno
, _("cannot create internal descriptor"));
825 munmap (addr
, st
.st_size
);
829 /* Store the address and size so that we can later free the resources. */
831 result
->size
= st
.st_size
;
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
));
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));
847 /* Create the gmon_hdr we expect or write. */
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
)))
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
));
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
)))
882 hist_hdr
.low_pc
= (char *) shobj
->lowpc
- shobj
->map
->l_addr
;
883 hist_hdr
.high_pc
= (char *) shobj
->highpc
- shobj
->map
->l_addr
;
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';
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
)
898 error (0, 0, _("`%s' is no correct profile data file for `%s'"),
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");
913 munmap (addr
, st
.st_size
);
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
)
922 error (0, errno
, _("cannot create internal descriptor"));
923 munmap (addr
, st
.st_size
);
928 result
->froms
= (struct here_fromstruct
*) ((char *) result
->tos
932 /* Now we have to process all the arc count entries. */
933 fromlimit
= shobj
->fromlimit
;
935 froms
= result
->froms
;
937 for (idx
= 0; idx
< MIN (*narcsp
, fromlimit
); ++idx
)
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
;
953 unload_profdata (struct profdata
*profdata
)
955 free (profdata
->tos
);
956 munmap (profdata
->addr
, profdata
->size
);
962 count_total_ticks (struct shobj
*shobj
, struct profdata
*profdata
)
964 volatile uint16_t *kcount
= profdata
->kcount
;
965 size_t maxkidx
= shobj
->kcountsize
;
966 size_t factor
= 2 * (65536 / shobj
->s_scale
);
970 while (sidx
< symidx
)
972 uintptr_t start
= sortsym
[sidx
]->addr
;
973 uintptr_t end
= start
+ sortsym
[sidx
]->size
;
975 while (kidx
< maxkidx
&& factor
* kidx
< start
)
980 while (kidx
< maxkidx
&& factor
* kidx
< end
)
981 sortsym
[sidx
]->ticks
+= kcount
[kidx
++];
985 total_ticks
+= sortsym
[sidx
++]->ticks
;
991 find_symbol (uintptr_t addr
)
995 while (sidx
< symidx
)
997 uintptr_t start
= sortsym
[sidx
]->addr
;
998 uintptr_t end
= start
+ sortsym
[sidx
]->size
;
1000 if (addr
>= start
&& addr
< end
)
1009 return (size_t) -1l;
1014 count_calls (struct shobj
*shobj
, struct profdata
*profdata
)
1016 struct here_cg_arc_record
*data
= profdata
->data
;
1017 uint32_t narcs
= profdata
->narcs
;
1020 for (cnt
= 0; cnt
< narcs
; ++cnt
)
1022 uintptr_t here
= data
[cnt
].self_pc
;
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
;
1034 symorder (const void *o1
, const void *o2
)
1036 const struct known_symbol
*p1
= (const struct known_symbol
*) o1
;
1037 const struct known_symbol
*p2
= (const struct known_symbol
*) o2
;
1039 return p1
->addr
- p2
->addr
;
1044 printsym (const void *node
, VISIT value
, int level
)
1046 if (value
== leaf
|| value
== postorder
)
1047 sortsym
[symidx
++] = *(struct known_symbol
**) node
;
1052 read_symbols (struct shobj
*shobj
)
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
);
1063 /* Process the symbols. */
1064 if (shobj
->symtab
!= NULL
)
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)
1074 struct known_symbol
**existp
;
1075 struct known_symbol
*newsym
1076 = (struct known_symbol
*) obstack_alloc (&shobj
->ob_sym
,
1079 error (EXIT_FAILURE
, errno
, _("cannot allocate symbol data"));
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
)
1090 existp
= tfind (newsym
, &symroot
, symorder
);
1094 tsearch (newsym
, &symroot
, symorder
);
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
)))
1107 /* We don't need the allocated memory. */
1108 obstack_free (&shobj
->ob_sym
, newsym
);
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
,
1118 const char *strtab
= (const char *) D_PTR (shobj
->map
,
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
)
1126 if ((ELFW(ST_TYPE
)(symtab
->st_info
) == STT_FUNC
1127 || ELFW(ST_TYPE
)(symtab
->st_info
) == STT_NOTYPE
)
1128 && symtab
->st_size
!= 0)
1130 struct known_symbol
*newsym
;
1131 struct known_symbol
**existp
;
1134 (struct known_symbol
*) obstack_alloc (&shobj
->ob_sym
,
1137 error (EXIT_FAILURE
, errno
, _("cannot allocate symbol data"));
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
)
1146 newsym
->froms
= NULL
;
1149 existp
= tfind (newsym
, &symroot
, symorder
);
1153 tsearch (newsym
, &symroot
, symorder
);
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
)))
1166 /* We don't need the allocated memory. */
1167 obstack_free (&shobj
->ob_sym
, newsym
);
1175 sortsym
= malloc (n
* sizeof (struct known_symbol
*));
1176 if (sortsym
== NULL
)
1179 twalk (symroot
, printsym
);
1184 add_arcs (struct profdata
*profdata
)
1186 uint32_t narcs
= profdata
->narcs
;
1187 struct here_cg_arc_record
*data
= profdata
->data
;
1190 for (cnt
= 0; cnt
< narcs
; ++cnt
)
1192 /* First add the incoming arc. */
1193 size_t sym_idx
= find_symbol (data
[cnt
].self_pc
);
1195 if (sym_idx
!= (size_t) -1l)
1197 struct known_symbol
*sym
= sortsym
[sym_idx
];
1198 struct arc_list
*runp
= sym
->froms
;
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
))))))
1212 /* We need a new entry. */
1213 struct arc_list
*newp
= (struct arc_list
*)
1214 obstack_alloc (&ob_list
, sizeof (struct arc_list
));
1216 if (data
[cnt
].from_pc
== 0)
1217 newp
->idx
= (size_t) -1l;
1219 newp
->idx
= find_symbol (data
[cnt
].from_pc
);
1220 newp
->count
= data
[cnt
].count
;
1221 newp
->next
= sym
->froms
;
1225 /* Increment the counter for the found entry. */
1226 runp
->count
+= data
[cnt
].count
;
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)
1233 struct known_symbol
*sym
= sortsym
[sym_idx
];
1234 struct arc_list
*runp
= sym
->tos
;
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
)))
1245 /* We need a new entry. */
1246 struct arc_list
*newp
= (struct arc_list
*)
1247 obstack_alloc (&ob_list
, sizeof (struct arc_list
));
1249 newp
->idx
= find_symbol (data
[cnt
].self_pc
);
1250 newp
->count
= data
[cnt
].count
;
1251 newp
->next
= sym
->tos
;
1255 /* Increment the counter for the found entry. */
1256 runp
->count
+= data
[cnt
].count
;
1263 countorder (const void *p1
, const void *p2
)
1265 struct known_symbol
*s1
= (struct known_symbol
*) p1
;
1266 struct known_symbol
*s2
= (struct known_symbol
*) p2
;
1268 if (s1
->ticks
!= s2
->ticks
)
1269 return (int) (s2
->ticks
- s1
->ticks
);
1271 if (s1
->calls
!= s2
->calls
)
1272 return (int) (s2
->calls
- s1
->calls
);
1274 return strcmp (s1
->name
, s2
->name
);
1278 static double tick_unit
;
1279 static uintmax_t cumu_ticks
;
1282 printflat (const void *node
, VISIT value
, int level
)
1284 if (value
== leaf
|| value
== postorder
)
1286 struct known_symbol
*s
= *(struct known_symbol
**) node
;
1288 cumu_ticks
+= s
->ticks
;
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
,
1295 s
->calls
? (s
->ticks
* 1000000) * tick_unit
/ s
->calls
: 0,
1296 /* FIXME: don't know about called functions. */
1310 generate_flat_profile (struct profdata
*profdata
)
1315 tick_unit
= 1.0 / profdata
->hist_hdr
->prof_rate
;
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",
1324 for (n
= 0; n
< symidx
; ++n
)
1325 if (sortsym
[n
]->calls
!= 0 || sortsym
[n
]->ticks
!= 0)
1326 tsearch (sortsym
[n
], &data
, countorder
);
1328 twalk (data
, printflat
);
1330 tdestroy (data
, freenoop
);
1335 generate_call_graph (struct profdata
*profdata
)
1339 puts ("\nindex % time self children called name\n");
1341 for (cnt
= 0; cnt
< symidx
; ++cnt
)
1342 if (sortsym
[cnt
]->froms
!= NULL
|| sortsym
[cnt
]->tos
!= NULL
)
1344 struct arc_list
*runp
;
1347 /* First print the from-information. */
1348 runp
= sortsym
[cnt
]->froms
;
1349 while (runp
!= NULL
)
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>"));
1359 if (runp
->idx
!= (size_t) -1l)
1360 printf (" [%zd]", runp
->idx
);
1361 putchar_unlocked ('\n');
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",
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
);
1376 /* Info about the functions this function calls. */
1377 runp
= sortsym
[cnt
]->tos
;
1378 while (runp
!= NULL
)
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 */
1386 if (runp
->idx
!= (size_t) -1l)
1387 printf ("%-9" PRIdMAX
" %s [%zd]\n",
1388 sortsym
[runp
->idx
]->calls
,
1389 sortsym
[runp
->idx
]->name
,
1392 fputs ("??? <UNKNOWN>\n\n", stdout
);
1397 fputs ("-----------------------------------------------\n", stdout
);
1403 generate_call_pair_list (struct profdata
*profdata
)
1407 for (cnt
= 0; cnt
< symidx
; ++cnt
)
1408 if (sortsym
[cnt
]->froms
!= NULL
|| sortsym
[cnt
]->tos
!= NULL
)
1410 struct arc_list
*runp
;
1412 /* First print the incoming arcs. */
1413 runp
= sortsym
[cnt
]->froms
;
1414 while (runp
!= NULL
)
1416 if (runp
->idx
== (size_t) -1l)
1418 <UNKNOWN> %-34s %9" PRIdMAX
"\n",
1419 sortsym
[cnt
]->name
, runp
->count
);
1423 /* Next the outgoing arcs. */
1424 runp
= sortsym
[cnt
]->tos
;
1425 while (runp
!= NULL
)
1427 printf ("%-34s %-34s %9" PRIdMAX
"\n",
1429 (runp
->idx
!= (size_t) -1l
1430 ? sortsym
[runp
->idx
]->name
: "<UNKNOWN>"),