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