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