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