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 __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 if (asmf) 438 snprintf(tmpf, sizeof(tmpf), 439 "objdump --start-address=%p " 440 "--stop-address=%p -d %s >> %s", (void *)start, 441 (void *)end, bin, temp); 442 else 443 snprintf(tmpf, sizeof(tmpf), 444 "objdump --start-address=%p " 445 "--stop-address=%p -S %s >> %s", (void *)start, 446 (void *)end, bin, temp); 447 if (system(tmpf) != 0) 448 return (-1); 449 } 450 return (0); 451 } 452 453 /* 454 * Insert all the raw entries present in the general queue 455 * into the first-level aggregations queue. 456 */ 457 static int 458 fqueue_insertgen(void) 459 { 460 struct entry *obj; 461 462 TAILQ_FOREACH(obj, &mainlst, en_iter) 463 if (fqueue_insertent(obj) == -1) 464 return (-1); 465 return (0); 466 } 467 468 /* 469 * Flush the raw entries general queue. 470 */ 471 static void 472 general_deleteall(void) 473 { 474 struct entry *obj; 475 476 while (TAILQ_EMPTY(&mainlst) == 0) { 477 obj = TAILQ_FIRST(&mainlst); 478 TAILQ_REMOVE(&mainlst, obj, en_iter); 479 } 480 } 481 482 /* 483 * Lookup a raw entry by the PC. 484 */ 485 static struct entry * 486 general_findent(uintptr_t pc) 487 { 488 struct entry *obj; 489 490 TAILQ_FOREACH(obj, &mainlst, en_iter) 491 if (obj->en_pc == pc) 492 return (obj); 493 return (NULL); 494 } 495 496 /* 497 * Insert a new raw entry in the general queue. 498 */ 499 static void 500 general_insertent(struct entry *entry) 501 { 502 503 TAILQ_INSERT_TAIL(&mainlst, entry, en_iter); 504 } 505 506 /* 507 * Printout the body of an "objdump -d" assembly function. 508 * It does simply stops when a new function is encountered, 509 * bringing back the file position in order to not mess up 510 * subsequent analysis. 511 * C lines and others not recognized are simply skipped. 512 */ 513 static void 514 general_printasm(FILE *fp, struct aggent *agg) 515 { 516 char buffer[LNBUFF]; 517 struct entry *obj; 518 int nbytes; 519 void *ptr; 520 521 while (fgets(buffer, LNBUFF, fp) != NULL) { 522 if ((nbytes = newfunction(buffer)) != 0) { 523 fseek(fp, nbytes * -1, SEEK_CUR); 524 break; 525 } 526 if (!isasminline(buffer)) 527 continue; 528 if (sscanf(buffer, " %p:", &ptr) != 1) 529 continue; 530 obj = general_findent((uintptr_t)ptr); 531 if (obj == NULL) 532 printf("\t| %s", buffer); 533 else 534 printf("%.2f%%\t| %s", 535 (float)obj->en_nsamples * 100 / agg->ag_nsamples, 536 buffer); 537 } 538 } 539 540 /* 541 * Printout the body of an "objdump -S" function. 542 * It does simply stops when a new function is encountered, 543 * bringing back the file position in order to not mess up 544 * subsequent analysis. 545 * It expect from the starting to the end to find, always, valid blocks 546 * (see below for an explanation of the "block" concept). 547 */ 548 static int 549 general_printc(FILE *fp, struct aggent *agg) 550 { 551 char buffer[LNBUFF]; 552 553 while (fgets(buffer, LNBUFF, fp) != NULL) { 554 fseek(fp, strlen(buffer) * -1, SEEK_CUR); 555 if (newfunction(buffer) != 0) 556 break; 557 if (printblock(fp, agg) == -1) 558 return (-1); 559 } 560 return (0); 561 } 562 563 /* 564 * Printout a single block inside an "objdump -S" function. 565 * The block is composed of a first part in C and subsequent translation 566 * in assembly. 567 * This code also operates a second-level aggregation packing together 568 * samples relative to PCs into a (lower bottom) block with their 569 * C (higher half) counterpart. 570 */ 571 static int 572 printblock(FILE *fp, struct aggent *agg) 573 { 574 char buffer[LNBUFF]; 575 long lstart; 576 struct entry *obj; 577 u_int tnsamples; 578 int done, nbytes, sentinel; 579 void *ptr; 580 581 /* 582 * We expect the first thing of the block is C code, so simply give 583 * up if asm line is found. 584 */ 585 lstart = ftell(fp); 586 sentinel = 0; 587 for (;;) { 588 if (fgets(buffer, LNBUFF, fp) == NULL) 589 return (0); 590 if (isasminline(buffer) != 0) 591 break; 592 sentinel = 1; 593 nbytes = newfunction(buffer); 594 if (nbytes != 0) { 595 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 596 return (-1); 597 return (0); 598 } 599 } 600 601 /* 602 * If the sentinel is not set, it means it did not match any 603 * "high half" for this code so simply give up. 604 * Operates the second-level aggregation. 605 */ 606 tnsamples = 0; 607 do { 608 if (sentinel == 0) 609 return (-1); 610 if (sscanf(buffer, " %p:", &ptr) != 1) 611 return (-1); 612 obj = general_findent((uintptr_t)ptr); 613 if (obj != NULL) 614 tnsamples += obj->en_nsamples; 615 } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0); 616 617 /* Rewind to the start of the block in order to start the printout. */ 618 if (fseek(fp, lstart, SEEK_SET) == -1) 619 return (-1); 620 621 /* Again the high half of the block rappresenting the C part. */ 622 done = 0; 623 while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) { 624 if (tnsamples == 0 || done != 0) 625 printf("\t| %s", buffer); 626 else { 627 done = 1; 628 printf("%.2f%%\t| %s", 629 (float)tnsamples * 100 / agg->ag_nsamples, buffer); 630 } 631 } 632 633 /* 634 * Again the low half of the block rappresenting the asm 635 * translation part. 636 */ 637 for (;;) { 638 if (fgets(buffer, LNBUFF, fp) == NULL) 639 return (0); 640 if (isasminline(buffer) == 0) 641 break; 642 nbytes = newfunction(buffer); 643 if (nbytes != 0) { 644 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 645 return (-1); 646 return (0); 647 } 648 } 649 if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1) 650 return (-1); 651 return (0); 652 } 653 654 /* 655 * Helper printout functions. 656 */ 657 static void 658 usage(const char *progname) 659 { 660 661 fprintf(stderr, 662 "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n", 663 progname); 664 exit(EXIT_SUCCESS); 665 } 666 667 int 668 main(int argc, char *argv[]) 669 { 670 char buffer[LNBUFF], fname[FNBUFF]; 671 char *tbfl, *tofl, *tmpdir; 672 char tmpf[MAXPATHLEN * 2 + 50]; 673 float limit; 674 char *bin, *exec, *kfile, *ofile; 675 struct entry *obj; 676 FILE *gfp, *bfp; 677 void *ptr, *hstart, *hend; 678 uintptr_t tmppc, ostart, oend; 679 int cget, asmsrc; 680 681 exec = argv[0]; 682 ofile = NULL; 683 bin = NULL; 684 kfile = NULL; 685 asmsrc = 0; 686 limit = 0.5; 687 while ((cget = getopt(argc, argv, "ahl:k:")) != -1) 688 switch(cget) { 689 case 'a': 690 asmsrc = 1; 691 break; 692 case 'k': 693 kfile = optarg; 694 break; 695 case 'l': 696 limit = (float)atof(optarg); 697 break; 698 case 'h': 699 case '?': 700 default: 701 usage(exec); 702 } 703 argc -= optind; 704 argv += optind; 705 if (argc != 2) 706 usage(exec); 707 ofile = argv[0]; 708 bin = argv[1]; 709 710 if (access(bin, R_OK | F_OK) == -1) 711 FATAL(exec, "%s: Impossible to locate the binary file\n", 712 exec); 713 if (access(ofile, R_OK | F_OK) == -1) 714 FATAL(exec, "%s: Impossible to locate the pmcstat file\n", 715 exec); 716 if (kfile != NULL && access(kfile, R_OK | F_OK) == -1) 717 FATAL(exec, "%s: Impossible to locate the kernel file\n", 718 exec); 719 720 bzero(tmpf, sizeof(tmpf)); 721 tmpdir = getenv("TMPDIR"); 722 if (tmpdir == NULL) { 723 asprintf(&tbfl, "%s/%s", _PATH_TMP, TMPNAME); 724 asprintf(&tofl, "%s/%s", _PATH_TMP, TMPNAME); 725 } else { 726 asprintf(&tbfl, "%s/%s", tmpdir, TMPNAME); 727 asprintf(&tofl, "%s/%s", tmpdir, TMPNAME); 728 } 729 if (tofl == NULL || tbfl == NULL) 730 FATAL(exec, "%s: Cannot create tempfile templates\n", 731 exec); 732 if (mkstemp(tofl) == -1) 733 FATAL(exec, "%s: Impossible to create the tmp file\n", 734 exec); 735 if (kfile != NULL) 736 snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s", 737 kfile, ofile, tofl); 738 else 739 snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile, 740 tofl); 741 if (system(tmpf) != 0) 742 FATAL(exec, "%s: Impossible to create the tmp file\n", 743 exec); 744 745 gfp = fopen(tofl, "r"); 746 if (gfp == NULL) 747 FATAL(exec, "%s: Impossible to open the map file\n", 748 exec); 749 750 /* 751 * Make the collection of raw entries from a pmcstat mapped file. 752 * The heuristic here wants strings in the form: 753 * "addr funcname startfaddr endfaddr". 754 */ 755 while (fgets(buffer, LNBUFF, gfp) != NULL) { 756 if (isspace(buffer[0])) 757 continue; 758 if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname, 759 &hstart, &hend) != 4) 760 FATAL(NULL, 761 "%s: Invalid scan of function in the map file\n", 762 exec); 763 ostart = (uintptr_t)hstart; 764 oend = (uintptr_t)hend; 765 tmppc = (uintptr_t)ptr; 766 totalsamples++; 767 obj = general_findent(tmppc); 768 if (obj != NULL) { 769 entry_acqref(obj); 770 continue; 771 } 772 obj = entry_create(fname, tmppc, ostart, oend); 773 if (obj == NULL) 774 FATAL(exec, 775 "%s: Impossible to create a new object\n", exec); 776 general_insertent(obj); 777 } 778 if (fclose(gfp) == EOF) 779 FATAL(exec, "%s: Impossible to close the filedesc\n", 780 exec); 781 if (remove(tofl) == -1) 782 FATAL(exec, "%s: Impossible to remove the tmpfile\n", 783 exec); 784 785 /* 786 * Remove the loose end objects and feed the first-level aggregation 787 * queue. 788 */ 789 if (fqueue_insertgen() == -1) 790 FATAL(exec, "%s: Impossible to generate an analysis\n", 791 exec); 792 fqueue_compact(limit); 793 if (fqueue_getall(bin, tbfl, asmsrc) == -1) 794 FATAL(exec, "%s: Impossible to create the tmp file\n", 795 exec); 796 797 bfp = fopen(tbfl, "r"); 798 if (bfp == NULL) 799 FATAL(exec, "%s: Impossible to open the binary file\n", 800 exec); 801 802 if (asmsrc != 0) 803 asmparse(bfp); 804 else if (cparse(bfp) == -1) 805 FATAL(NULL, "%s: Invalid format for the C file\n", exec); 806 if (fclose(bfp) == EOF) 807 FATAL(exec, "%s: Impossible to close the filedesc\n", 808 exec); 809 if (remove(tbfl) == -1) 810 FATAL(exec, "%s: Impossible to remove the tmpfile\n", 811 exec); 812 return (0); 813 } 814