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