]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pmcannotate/pmcannotate.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / usr.sbin / pmcannotate / pmcannotate.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/queue.h>
38
39 #include <ctype.h>
40 #include <paths.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include <unistd.h>
46
47 /* NB: Make sure FNBUFF is as large as LNBUFF, otherwise it could overflow */
48 #define FNBUFF  512
49 #define LNBUFF  512
50
51 #define TMPNAME "pmcannotate.XXXXXX"
52
53 #define FATAL(ptr, x ...) do {                                          \
54         fqueue_deleteall();                                             \
55         general_deleteall();                                            \
56         if ((ptr) != NULL)                                              \
57                 perror(ptr);                                            \
58         fprintf(stderr, ##x);                                           \
59         remove(tbfl);                                                   \
60         remove(tofl);                                                   \
61         exit(EXIT_FAILURE);                                             \
62 } while (0)
63
64 #define PERCSAMP(x)     ((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 float totalsamples;
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                 /*
438                  * Fix-up the end address in order to show it in the objdump's
439                  * trace.
440                  */
441                 end++;
442                 if (asmf)
443                         snprintf(tmpf, sizeof(tmpf),
444                             "objdump --start-address=%p "
445                             "--stop-address=%p -d %s >> %s", (void *)start,
446                             (void *)end, bin, temp);
447                 else
448                         snprintf(tmpf, sizeof(tmpf),
449                             "objdump --start-address=%p "
450                             "--stop-address=%p -S %s >> %s", (void *)start,
451                             (void *)end, bin, temp);
452                 if (system(tmpf) != 0)
453                         return (-1);
454         }
455         return (0);
456 }
457
458 /*
459  * Insert all the raw entries present in the general queue
460  * into the first-level aggregations queue.
461  */
462 static int
463 fqueue_insertgen(void)
464 {
465         struct entry *obj;
466
467         TAILQ_FOREACH(obj, &mainlst, en_iter)
468                 if (fqueue_insertent(obj) == -1)
469                         return (-1);
470         return (0);
471 }
472
473 /*
474  * Flush the raw entries general queue.
475  */
476 static void
477 general_deleteall(void)
478 {
479         struct entry *obj;
480
481         while (TAILQ_EMPTY(&mainlst) == 0) {
482                 obj = TAILQ_FIRST(&mainlst);
483                 TAILQ_REMOVE(&mainlst, obj, en_iter);
484         }
485 }
486
487 /*
488  * Lookup a raw entry by the PC.
489  */
490 static struct entry *
491 general_findent(uintptr_t pc)
492 {
493         struct entry *obj;
494
495         TAILQ_FOREACH(obj, &mainlst, en_iter)
496                 if (obj->en_pc == pc)
497                         return (obj);
498         return (NULL);
499 }
500
501 /*
502  * Insert a new raw entry in the general queue.
503  */
504 static void
505 general_insertent(struct entry *entry)
506 {
507
508         TAILQ_INSERT_TAIL(&mainlst, entry, en_iter);
509 }
510
511 /*
512  * Printout the body of an "objdump -d" assembly function.
513  * It does simply stops when a new function is encountered,
514  * bringing back the file position in order to not mess up
515  * subsequent analysis.
516  * C lines and others not recognized are simply skipped.
517  */
518 static void
519 general_printasm(FILE *fp, struct aggent *agg)
520 {
521         char buffer[LNBUFF];
522         struct entry *obj;
523         int nbytes;
524         void *ptr;
525
526         while (fgets(buffer, LNBUFF, fp) != NULL) {
527                 if ((nbytes = newfunction(buffer)) != 0) {
528                         fseek(fp, nbytes * -1, SEEK_CUR);
529                         break;
530                 }
531                 if (!isasminline(buffer))
532                         continue;
533                 if (sscanf(buffer, " %p:", &ptr) != 1)
534                         continue;
535                 obj = general_findent((uintptr_t)ptr);
536                 if (obj == NULL)
537                         printf("\t| %s", buffer);
538                 else
539                         printf("%.2f%%\t| %s",
540                             (float)obj->en_nsamples * 100 / agg->ag_nsamples,
541                             buffer);
542         }
543 }
544
545 /*
546  * Printout the body of an "objdump -S" function.
547  * It does simply stops when a new function is encountered,
548  * bringing back the file position in order to not mess up
549  * subsequent analysis.
550  * It expect from the starting to the end to find, always, valid blocks
551  * (see below for an explanation of the "block" concept).
552  */
553 static int
554 general_printc(FILE *fp, struct aggent *agg)
555 {
556         char buffer[LNBUFF];
557
558         while (fgets(buffer, LNBUFF, fp) != NULL) {
559                 fseek(fp, strlen(buffer) * -1, SEEK_CUR);
560                 if (newfunction(buffer) != 0)
561                         break;
562                 if (printblock(fp, agg) == -1)
563                         return (-1);
564         }
565         return (0);
566 }
567
568 /*
569  * Printout a single block inside an "objdump -S" function.
570  * The block is composed of a first part in C and subsequent translation
571  * in assembly.
572  * This code also operates a second-level aggregation packing together
573  * samples relative to PCs into a (lower bottom) block with their
574  * C (higher half) counterpart.
575  */
576 static int
577 printblock(FILE *fp, struct aggent *agg)
578 {
579         char buffer[LNBUFF];
580         long lstart;
581         struct entry *obj;
582         u_int tnsamples;
583         int done, nbytes, sentinel;
584         void *ptr;
585
586         /*
587          * We expect the first thing of the block is C code, so simply give
588          * up if asm line is found.
589          */
590         lstart = ftell(fp);
591         sentinel = 0;
592         for (;;) {
593                 if (fgets(buffer, LNBUFF, fp) == NULL)
594                         return (0);
595                 if (isasminline(buffer) != 0)
596                         break;
597                 sentinel = 1;
598                 nbytes = newfunction(buffer);
599                 if (nbytes != 0) {
600                         if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
601                                 return (-1);
602                         return (0);
603                 }
604         }
605
606         /*
607          * If the sentinel is not set, it means it did not match any
608          * "high half" for this code so simply give up.
609          * Operates the second-level aggregation.
610          */
611         tnsamples = 0;
612         do {
613                 if (sentinel == 0)
614                         return (-1);
615                 if (sscanf(buffer, " %p:", &ptr) != 1)
616                         return (-1);
617                 obj = general_findent((uintptr_t)ptr);
618                 if (obj != NULL)
619                         tnsamples += obj->en_nsamples;
620         } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0);
621
622         /* Rewind to the start of the block in order to start the printout. */
623         if (fseek(fp, lstart, SEEK_SET) == -1)
624                 return (-1);
625
626         /* Again the high half of the block rappresenting the C part. */
627         done = 0;
628         while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) {
629                 if (tnsamples == 0 || done != 0)
630                         printf("\t| %s", buffer);
631                 else {
632                         done = 1;
633                         printf("%.2f%%\t| %s",
634                             (float)tnsamples * 100 / agg->ag_nsamples, buffer);
635                 }
636         }
637
638         /*
639          * Again the low half of the block rappresenting the asm
640          * translation part.
641          */
642         for (;;) {
643                 if (fgets(buffer, LNBUFF, fp) == NULL)
644                         return (0);
645                 if (isasminline(buffer) == 0)
646                         break;
647                 nbytes = newfunction(buffer);
648                 if (nbytes != 0) {
649                         if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
650                                 return (-1);
651                         return (0);
652                 }
653         }
654         if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1)
655                 return (-1);
656         return (0);
657 }
658
659 /*
660  * Helper printout functions.
661  */
662 static void
663 usage(const char *progname)
664 {
665
666         fprintf(stderr,
667             "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n",
668             progname);
669         exit(EXIT_SUCCESS);
670 }
671
672 int
673 main(int argc, char *argv[])
674 {
675         char buffer[LNBUFF], fname[FNBUFF];
676         char *tbfl, *tofl, *tmpdir;
677         char tmpf[MAXPATHLEN * 2 + 50];
678         float limit;
679         char *bin, *exec, *kfile, *ofile;
680         struct entry *obj;
681         FILE *gfp, *bfp;
682         void *ptr, *hstart, *hend;
683         uintptr_t tmppc, ostart, oend;
684         int cget, asmsrc;
685
686         exec = argv[0];
687         ofile = NULL;
688         bin = NULL;
689         kfile = NULL;
690         asmsrc = 0;
691         limit = 0.5;
692         while ((cget = getopt(argc, argv, "ahl:k:")) != -1)
693                 switch(cget) {
694                 case 'a':
695                         asmsrc = 1;
696                         break;
697                 case 'k':
698                         kfile = optarg;
699                         break;
700                 case 'l':
701                         limit = (float)atof(optarg);
702                         break;
703                 case 'h':
704                 case '?':
705                 default:
706                         usage(exec);
707                 }
708         argc -= optind;
709         argv += optind;
710         if (argc != 2)
711                 usage(exec);
712         ofile = argv[0];
713         bin = argv[1];
714
715         if (access(bin, R_OK | F_OK) == -1)
716                 FATAL(exec, "%s: Impossible to locate the binary file\n",
717                     exec);
718         if (access(ofile, R_OK | F_OK) == -1)
719                 FATAL(exec, "%s: Impossible to locate the pmcstat file\n",
720                     exec);
721         if (kfile != NULL && access(kfile, R_OK | F_OK) == -1)
722                 FATAL(exec, "%s: Impossible to locate the kernel file\n",
723                     exec);
724
725         bzero(tmpf, sizeof(tmpf));
726         tmpdir = getenv("TMPDIR");
727         if (tmpdir == NULL) {
728                 asprintf(&tbfl, "%s/%s", _PATH_TMP, TMPNAME);
729                 asprintf(&tofl, "%s/%s", _PATH_TMP, TMPNAME);
730         } else {
731                 asprintf(&tbfl, "%s/%s", tmpdir, TMPNAME);
732                 asprintf(&tofl, "%s/%s", tmpdir, TMPNAME);
733         }
734         if (tofl == NULL || tbfl == NULL)
735                 FATAL(exec, "%s: Cannot create tempfile templates\n",
736                     exec);
737         if (mkstemp(tofl) == -1)
738                 FATAL(exec, "%s: Impossible to create the tmp file\n",
739                     exec);
740         if (kfile != NULL)
741                 snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s",
742                     kfile, ofile, tofl);
743         else
744                 snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile,
745                     tofl);
746         if (system(tmpf) != 0)
747                 FATAL(exec, "%s: Impossible to create the tmp file\n",
748                     exec);
749
750         gfp = fopen(tofl, "r");
751         if (gfp == NULL)
752                 FATAL(exec, "%s: Impossible to open the map file\n",
753                     exec);
754
755         /*
756          * Make the collection of raw entries from a pmcstat mapped file.
757          * The heuristic here wants strings in the form:
758          * "addr funcname startfaddr endfaddr".
759          */
760         while (fgets(buffer, LNBUFF, gfp) != NULL) {
761                 if (isspace(buffer[0]))
762                         continue;
763                 if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname,
764                     &hstart, &hend) != 4)
765                         FATAL(NULL,
766                             "%s: Invalid scan of function in the map file\n",
767                             exec);
768                 ostart = (uintptr_t)hstart;
769                 oend = (uintptr_t)hend;
770                 tmppc = (uintptr_t)ptr;
771                 totalsamples++;
772                 obj = general_findent(tmppc);
773                 if (obj != NULL) {
774                         entry_acqref(obj);
775                         continue;
776                 }
777                 obj = entry_create(fname, tmppc, ostart, oend);
778                 if (obj == NULL)
779                         FATAL(exec,
780                             "%s: Impossible to create a new object\n", exec);
781                 general_insertent(obj);
782         }
783         if (fclose(gfp) == EOF)
784                 FATAL(exec, "%s: Impossible to close the filedesc\n",
785                     exec);
786         if (remove(tofl) == -1)
787                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
788                     exec);
789
790         /*
791          * Remove the loose end objects and feed the first-level aggregation
792          * queue.
793          */
794         if (fqueue_insertgen() == -1)
795                 FATAL(exec, "%s: Impossible to generate an analysis\n",
796                     exec);
797         fqueue_compact(limit);
798         if (fqueue_getall(bin, tbfl, asmsrc) == -1)
799                 FATAL(exec, "%s: Impossible to create the tmp file\n",
800                     exec);
801
802         bfp = fopen(tbfl, "r");
803         if (bfp == NULL)
804                 FATAL(exec, "%s: Impossible to open the binary file\n",
805                     exec);
806
807         if (asmsrc != 0)
808                 asmparse(bfp);
809         else if (cparse(bfp) == -1)
810                 FATAL(NULL, "%s: Invalid format for the C file\n", exec);
811         if (fclose(bfp) == EOF)
812                 FATAL(exec, "%s: Impossible to close the filedesc\n",
813                     exec);
814         if (remove(tbfl) == -1)
815                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
816                     exec);
817         return (0);
818 }