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