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
isasminline(const char * str)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
newfunction(const char * str)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 *
agg_create(const char * name,u_int nsamples,uintptr_t start,uintptr_t end)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
agg_destroy(struct aggent * agg)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
asmparse(FILE * fp)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
cparse(FILE * fp)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
entry_acqref(struct entry * entry)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 *
entry_create(const char * name,uintptr_t pc,uintptr_t start,uintptr_t end)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
entry_destroy(struct entry * entry)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
fqueue_compact(float th)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
fqueue_deleteall(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
fqueue_insertent(struct entry * entry)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 *
fqueue_findent_by_name(const char * name)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
fqueue_getall(const char * bin,char * temp,int asmf)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
fqueue_insertgen(void)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
general_deleteall(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 *
general_findent(uintptr_t pc)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
general_insertent(struct entry * entry)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 *
print_count(u_int nsamples,u_int totsamples)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
general_printasm(FILE * fp,struct aggent * agg)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
general_printc(FILE * fp,struct aggent * agg)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
printblock(FILE * fp,struct aggent * agg)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
usage(const char * progname)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
main(int argc,char * argv[])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