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 161 45 #define LNBUFF 161 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 62 struct 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 71 struct 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 80 static struct aggent *agg_create(const char *name, u_int nsamples, 81 uintptr_t start, uintptr_t end); 82 static void agg_destroy(struct aggent *agg) __unused; 83 static void asmparse(FILE *fp); 84 static int cparse(FILE *fp); 85 static void entry_acqref(struct entry *entry); 86 static struct entry *entry_create(const char *name, uintptr_t pc, 87 uintptr_t start, uintptr_t end); 88 static void entry_destroy(struct entry *entry) __unused; 89 static void fqueue_compact(float th); 90 static void fqueue_deleteall(void); 91 static struct aggent *fqueue_findent_by_name(const char *name); 92 static int fqueue_getall(const char *bin, char *temp, int asmf); 93 static int fqueue_insertent(struct entry *entry); 94 static int fqueue_insertgen(void); 95 static void general_deleteall(void); 96 static struct entry *general_findent(uintptr_t pc); 97 static void general_insertent(struct entry *entry); 98 static void general_printasm(FILE *fp, struct aggent *agg); 99 static int general_printc(FILE *fp, struct aggent *agg); 100 static int printblock(FILE *fp, struct aggent *agg); 101 static void usage(const char *progname) __dead2; 102 103 static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst); 104 static 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 */ 110 static float totalsamples; 111 112 /* 113 * Identifies a string cointaining objdump's assembly printout. 114 */ 115 static inline int 116 isasminline(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 */ 132 static inline int 133 newfunction(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 */ 150 static struct aggent * 151 agg_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 */ 173 static void 174 agg_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 */ 188 static void 189 asmparse(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 */ 227 static int 228 cparse(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 */ 260 static void 261 entry_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 */ 270 static struct entry * 271 entry_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 */ 293 static void 294 entry_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 */ 306 static void 307 fqueue_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 */ 325 static void 326 fqueue_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 */ 342 static int 343 fqueue_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 */ 405 static struct aggent * 406 fqueue_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 */ 419 static int 420 fqueue_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 */ 458 static int 459 fqueue_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 */ 472 static void 473 general_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 */ 486 static struct entry * 487 general_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 */ 500 static void 501 general_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 */ 514 static void 515 general_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 */ 549 static int 550 general_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 */ 572 static int 573 printblock(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 */ 658 static void 659 usage(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 668 int 669 main(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