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