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/param.h> 34 #include <sys/queue.h> 35 36 #include <ctype.h> 37 #include <err.h> 38 #include <paths.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 43 #include <unistd.h> 44 45 /* NB: Make sure FNBUFF is as large as LNBUFF, otherwise it could overflow */ 46 #define FNBUFF 512 47 #define LNBUFF 512 48 49 #define TMPNAME "pmcannotate.XXXXXX" 50 51 #define FATAL(ptr, x ...) do { \ 52 fqueue_deleteall(); \ 53 general_deleteall(); \ 54 if ((ptr) != NULL) \ 55 perror(ptr); \ 56 fprintf(stderr, ##x); \ 57 remove(tbfl); \ 58 remove(tofl); \ 59 exit(EXIT_FAILURE); \ 60 } while (0) 61 62 #define PERCSAMP(x) ((float)(x) * 100 / totalsamples) 63 64 struct entry { 65 TAILQ_ENTRY(entry) en_iter; 66 char *en_name; 67 uintptr_t en_pc; 68 uintptr_t en_ostart; 69 uintptr_t en_oend; 70 u_int en_nsamples; 71 }; 72 73 struct aggent { 74 TAILQ_ENTRY(aggent) ag_fiter; 75 long ag_offset; 76 uintptr_t ag_ostart; 77 uintptr_t ag_oend; 78 char *ag_name; 79 u_int ag_nsamples; 80 }; 81 82 static struct aggent *agg_create(const char *name, u_int nsamples, 83 uintptr_t start, uintptr_t end); 84 static void agg_destroy(struct aggent *agg) __unused; 85 static void asmparse(FILE *fp); 86 static int cparse(FILE *fp); 87 static void entry_acqref(struct entry *entry); 88 static struct entry *entry_create(const char *name, uintptr_t pc, 89 uintptr_t start, uintptr_t end); 90 static void entry_destroy(struct entry *entry) __unused; 91 static void fqueue_compact(float th); 92 static void fqueue_deleteall(void); 93 static struct aggent *fqueue_findent_by_name(const char *name); 94 static int fqueue_getall(const char *bin, char *temp, int asmf); 95 static int fqueue_insertent(struct entry *entry); 96 static int fqueue_insertgen(void); 97 static void general_deleteall(void); 98 static struct entry *general_findent(uintptr_t pc); 99 static void general_insertent(struct entry *entry); 100 static void general_printasm(FILE *fp, struct aggent *agg); 101 static int general_printc(FILE *fp, struct aggent *agg); 102 static int printblock(FILE *fp, struct aggent *agg); 103 static void usage(const char *progname) __dead2; 104 105 static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst); 106 static TAILQ_HEAD(, aggent) fqueue = TAILQ_HEAD_INITIALIZER(fqueue); 107 108 /* 109 * Use a float value in order to automatically promote operations 110 * to return a float value rather than use casts. 111 */ 112 static u_int totalsamples; 113 114 static enum { RAW, BLOCK_PERCENT, GLOBAL_PERCENT } print_mode; 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 * Return a string either holding a percentage or the raw count value. 508 */ 509 static const char * 510 print_count(u_int nsamples, u_int totsamples) 511 { 512 static char buf[16]; 513 514 switch (print_mode) { 515 case RAW: 516 snprintf(buf, sizeof(buf), "%u", nsamples); 517 break; 518 case BLOCK_PERCENT: 519 snprintf(buf, sizeof(buf), "%.2f%%", (float)nsamples * 100 / 520 totsamples); 521 break; 522 case GLOBAL_PERCENT: 523 snprintf(buf, sizeof(buf), "%.2f%%", (float)nsamples * 100 / 524 totalsamples); 525 break; 526 } 527 return (buf); 528 } 529 530 /* 531 * Printout the body of an "objdump -d" assembly function. 532 * It does simply stops when a new function is encountered, 533 * bringing back the file position in order to not mess up 534 * subsequent analysis. 535 * C lines and others not recognized are simply skipped. 536 */ 537 static void 538 general_printasm(FILE *fp, struct aggent *agg) 539 { 540 char buffer[LNBUFF]; 541 struct entry *obj; 542 int nbytes; 543 void *ptr; 544 545 while (fgets(buffer, LNBUFF, fp) != NULL) { 546 if ((nbytes = newfunction(buffer)) != 0) { 547 fseek(fp, nbytes * -1, SEEK_CUR); 548 break; 549 } 550 if (!isasminline(buffer)) 551 continue; 552 if (sscanf(buffer, " %p:", &ptr) != 1) 553 continue; 554 obj = general_findent((uintptr_t)ptr); 555 if (obj == NULL) 556 printf("\t| %s", buffer); 557 else 558 printf("%7s | %s", 559 print_count(obj->en_nsamples, agg->ag_nsamples), 560 buffer); 561 } 562 } 563 564 /* 565 * Printout the body of an "objdump -S" function. 566 * It does simply stops when a new function is encountered, 567 * bringing back the file position in order to not mess up 568 * subsequent analysis. 569 * It expect from the starting to the end to find, always, valid blocks 570 * (see below for an explanation of the "block" concept). 571 */ 572 static int 573 general_printc(FILE *fp, struct aggent *agg) 574 { 575 char buffer[LNBUFF]; 576 577 while (fgets(buffer, LNBUFF, fp) != NULL) { 578 fseek(fp, strlen(buffer) * -1, SEEK_CUR); 579 if (newfunction(buffer) != 0) 580 break; 581 if (printblock(fp, agg) == -1) 582 return (-1); 583 } 584 return (0); 585 } 586 587 /* 588 * Printout a single block inside an "objdump -S" function. 589 * The block is composed of a first part in C and subsequent translation 590 * in assembly. 591 * This code also operates a second-level aggregation packing together 592 * samples relative to PCs into a (lower bottom) block with their 593 * C (higher half) counterpart. 594 */ 595 static int 596 printblock(FILE *fp, struct aggent *agg) 597 { 598 char buffer[LNBUFF]; 599 long lstart; 600 struct entry *obj; 601 u_int tnsamples; 602 int done, nbytes, sentinel; 603 void *ptr; 604 605 /* 606 * We expect the first thing of the block is C code, so simply give 607 * up if asm line is found. 608 */ 609 lstart = ftell(fp); 610 sentinel = 0; 611 for (;;) { 612 if (fgets(buffer, LNBUFF, fp) == NULL) 613 return (0); 614 if (isasminline(buffer) != 0) 615 break; 616 sentinel = 1; 617 nbytes = newfunction(buffer); 618 if (nbytes != 0) { 619 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 620 return (-1); 621 return (0); 622 } 623 } 624 625 /* 626 * If the sentinel is not set, it means it did not match any 627 * "high half" for this code so simply give up. 628 * Operates the second-level aggregation. 629 */ 630 tnsamples = 0; 631 do { 632 if (sentinel == 0) 633 return (-1); 634 if (sscanf(buffer, " %p:", &ptr) != 1) 635 return (-1); 636 obj = general_findent((uintptr_t)ptr); 637 if (obj != NULL) 638 tnsamples += obj->en_nsamples; 639 } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0); 640 641 /* Rewind to the start of the block in order to start the printout. */ 642 if (fseek(fp, lstart, SEEK_SET) == -1) 643 return (-1); 644 645 /* Again the high half of the block rappresenting the C part. */ 646 done = 0; 647 while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) { 648 if (tnsamples == 0 || done != 0) 649 printf("\t| %s", buffer); 650 else { 651 done = 1; 652 printf("%7s | %s", 653 print_count(tnsamples, agg->ag_nsamples), buffer); 654 } 655 } 656 657 /* 658 * Again the low half of the block rappresenting the asm 659 * translation part. 660 */ 661 for (;;) { 662 if (fgets(buffer, LNBUFF, fp) == NULL) 663 return (0); 664 if (isasminline(buffer) == 0) 665 break; 666 nbytes = newfunction(buffer); 667 if (nbytes != 0) { 668 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1) 669 return (-1); 670 return (0); 671 } 672 } 673 if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1) 674 return (-1); 675 return (0); 676 } 677 678 /* 679 * Helper printout functions. 680 */ 681 static void 682 usage(const char *progname) 683 { 684 685 fprintf(stderr, 686 "usage: %s [-a] [-h] [-k kfile] [-l lb] [-m mode] pmcraw.out binary\n", 687 progname); 688 exit(EXIT_SUCCESS); 689 } 690 691 int 692 main(int argc, char *argv[]) 693 { 694 char buffer[LNBUFF], fname[FNBUFF]; 695 char *tbfl, *tofl, *tmpdir; 696 char tmpf[MAXPATHLEN * 2 + 50]; 697 float limit; 698 char *bin, *exec, *kfile, *ofile; 699 struct entry *obj; 700 FILE *gfp, *bfp; 701 void *ptr, *hstart, *hend; 702 uintptr_t tmppc, ostart, oend; 703 int cget, asmsrc; 704 705 exec = argv[0]; 706 ofile = NULL; 707 bin = NULL; 708 kfile = NULL; 709 asmsrc = 0; 710 limit = 0.5; 711 print_mode = BLOCK_PERCENT; 712 while ((cget = getopt(argc, argv, "ahl:m:k:")) != -1) 713 switch(cget) { 714 case 'a': 715 asmsrc = 1; 716 break; 717 case 'k': 718 kfile = optarg; 719 break; 720 case 'l': 721 limit = (float)atof(optarg); 722 break; 723 case 'm': 724 if (strcasecmp(optarg, "raw") == 0) 725 print_mode = RAW; 726 else if (strcasecmp(optarg, "global") == 0) 727 print_mode = GLOBAL_PERCENT; 728 else if (strcasecmp(optarg, "block") == 0) 729 print_mode = BLOCK_PERCENT; 730 else 731 errx(1, "Invalid mode %s", optarg); 732 break; 733 case 'h': 734 case '?': 735 default: 736 usage(exec); 737 } 738 argc -= optind; 739 argv += optind; 740 if (argc != 2) 741 usage(exec); 742 ofile = argv[0]; 743 bin = argv[1]; 744 745 if (access(bin, R_OK | F_OK) == -1) 746 FATAL(exec, "%s: Impossible to locate the binary file\n", 747 exec); 748 if (access(ofile, R_OK | F_OK) == -1) 749 FATAL(exec, "%s: Impossible to locate the pmcstat file\n", 750 exec); 751 if (kfile != NULL && access(kfile, R_OK | F_OK) == -1) 752 FATAL(exec, "%s: Impossible to locate the kernel file\n", 753 exec); 754 755 bzero(tmpf, sizeof(tmpf)); 756 tmpdir = getenv("TMPDIR"); 757 if (tmpdir == NULL) { 758 asprintf(&tbfl, "%s/%s", _PATH_TMP, TMPNAME); 759 asprintf(&tofl, "%s/%s", _PATH_TMP, TMPNAME); 760 } else { 761 asprintf(&tbfl, "%s/%s", tmpdir, TMPNAME); 762 asprintf(&tofl, "%s/%s", tmpdir, TMPNAME); 763 } 764 if (tofl == NULL || tbfl == NULL) 765 FATAL(exec, "%s: Cannot create tempfile templates\n", 766 exec); 767 if (mkstemp(tofl) == -1) 768 FATAL(exec, "%s: Impossible to create the tmp file\n", 769 exec); 770 if (kfile != NULL) 771 snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s", 772 kfile, ofile, tofl); 773 else 774 snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile, 775 tofl); 776 if (system(tmpf) != 0) 777 FATAL(exec, "%s: Impossible to create the tmp file\n", 778 exec); 779 780 gfp = fopen(tofl, "r"); 781 if (gfp == NULL) 782 FATAL(exec, "%s: Impossible to open the map file\n", 783 exec); 784 785 /* 786 * Make the collection of raw entries from a pmcstat mapped file. 787 * The heuristic here wants strings in the form: 788 * "addr funcname startfaddr endfaddr". 789 */ 790 while (fgets(buffer, LNBUFF, gfp) != NULL) { 791 if (isspace(buffer[0])) 792 continue; 793 if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname, 794 &hstart, &hend) != 4) 795 FATAL(NULL, 796 "%s: Invalid scan of function in the map file\n", 797 exec); 798 ostart = (uintptr_t)hstart; 799 oend = (uintptr_t)hend; 800 tmppc = (uintptr_t)ptr; 801 totalsamples++; 802 obj = general_findent(tmppc); 803 if (obj != NULL) { 804 entry_acqref(obj); 805 continue; 806 } 807 obj = entry_create(fname, tmppc, ostart, oend); 808 if (obj == NULL) 809 FATAL(exec, 810 "%s: Impossible to create a new object\n", exec); 811 general_insertent(obj); 812 } 813 if (fclose(gfp) == EOF) 814 FATAL(exec, "%s: Impossible to close the filedesc\n", 815 exec); 816 if (remove(tofl) == -1) 817 FATAL(exec, "%s: Impossible to remove the tmpfile\n", 818 exec); 819 820 /* 821 * Remove the loose end objects and feed the first-level aggregation 822 * queue. 823 */ 824 if (fqueue_insertgen() == -1) 825 FATAL(exec, "%s: Impossible to generate an analysis\n", 826 exec); 827 fqueue_compact(limit); 828 if (fqueue_getall(bin, tbfl, asmsrc) == -1) 829 FATAL(exec, "%s: Impossible to create the tmp file\n", 830 exec); 831 832 bfp = fopen(tbfl, "r"); 833 if (bfp == NULL) 834 FATAL(exec, "%s: Impossible to open the binary file\n", 835 exec); 836 837 if (asmsrc != 0) 838 asmparse(bfp); 839 else if (cparse(bfp) == -1) 840 FATAL(NULL, "%s: Invalid format for the C file\n", exec); 841 if (fclose(bfp) == EOF) 842 FATAL(exec, "%s: Impossible to close the filedesc\n", 843 exec); 844 if (remove(tbfl) == -1) 845 FATAL(exec, "%s: Impossible to remove the tmpfile\n", 846 exec); 847 return (0); 848 } 849