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