1 /* basic_blocks.c - Basic-block level related code: reading/writing 2 of basic-block info to/from gmon.out; computing and formatting of 3 basic-block related statistics. 4 5 Copyright (C) 1999-2025 Free Software Foundation, Inc. 6 7 This file is part of GNU Binutils. 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program; if not, write to the Free Software 21 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 22 02110-1301, USA. */ 23 24 #include "gprof.h" 25 #include "libiberty.h" 26 #include "filenames.h" 27 #include "basic_blocks.h" 28 #include "corefile.h" 29 #include "gmon_io.h" 30 #include "gmon_out.h" 31 #include "search_list.h" 32 #include "source.h" 33 #include "symtab.h" 34 #include "sym_ids.h" 35 36 static int cmp_bb (const void *, const void *); 37 static int cmp_ncalls (const void *, const void *); 38 static void fskip_string (FILE *); 39 static void annotate_with_count (char *, unsigned int, int, void *); 40 41 /* Default option values: */ 42 bool bb_annotate_all_lines = false; 43 unsigned long bb_min_calls = 1; 44 int bb_table_length = 10; 45 46 /* Variables used to compute annotated source listing stats: */ 47 static long num_executable_lines; 48 static long num_lines_executed; 49 50 51 /* Helper for sorting. Compares two symbols and returns result 52 such that sorting will be increasing according to filename, line 53 number, and address (in that order). */ 54 55 static int 56 cmp_bb (const void *lp, const void *rp) 57 { 58 int r; 59 const Sym *left = *(const Sym **) lp; 60 const Sym *right = *(const Sym **) rp; 61 62 if (left->file && right->file) 63 { 64 r = filename_cmp (left->file->name, right->file->name); 65 66 if (r) 67 return r; 68 69 if (left->line_num != right->line_num) 70 return left->line_num - right->line_num; 71 } 72 73 if (left->addr < right->addr) 74 return -1; 75 else if (left->addr > right->addr) 76 return 1; 77 else 78 return 0; 79 } 80 81 82 /* Helper for sorting. Order basic blocks in decreasing number of 83 calls, ties are broken in increasing order of line numbers. */ 84 static int 85 cmp_ncalls (const void *lp, const void *rp) 86 { 87 const Sym *left = *(const Sym **) lp; 88 const Sym *right = *(const Sym **) rp; 89 90 if (!left) 91 return 1; 92 else if (!right) 93 return -1; 94 95 if (left->ncalls < right->ncalls) 96 return 1; 97 else if (left->ncalls > right->ncalls) 98 return -1; 99 100 return left->line_num - right->line_num; 101 } 102 103 /* Skip over variable length string. */ 104 static void 105 fskip_string (FILE *fp) 106 { 107 int ch; 108 109 while ((ch = fgetc (fp)) != EOF) 110 { 111 if (ch == '\0') 112 break; 113 } 114 } 115 116 /* Read a basic-block record from file IFP. FILENAME is the name 117 of file IFP and is provided for formatting error-messages only. */ 118 119 void 120 bb_read_rec (FILE *ifp, const char *filename) 121 { 122 unsigned int nblocks, b; 123 bfd_vma addr, ncalls; 124 Sym *sym; 125 Sym_Table *symtab; 126 127 if (gmon_io_read_32 (ifp, &nblocks)) 128 { 129 fprintf (stderr, _("%s: %s: unexpected end of file\n"), 130 whoami, filename); 131 done (1); 132 } 133 134 symtab = get_symtab (); 135 136 nblocks = bfd_get_32 (core_bfd, (bfd_byte *) & nblocks); 137 if (gmon_file_version == 0) 138 fskip_string (ifp); 139 140 for (b = 0; b < nblocks; ++b) 141 { 142 if (gmon_file_version == 0) 143 { 144 int line_num; 145 146 /* Version 0 had lots of extra stuff that we don't 147 care about anymore. */ 148 if ((fread (&ncalls, sizeof (ncalls), 1, ifp) != 1) 149 || (fread (&addr, sizeof (addr), 1, ifp) != 1) 150 || (fskip_string (ifp), false) 151 || (fskip_string (ifp), false) 152 || (fread (&line_num, sizeof (line_num), 1, ifp) != 1)) 153 { 154 perror (filename); 155 done (1); 156 } 157 } 158 else if (gmon_io_read_vma (ifp, &addr) 159 || gmon_io_read_vma (ifp, &ncalls)) 160 { 161 perror (filename); 162 done (1); 163 } 164 165 /* Basic-block execution counts are meaningful only if we're 166 profiling at the line-by-line level: */ 167 if (line_granularity) 168 { 169 sym = sym_lookup (symtab, addr); 170 171 if (sym) 172 { 173 int i; 174 175 DBG (BBDEBUG, 176 printf ("[bb_read_rec] 0x%lx->0x%lx (%s:%d) cnt=%lu\n", 177 (unsigned long) addr, (unsigned long) sym->addr, 178 sym->name, sym->line_num, (unsigned long) ncalls)); 179 180 for (i = 0; i < NBBS; i++) 181 { 182 if (! sym->bb_addr[i] || sym->bb_addr[i] == addr) 183 { 184 sym->bb_addr[i] = addr; 185 sym->bb_calls[i] += ncalls; 186 break; 187 } 188 } 189 } 190 } 191 else 192 { 193 static bool user_warned = false; 194 195 if (!user_warned) 196 { 197 user_warned = true; 198 fprintf (stderr, 199 _("%s: warning: ignoring basic-block exec counts (use -l or --line)\n"), 200 whoami); 201 } 202 } 203 } 204 return; 205 } 206 207 /* Write all basic-blocks with non-zero counts to file OFP. FILENAME 208 is the name of OFP and is provided for producing error-messages 209 only. */ 210 void 211 bb_write_blocks (FILE *ofp, const char *filename) 212 { 213 unsigned int nblocks = 0; 214 Sym *sym; 215 int i; 216 Sym_Table *symtab = get_symtab (); 217 218 /* Count how many non-zero blocks with have: */ 219 for (sym = symtab->base; sym < symtab->limit; ++sym) 220 { 221 for (i = 0; i < NBBS && sym->bb_addr[i]; i++) 222 ; 223 nblocks += i; 224 } 225 226 /* Write header: */ 227 if (gmon_io_write_8 (ofp, GMON_TAG_BB_COUNT) 228 || gmon_io_write_32 (ofp, nblocks)) 229 { 230 perror (filename); 231 done (1); 232 } 233 234 /* Write counts: */ 235 for (sym = symtab->base; sym < symtab->limit; ++sym) 236 { 237 for (i = 0; i < NBBS && sym->bb_addr[i]; i++) 238 { 239 if (gmon_io_write_vma (ofp, sym->bb_addr[i]) 240 || gmon_io_write_vma (ofp, (bfd_vma) sym->bb_calls[i])) 241 { 242 perror (filename); 243 done (1); 244 } 245 } 246 } 247 } 248 249 /* Output basic-block statistics in a format that is easily parseable. 250 Current the format is: 251 252 <filename>:<line-number>: (<function-name>:<bb-addr): <ncalls> */ 253 254 void 255 print_exec_counts (void) 256 { 257 Sym **sorted_bbs, *sym; 258 unsigned int i, j, len; 259 Sym_Table *symtab = get_symtab (); 260 261 if (first_output) 262 first_output = false; 263 else 264 printf ("\f\n"); 265 266 /* Sort basic-blocks according to function name and line number: */ 267 sorted_bbs = (Sym **) xmalloc (symtab->len * sizeof (sorted_bbs[0])); 268 len = 0; 269 270 for (sym = symtab->base; sym < symtab->limit; ++sym) 271 { 272 /* Accept symbol if it's in the INCL_EXEC table 273 or there is no INCL_EXEC table 274 and it does not appear in the EXCL_EXEC table. */ 275 if (sym_lookup (&syms[INCL_EXEC], sym->addr) 276 || (syms[INCL_EXEC].len == 0 277 && !sym_lookup (&syms[EXCL_EXEC], sym->addr))) 278 { 279 sorted_bbs[len++] = sym; 280 } 281 } 282 283 qsort (sorted_bbs, len, sizeof (sorted_bbs[0]), cmp_bb); 284 285 /* Output basic-blocks: */ 286 287 for (i = 0; i < len; ++i) 288 { 289 sym = sorted_bbs [i]; 290 291 if (sym->ncalls > 0 || ! ignore_zeros) 292 { 293 /* FIXME: This only works if bfd_vma is unsigned long. */ 294 printf (_("%s:%d: (%s:0x%lx) %lu executions\n"), 295 sym->file ? sym->file->name : _("<unknown>"), sym->line_num, 296 sym->name, (unsigned long) sym->addr, sym->ncalls); 297 } 298 299 for (j = 0; j < NBBS && sym->bb_addr[j]; j ++) 300 { 301 if (sym->bb_calls[j] > 0 || ! ignore_zeros) 302 { 303 /* FIXME: This only works if bfd_vma is unsigned long. */ 304 printf (_("%s:%d: (%s:0x%lx) %lu executions\n"), 305 sym->file ? sym->file->name : _("<unknown>"), sym->line_num, 306 sym->name, (unsigned long) sym->bb_addr[j], 307 sym->bb_calls[j]); 308 } 309 } 310 } 311 free (sorted_bbs); 312 } 313 314 /* Helper for bb_annotated_source: format annotation containing 315 number of line executions. Depends on being called on each 316 line of a file in sequential order. 317 318 Global variable bb_annotate_all_lines enables execution count 319 compression (counts are suppressed if identical to the last one) 320 and prints counts on all executed lines. Otherwise, print 321 all basic-block execution counts exactly once on the line 322 that starts the basic-block. */ 323 324 static void 325 annotate_with_count (char *buf, unsigned int width, int line_num, void *arg) 326 { 327 Source_File *sf = (Source_File *) arg; 328 Sym *b; 329 unsigned int i; 330 static unsigned long last_count; 331 unsigned long last_print = (unsigned long) -1; 332 333 b = NULL; 334 335 if (line_num <= sf->num_lines) 336 b = (Sym *) sf->line[line_num - 1]; 337 338 if (!b) 339 { 340 for (i = 0; i < width; i++) 341 buf[i] = ' '; 342 buf[width] = '\0'; 343 } 344 else 345 { 346 char tmpbuf[NBBS * 30]; 347 char *p; 348 unsigned long ncalls; 349 int ncalls_set; 350 unsigned int len; 351 352 ++num_executable_lines; 353 354 p = tmpbuf; 355 *p = '\0'; 356 357 ncalls = 0; 358 ncalls_set = 0; 359 360 /* If this is a function entry point, label the line no matter what. 361 Otherwise, we're in the middle of a function, so check to see 362 if the first basic-block address is larger than the starting 363 address of the line. If so, then this line begins with a 364 a portion of the previous basic-block, so print that prior 365 execution count (if bb_annotate_all_lines is set). */ 366 if (b->is_func) 367 { 368 p += sprintf (p, "%lu", b->ncalls); 369 last_count = b->ncalls; 370 last_print = last_count; 371 ncalls = b->ncalls; 372 ncalls_set = 1; 373 } 374 else if (bb_annotate_all_lines 375 && b->bb_addr[0] && b->bb_addr[0] > b->addr) 376 { 377 p += sprintf (p, "%lu", last_count); 378 last_print = last_count; 379 ncalls = last_count; 380 ncalls_set = 1; 381 } 382 383 /* Loop through all of this line's basic-blocks. For each one, 384 update last_count, then compress sequential identical counts 385 (if bb_annotate_all_lines) and print the execution count. */ 386 387 for (i = 0; i < NBBS && b->bb_addr[i]; i++) 388 { 389 last_count = b->bb_calls[i]; 390 if (! ncalls_set) 391 { 392 ncalls = 0; 393 ncalls_set = 1; 394 } 395 ncalls += last_count; 396 397 if (bb_annotate_all_lines && last_count == last_print) 398 continue; 399 400 if (p > tmpbuf) 401 *p++ = ','; 402 p += sprintf (p, "%lu", last_count); 403 404 last_print = last_count; 405 } 406 407 /* We're done. If nothing has been printed on this line, 408 print the last execution count (bb_annotate_all_lines), 409 which could be from either a previous line (if there were 410 no BBs on this line), or from this line (if all our BB 411 counts were compressed out because they were identical). */ 412 413 if (bb_annotate_all_lines && p == tmpbuf) 414 { 415 p += sprintf (p, "%lu", last_count); 416 ncalls = last_count; 417 ncalls_set = 1; 418 } 419 420 if (! ncalls_set) 421 { 422 unsigned int c; 423 424 for (c = 0; c < width; c++) 425 buf[c] = ' '; 426 buf[width] = '\0'; 427 return; 428 } 429 430 ++num_lines_executed; 431 432 if (ncalls < bb_min_calls) 433 { 434 strcpy (tmpbuf, "#####"); 435 p = tmpbuf + 5; 436 } 437 438 strcpy (p, " -> "); 439 p += 4; 440 441 len = p - tmpbuf; 442 if (len >= width) 443 { 444 strncpy (buf, tmpbuf, width); 445 buf[width] = '\0'; 446 } 447 else 448 { 449 unsigned int c; 450 451 strcpy (buf + width - len, tmpbuf); 452 for (c = 0; c < width - len; ++c) 453 buf[c] = ' '; 454 } 455 } 456 } 457 458 /* Annotate the files named in SOURCE_FILES with basic-block statistics 459 (execution counts). After each source files, a few statistics 460 regarding that source file are printed. */ 461 462 void 463 print_annotated_source (void) 464 { 465 Sym *sym, *line_stats, *new_line; 466 Source_File *sf; 467 int i, table_len; 468 FILE *ofp; 469 Sym_Table *symtab = get_symtab (); 470 471 /* Find maximum line number for each source file that user is 472 interested in: */ 473 for (sym = symtab->base; sym < symtab->limit; ++sym) 474 { 475 /* Accept symbol if it's file is known, its line number is 476 bigger than anything we have seen for that file so far and 477 if it's in the INCL_ANNO table or there is no INCL_ANNO 478 table and it does not appear in the EXCL_ANNO table. */ 479 if (sym->file && sym->line_num > sym->file->num_lines 480 && (sym_lookup (&syms[INCL_ANNO], sym->addr) 481 || (syms[INCL_ANNO].len == 0 482 && !sym_lookup (&syms[EXCL_ANNO], sym->addr)))) 483 { 484 sym->file->num_lines = sym->line_num; 485 } 486 } 487 488 /* Allocate line descriptors: */ 489 for (sf = first_src_file; sf; sf = sf->next) 490 { 491 if (sf->num_lines > 0) 492 { 493 sf->line = (void **) xmalloc (sf->num_lines * sizeof (sf->line[0])); 494 memset (sf->line, 0, sf->num_lines * sizeof (sf->line[0])); 495 } 496 } 497 498 /* Count executions per line: */ 499 for (sym = symtab->base; sym < symtab->limit; ++sym) 500 { 501 if (sym->file && sym->file->num_lines 502 && (sym_lookup (&syms[INCL_ANNO], sym->addr) 503 || (syms[INCL_ANNO].len == 0 504 && !sym_lookup (&syms[EXCL_ANNO], sym->addr)))) 505 { 506 sym->file->ncalls += sym->ncalls; 507 line_stats = (Sym *) sym->file->line[sym->line_num - 1]; 508 509 if (!line_stats) 510 { 511 /* Common case has at most one basic-block per source line: */ 512 sym->file->line[sym->line_num - 1] = sym; 513 } 514 else if (!line_stats->addr) 515 { 516 /* sym is the 3rd .. nth basic block for this line: */ 517 line_stats->ncalls += sym->ncalls; 518 } 519 else 520 { 521 /* sym is the second basic block for this line. */ 522 new_line = (Sym *) xmalloc (sizeof (*new_line)); 523 *new_line = *line_stats; 524 new_line->addr = 0; 525 new_line->ncalls += sym->ncalls; 526 sym->file->line[sym->line_num - 1] = new_line; 527 } 528 } 529 } 530 531 /* Plod over source files, annotating them: */ 532 for (sf = first_src_file; sf; sf = sf->next) 533 { 534 if (!sf->num_lines || (ignore_zeros && sf->ncalls == 0)) 535 continue; 536 537 num_executable_lines = num_lines_executed = 0; 538 539 ofp = annotate_source (sf, 16, annotate_with_count, sf); 540 if (!ofp) 541 continue; 542 543 if (bb_table_length > 0) 544 { 545 fprintf (ofp, _("\n\nTop %d Lines:\n\n Line Count\n\n"), 546 bb_table_length); 547 548 /* Abuse line arrays---it's not needed anymore: */ 549 qsort (sf->line, sf->num_lines, sizeof (sf->line[0]), cmp_ncalls); 550 table_len = bb_table_length; 551 552 if (table_len > sf->num_lines) 553 table_len = sf->num_lines; 554 555 for (i = 0; i < table_len; ++i) 556 { 557 sym = (Sym *) sf->line[i]; 558 559 if (!sym || sym->ncalls == 0) 560 break; 561 562 fprintf (ofp, "%9d %10lu\n", sym->line_num, sym->ncalls); 563 } 564 } 565 566 free (sf->line); 567 sf->line = 0; 568 569 fprintf (ofp, _("\nExecution Summary:\n\n")); 570 fprintf (ofp, _("%9ld Executable lines in this file\n"), 571 num_executable_lines); 572 fprintf (ofp, _("%9ld Lines executed\n"), num_lines_executed); 573 fprintf (ofp, _("%9.2f Percent of the file executed\n"), 574 num_executable_lines 575 ? 100.0 * num_lines_executed / (double) num_executable_lines 576 : 100.0); 577 fprintf (ofp, _("\n%9lu Total number of line executions\n"), 578 sf->ncalls); 579 fprintf (ofp, _("%9.2f Average executions per line\n"), 580 num_executable_lines 581 ? (double) sf->ncalls / (double) num_executable_lines 582 : 0.0); 583 584 if (ofp != stdout) 585 fclose (ofp); 586 } 587 } 588