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