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