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