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