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