1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008 Nokia Corporation 5 * All rights reserved. 6 * 7 * This software was developed by Attilio Rao for the IPSO project under 8 * contract to Nokia Corporation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice unmodified, this list of conditions, and the following 15 * disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/queue.h> 38 39 #include <ctype.h> 40 #include <paths.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 45 #include <unistd.h> 46 47 /* NB: Make sure FNBUFF is as large as LNBUFF, otherwise it could overflow */ 48 #define FNBUFF 512 49 #define LNBUFF 512 50 51 #define TMPNAME "pmcannotate.XXXXXX" 52 53 #define FATAL(ptr, x ...) do { \ 54 fqueue_deleteall(); \ 55 general_deleteall(); \ 56 if ((ptr) != NULL) \ 57 perror(ptr); \ 58 fprintf(stderr, ##x); \ 59 remove(tbfl); \ 60 remove(tofl); \ 61 exit(EXIT_FAILURE); \ 62 } while (0) 63 64 #define PERCSAMP(x) ((x) * 100 / totalsamples) 65 66 struct entry { 67 TAILQ_ENTRY(entry) en_iter; 68 char *en_name; 69 uintptr_t en_pc; 70 uintptr_t en_ostart; 71 uintptr_t en_oend; 72 u_int en_nsamples; 73 }; 74 75 struct aggent { 76 TAILQ_ENTRY(aggent) ag_fiter; 77 long ag_offset; 78 uintptr_t ag_ostart; 79 uintptr_t ag_oend; 80 char *ag_name; 81 u_int ag_nsamples; 82 }; 83 84 static struct aggent *agg_create(const char *name, u_int nsamples, 85 uintptr_t start, uintptr_t end); 86 static void agg_destroy(struct aggent *agg) __unused; 87 static void asmparse(FILE *fp); 88 static int cparse(FILE *fp); 89 static void entry_acqref(struct entry *entry); 90 static struct entry *entry_create(const char *name, uintptr_t pc, 91 uintptr_t start, uintptr_t end); 92 static void entry_destroy(struct entry *entry) __unused; 93 static void fqueue_compact(float th); 94 static void fqueue_deleteall(void); 95 static struct aggent *fqueue_findent_by_name(const char *name); 96 static int fqueue_getall(const char *bin, char *temp, int asmf); 97 static int fqueue_insertent(struct entry *entry); 98 static int fqueue_insertgen(void); 99 static void general_deleteall(void); 100 static struct entry *general_findent(uintptr_t pc); 101 static void general_insertent(struct entry *entry); 102 static void general_printasm(FILE *fp, struct aggent *agg); 103 static int general_printc(FILE *fp, struct aggent *agg); 104 static int printblock(FILE *fp, struct aggent *agg); 105 static void usage(const char *progname) __dead2; 106 107 static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst); 108 static TAILQ_HEAD(, aggent) fqueue = TAILQ_HEAD_INITIALIZER(fqueue); 109 110 /* 111 * Use a float value in order to automatically promote operations 112 * to return a float value rather than use casts. 113 */ 114 static float totalsamples; 115 116 /* 117 * Identifies a string cointaining objdump's assembly printout. 118 */ 119 static inline int 120 isasminline(const char *str) 121 { 122 void *ptr; 123 int nbytes; 124 125 if (sscanf(str, " %p%n", &ptr, &nbytes) != 1) 126 return (0); 127 if (str[nbytes] != ':' || isspace(str[nbytes + 1]) == 0) 128 return (0); 129 return (1); 130 } 131 132 /* 133 * Identifies a string containing objdump's assembly printout 134 * for a new function. 135 */ 136 static inline int 137 newfunction(const char *str) 138 { 139 char fname[FNBUFF]; 140 void *ptr; 141 int nbytes; 142 143 if (isspace(str[0])) 144 return (0); 145 if (sscanf(str, "%p <%[^>:]>:%n", &ptr, fname, &nbytes) != 2) 146 return (0); 147 return (nbytes); 148 } 149 150 /* 151 * Create a new first-level aggregation object for a specified 152 * function. 153 */ 154 static struct aggent * 155 agg_create(const char *name, u_int nsamples, uintptr_t start, uintptr_t end) 156 { 157 struct aggent *agg; 158 159 agg = calloc(1, sizeof(struct aggent)); 160 if (agg == NULL) 161 return (NULL); 162 agg->ag_name = strdup(name); 163 if (agg->ag_name == NULL) { 164 free(agg); 165 return (NULL); 166 } 167 agg->ag_nsamples = nsamples; 168 agg->ag_ostart = start; 169 agg->ag_oend = end; 170 return (agg); 171 } 172 173 /* 174 * Destroy a first-level aggregation object for a specified 175 * function. 176 */ 177 static void 178 agg_destroy(struct aggent *agg) 179 { 180 181 free(agg->ag_name); 182 free(agg); 183 } 184 185 /* 186 * Analyze the "objdump -d" output, locate functions and start 187 * printing out the assembly functions content. 188 * We do not use newfunction() because we actually need the 189 * function name in available form, but the heurstic used is 190 * the same. 191 */ 192 static void 193 asmparse(FILE *fp) 194 { 195 char buffer[LNBUFF], fname[FNBUFF]; 196 struct aggent *agg; 197 void *ptr; 198 199 while (fgets(buffer, LNBUFF, fp) != NULL) { 200 if (isspace(buffer[0])) 201 continue; 202 if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2) 203 continue; 204 agg = fqueue_findent_by_name(fname); 205 if (agg == NULL) 206 continue; 207 agg->ag_offset = ftell(fp); 208 } 209 210 TAILQ_FOREACH(agg, &fqueue, ag_fiter) { 211 if (fseek(fp, agg->ag_offset, SEEK_SET) == -1) 212 return; 213 printf("Profile trace for function: %s() [%.2f%%]\n", 214 agg->ag_name, PERCSAMP(agg->ag_nsamples)); 215 general_printasm(fp, agg); 216 printf("\n"); 217 } 218 } 219 220 /* 221 * Analyze the "objdump -S" output, locate functions and start 222 * printing out the C functions content. 223 * We do not use newfunction() because we actually need the 224 * function name in available form, but the heurstic used is 225 * the same. 226 * In order to maintain the printout sorted, on the first pass it 227 * simply stores the file offsets in order to fastly moved later 228 * (when the file is hot-cached also) when the real printout will 229 * happen. 230 */ 231 static int 232 cparse(FILE *fp) 233 { 234 char buffer[LNBUFF], fname[FNBUFF]; 235 struct aggent *agg; 236 void *ptr; 237 238 while (fgets(buffer, LNBUFF, fp) != NULL) { 239 if (isspace(buffer[0])) 240 continue; 241 if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2) 242 continue; 243 agg = fqueue_findent_by_name(fname); 244 if (agg == NULL) 245 continue; 246 agg->ag_offset = ftell(fp); 247 } 248 249 TAILQ_FOREACH(agg, &fqueue, ag_fiter) { 250 if (fseek(fp, agg->ag_offset, SEEK_SET) == -1) 251 return (-1); 252 printf("Profile trace for function: %s() [%.2f%%]\n", 253 agg->ag_name, PERCSAMP(agg->ag_nsamples)); 254 if (general_printc(fp, agg) == -1) 255 return (-1); 256 printf("\n"); 257 } 258 return (0); 259 } 260 261 /* 262 * Bump the number of samples for any raw entry. 263 */ 264 static void 265 entry_acqref(struct entry *entry) 266 { 267 268 entry->en_nsamples++; 269 } 270 271 /* 272 * Create a new raw entry object for a specified function. 273 */ 274 static struct entry * 275 entry_create(const char *name, uintptr_t pc, uintptr_t start, uintptr_t end) 276 { 277 struct entry *obj; 278 279 obj = calloc(1, sizeof(struct entry)); 280 if (obj == NULL) 281 return (NULL); 282 obj->en_name = strdup(name); 283 if (obj->en_name == NULL) { 284 free(obj); 285 return (NULL); 286 } 287 obj->en_pc = pc; 288 obj->en_ostart = start; 289 obj->en_oend = end; 290 obj->en_nsamples = 1; 291 return (obj); 292 } 293 294 /* 295 * Destroy a raw entry object for a specified function. 296 */ 297 static void 298 entry_destroy(struct entry *entry) 299 { 300 301 free(entry->en_name); 302 free(entry); 303 } 304 305 /* 306 * Specify a lower bound in percentage and drop from the 307 * first-level aggregation queue all the objects with a 308 * smaller impact. 309 */ 310 static void 311 fqueue_compact(float th) 312 { 313 u_int thi; 314 struct aggent *agg, *tmpagg; 315 316 if (totalsamples == 0) 317 return; 318 319 /* Revert the percentage calculation. */ 320 thi = th * totalsamples / 100; 321 TAILQ_FOREACH_SAFE(agg, &fqueue, ag_fiter, tmpagg) 322 if (agg->ag_nsamples < thi) 323 TAILQ_REMOVE(&fqueue, agg, ag_fiter); 324 } 325 326 /* 327 * Flush the first-level aggregates queue. 328 */ 329 static void 330 fqueue_deleteall(void) 331 { 332 struct aggent *agg; 333 334 while (TAILQ_EMPTY(&fqueue) == 0) { 335 agg = TAILQ_FIRST(&fqueue); 336 TAILQ_REMOVE(&fqueue, agg, ag_fiter); 337 } 338 } 339 340 /* 341 * Insert a raw entry into the aggregations queue. 342 * If the respective first-level aggregation object 343 * does not exist create it and maintain it sorted 344 * in respect of the number of samples. 345 */ 346 static int 347 fqueue_insertent(struct entry *entry) 348 { 349 struct aggent *obj, *tmp; 350 int found; 351 352 found = 0; 353 TAILQ_FOREACH(obj, &fqueue, ag_fiter) 354 if (!strcmp(obj->ag_name, entry->en_name)) { 355 found = 1; 356 obj->ag_nsamples += entry->en_nsamples; 357 break; 358 } 359 360 /* 361 * If the first-level aggregation object already exists, 362 * just aggregate the samples and, if needed, resort 363 * it. 364 */ 365 if (found) { 366 TAILQ_REMOVE(&fqueue, obj, ag_fiter); 367 found = 0; 368 TAILQ_FOREACH(tmp, &fqueue, ag_fiter) 369 if (obj->ag_nsamples > tmp->ag_nsamples) { 370 found = 1; 371 break; 372 } 373 if (found) 374 TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter); 375 else 376 TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter); 377 return (0); 378 } 379 380 /* 381 * If the first-level aggregation object does not 382 * exist, create it and put in the sorted queue. 383 * If this is the first object, we need to set the 384 * head of the queue. 385 */ 386 obj = agg_create(entry->en_name, entry->en_nsamples, entry->en_ostart, 387 entry->en_oend); 388 if (obj == NULL) 389 return (-1); 390 if (TAILQ_EMPTY(&fqueue) != 0) { 391 TAILQ_INSERT_HEAD(&fqueue, obj, ag_fiter); 392 return (0); 393 } 394 TAILQ_FOREACH(tmp, &fqueue, ag_fiter) 395 if (obj->ag_nsamples > tmp->ag_nsamples) { 396 found = 1; 397 break; 398 } 399 if (found) 400 TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter); 401 else 402 TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter); 403 return (0); 404 } 405 406 /* 407 * Lookup a first-level aggregation object by name. 408 */ 409 static struct aggent * 410 fqueue_findent_by_name(const char *name) 411 { 412 struct aggent *obj; 413 414 TAILQ_FOREACH(obj, &fqueue, ag_fiter) 415 if (!strcmp(obj->ag_name, name)) 416 return (obj); 417 return (NULL); 418 } 419 420 /* 421 * Return the number of object in the first-level aggregations queue. 422 */ 423 static int 424 fqueue_getall(const char *bin, char *temp, int asmf) 425 { 426 char tmpf[MAXPATHLEN * 2 + 50]; 427 struct aggent *agg; 428 uintptr_t start, end; 429 430 if (mkstemp(temp) == -1) 431 return (-1); 432 TAILQ_FOREACH(agg, &fqueue, ag_fiter) { 433 bzero(tmpf, sizeof(tmpf)); 434 start = agg->ag_ostart; 435 end = agg->ag_oend; 436 437 /* 438 * Fix-up the end address in order to show it in the objdump's 439 * trace. 440 */ 441 end++; 442 if (asmf) 443 snprintf(tmpf, sizeof(tmpf), 444 "objdump --start-address=%p " 445 "--stop-address=%p -d %s >> %s", (void *)start, 446 (void *)end, bin, temp); 447 else 448 snprintf(tmpf, sizeof(tmpf), 449 "objdump --start-address=%p " 450 "--stop-address=%p -S %s >> %s", (void *)start, 451 (void *)end, bin, temp); 452 if (system(tmpf) != 0) 453 return (-1); 454 } 455 return (0); 456 } 457 458 /* 459 * Insert all the raw entries present in the general queue 460 * into the first-level aggregations queue. 461 */ 462 static int 463 fqueue_insertgen(void) 464 { 465 struct entry *obj; 466 467 TAILQ_FOREACH(obj, &mainlst, en_iter) 468 if (fqueue_insertent(obj) == -1) 469 return (-1); 470 return (0); 471 } 472 473 /* 474 * Flush the raw entries general queue. 475 */ 476 static void 477 general_deleteall(void) 478 { 479 struct entry *obj; 480 481 while (TAILQ_EMPTY(&mainlst) == 0) { 482 obj = TAILQ_FIRST(&mainlst); 483 TAILQ_REMOVE(&mainlst, obj, en_iter); 484 } 485 } 486 487 /* 488 * Lookup a raw entry by the PC. 489 */ 490 static struct entry * 491 general_findent(uintptr_t pc) 492 { 493 struct entry *obj; 494 495 TAILQ_FOREACH(obj, &mainlst, en_iter) 496 if (obj->en_pc == pc) 497 return (obj); 498 return (NULL); 499 } 500 501 /* 502 * Insert a new raw entry in the general queue. 503 */ 504 static void 505 general_insertent(struct entry *entry) 506 { 507 508 TAILQ_INSERT_TAIL(&mainlst, entry, en_iter); 509 } 510 511 /* 512 * Printout the body of an "objdump -d" assembly function. 513 * It does simply stops when a new function is encountered, 514 * bringing back the file position in order to not mess up 515 * subsequent analysis. 516 * C lines and others not recognized are simply skipped. 517 */ 518 static void 519 general_printasm(FILE *fp, struct aggent *agg) 520 { 521 char buffer[LNBUFF]; 522 struct entry *obj; 523 int nbytes; 524 void *ptr; 525 526 while (fgets(buffer, LNBUFF, fp) != NULL) { 527 if ((nbytes = newfunction(buffer)) != 0) { 528 fseek(fp, nbytes * -1, SEEK_CUR); 529 break; 530 } 531 if (!isasminline(buffer)) 532 continue; 533 if (sscanf(buffer, " %p:", &ptr) != 1) 534 continue; 535 obj = general_findent((uintptr_t)ptr); 536 if (obj == NULL) 537 printf("\t| %s", buffer); 538 else 539 printf("%.2f%%\t| %s", 540 (float)obj->en_nsamples * 100 / agg->ag_nsamples, 541 buffer); 542 } 543 } 544 545 /* 546 * Printout the body of an "objdump -S" function. 547 * It does simply stops when a new function is encountered, 548 * bringing back the file position in order to not mess up 549 * subsequent analysis. 550 * It expect from the starting to the end to find, always, valid blocks 551 * (see below for an explanation of the "block" concept). 552 */ 553 static int 554 general_printc(FILE *fp, struct aggent *agg) 555 { 556 char buffer[LNBUFF]; 557 558 while (fgets(buffer, LNBUFF, fp) != NULL) { 559 fseek(fp, strlen(buffer) * -1, SEEK_CUR); 560 if (newfunction(buffer) != 0) 561 break; 562 if (printblock(fp, agg) == -1) 563 return (-1); 564 } 565 return (0); 566 } 567 568 /* 569 * Printout a single block inside an "objdump -S" function. 570 * The block is composed of a first part in C and subsequent translation 571 * in assembly. 572 * This code also operates a second-level aggregation packing together 573 * samples relative to PCs into a (lower bottom) block with their 574 * C (higher half) counterpart. 575 */ 576 static int 577 printblock(FILE *fp, struct aggent *agg) 578 { 579 char buffer[LNBUFF]; 580 long lstart; 581 struct entry *obj; 582 u_int tnsamples; 583 int done, nbytes, sentinel; 584 void *ptr; 585 586 /* 587 * We expect the first thing of the block is C code, so simply give 588 * up if asm line is found. 589 */ 590 lstart = ftell(fp); 591 sentinel = 0; 592 for (;;) { 593 if (fgets(buffer, LNBUFF, fp) == NULL) 594 return (0); 595 if (isasminline(buffer) != 0) 596 break; 597 sentinel = 1; 598 nbytes = newfunction(buffer); 599 if (nbytes != 0) { 600 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 601 return (-1); 602 return (0); 603 } 604 } 605 606 /* 607 * If the sentinel is not set, it means it did not match any 608 * "high half" for this code so simply give up. 609 * Operates the second-level aggregation. 610 */ 611 tnsamples = 0; 612 do { 613 if (sentinel == 0) 614 return (-1); 615 if (sscanf(buffer, " %p:", &ptr) != 1) 616 return (-1); 617 obj = general_findent((uintptr_t)ptr); 618 if (obj != NULL) 619 tnsamples += obj->en_nsamples; 620 } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0); 621 622 /* Rewind to the start of the block in order to start the printout. */ 623 if (fseek(fp, lstart, SEEK_SET) == -1) 624 return (-1); 625 626 /* Again the high half of the block rappresenting the C part. */ 627 done = 0; 628 while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) { 629 if (tnsamples == 0 || done != 0) 630 printf("\t| %s", buffer); 631 else { 632 done = 1; 633 printf("%.2f%%\t| %s", 634 (float)tnsamples * 100 / agg->ag_nsamples, buffer); 635 } 636 } 637 638 /* 639 * Again the low half of the block rappresenting the asm 640 * translation part. 641 */ 642 for (;;) { 643 if (fgets(buffer, LNBUFF, fp) == NULL) 644 return (0); 645 if (isasminline(buffer) == 0) 646 break; 647 nbytes = newfunction(buffer); 648 if (nbytes != 0) { 649 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 650 return (-1); 651 return (0); 652 } 653 } 654 if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1) 655 return (-1); 656 return (0); 657 } 658 659 /* 660 * Helper printout functions. 661 */ 662 static void 663 usage(const char *progname) 664 { 665 666 fprintf(stderr, 667 "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n", 668 progname); 669 exit(EXIT_SUCCESS); 670 } 671 672 int 673 main(int argc, char *argv[]) 674 { 675 char buffer[LNBUFF], fname[FNBUFF]; 676 char *tbfl, *tofl, *tmpdir; 677 char tmpf[MAXPATHLEN * 2 + 50]; 678 float limit; 679 char *bin, *exec, *kfile, *ofile; 680 struct entry *obj; 681 FILE *gfp, *bfp; 682 void *ptr, *hstart, *hend; 683 uintptr_t tmppc, ostart, oend; 684 int cget, asmsrc; 685 686 exec = argv[0]; 687 ofile = NULL; 688 bin = NULL; 689 kfile = NULL; 690 asmsrc = 0; 691 limit = 0.5; 692 while ((cget = getopt(argc, argv, "ahl:k:")) != -1) 693 switch(cget) { 694 case 'a': 695 asmsrc = 1; 696 break; 697 case 'k': 698 kfile = optarg; 699 break; 700 case 'l': 701 limit = (float)atof(optarg); 702 break; 703 case 'h': 704 case '?': 705 default: 706 usage(exec); 707 } 708 argc -= optind; 709 argv += optind; 710 if (argc != 2) 711 usage(exec); 712 ofile = argv[0]; 713 bin = argv[1]; 714 715 if (access(bin, R_OK | F_OK) == -1) 716 FATAL(exec, "%s: Impossible to locate the binary file\n", 717 exec); 718 if (access(ofile, R_OK | F_OK) == -1) 719 FATAL(exec, "%s: Impossible to locate the pmcstat file\n", 720 exec); 721 if (kfile != NULL && access(kfile, R_OK | F_OK) == -1) 722 FATAL(exec, "%s: Impossible to locate the kernel file\n", 723 exec); 724 725 bzero(tmpf, sizeof(tmpf)); 726 tmpdir = getenv("TMPDIR"); 727 if (tmpdir == NULL) { 728 asprintf(&tbfl, "%s/%s", _PATH_TMP, TMPNAME); 729 asprintf(&tofl, "%s/%s", _PATH_TMP, TMPNAME); 730 } else { 731 asprintf(&tbfl, "%s/%s", tmpdir, TMPNAME); 732 asprintf(&tofl, "%s/%s", tmpdir, TMPNAME); 733 } 734 if (tofl == NULL || tbfl == NULL) 735 FATAL(exec, "%s: Cannot create tempfile templates\n", 736 exec); 737 if (mkstemp(tofl) == -1) 738 FATAL(exec, "%s: Impossible to create the tmp file\n", 739 exec); 740 if (kfile != NULL) 741 snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s", 742 kfile, ofile, tofl); 743 else 744 snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile, 745 tofl); 746 if (system(tmpf) != 0) 747 FATAL(exec, "%s: Impossible to create the tmp file\n", 748 exec); 749 750 gfp = fopen(tofl, "r"); 751 if (gfp == NULL) 752 FATAL(exec, "%s: Impossible to open the map file\n", 753 exec); 754 755 /* 756 * Make the collection of raw entries from a pmcstat mapped file. 757 * The heuristic here wants strings in the form: 758 * "addr funcname startfaddr endfaddr". 759 */ 760 while (fgets(buffer, LNBUFF, gfp) != NULL) { 761 if (isspace(buffer[0])) 762 continue; 763 if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname, 764 &hstart, &hend) != 4) 765 FATAL(NULL, 766 "%s: Invalid scan of function in the map file\n", 767 exec); 768 ostart = (uintptr_t)hstart; 769 oend = (uintptr_t)hend; 770 tmppc = (uintptr_t)ptr; 771 totalsamples++; 772 obj = general_findent(tmppc); 773 if (obj != NULL) { 774 entry_acqref(obj); 775 continue; 776 } 777 obj = entry_create(fname, tmppc, ostart, oend); 778 if (obj == NULL) 779 FATAL(exec, 780 "%s: Impossible to create a new object\n", exec); 781 general_insertent(obj); 782 } 783 if (fclose(gfp) == EOF) 784 FATAL(exec, "%s: Impossible to close the filedesc\n", 785 exec); 786 if (remove(tofl) == -1) 787 FATAL(exec, "%s: Impossible to remove the tmpfile\n", 788 exec); 789 790 /* 791 * Remove the loose end objects and feed the first-level aggregation 792 * queue. 793 */ 794 if (fqueue_insertgen() == -1) 795 FATAL(exec, "%s: Impossible to generate an analysis\n", 796 exec); 797 fqueue_compact(limit); 798 if (fqueue_getall(bin, tbfl, asmsrc) == -1) 799 FATAL(exec, "%s: Impossible to create the tmp file\n", 800 exec); 801 802 bfp = fopen(tbfl, "r"); 803 if (bfp == NULL) 804 FATAL(exec, "%s: Impossible to open the binary file\n", 805 exec); 806 807 if (asmsrc != 0) 808 asmparse(bfp); 809 else if (cparse(bfp) == -1) 810 FATAL(NULL, "%s: Invalid format for the C file\n", exec); 811 if (fclose(bfp) == EOF) 812 FATAL(exec, "%s: Impossible to close the filedesc\n", 813 exec); 814 if (remove(tbfl) == -1) 815 FATAL(exec, "%s: Impossible to remove the tmpfile\n", 816 exec); 817 return (0); 818 } 819