1 /*-
2  * Copyright (c) 2008 Nokia Corporation
3  * All rights reserved.
4  *
5  * This software was developed by Attilio Rao for the IPSO project under
6  * contract to Nokia Corporation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice unmodified, this list of conditions, and the following
13  *    disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 
37 #include <ctype.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	TMPPATH	"/tmp/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 		/*
435 		 * Fix-up the end address in order to show it in the objdump's
436 		 * trace.
437 		 */
438 		end++;
439 		if (asmf)
440 			snprintf(tmpf, sizeof(tmpf),
441 			    "objdump --start-address=%p "
442 			    "--stop-address=%p -d %s >> %s", (void *)start,
443 			    (void *)end, bin, temp);
444 		else
445 			snprintf(tmpf, sizeof(tmpf),
446 			    "objdump --start-address=%p "
447 			    "--stop-address=%p -S %s >> %s", (void *)start,
448 			    (void *)end, bin, temp);
449 		if (system(tmpf) != 0)
450 			return (-1);
451 	}
452 	return (0);
453 }
454 
455 /*
456  * Insert all the raw entries present in the general queue
457  * into the first-level aggregations queue.
458  */
459 static int
460 fqueue_insertgen(void)
461 {
462 	struct entry *obj;
463 
464 	TAILQ_FOREACH(obj, &mainlst, en_iter)
465 		if (fqueue_insertent(obj) == -1)
466 			return (-1);
467 	return (0);
468 }
469 
470 /*
471  * Flush the raw entries general queue.
472  */
473 static void
474 general_deleteall(void)
475 {
476 	struct entry *obj;
477 
478 	while (TAILQ_EMPTY(&mainlst) == 0) {
479 		obj = TAILQ_FIRST(&mainlst);
480 		TAILQ_REMOVE(&mainlst, obj, en_iter);
481 	}
482 }
483 
484 /*
485  * Lookup a raw entry by the PC.
486  */
487 static struct entry *
488 general_findent(uintptr_t pc)
489 {
490 	struct entry *obj;
491 
492 	TAILQ_FOREACH(obj, &mainlst, en_iter)
493 		if (obj->en_pc == pc)
494 			return (obj);
495 	return (NULL);
496 }
497 
498 /*
499  * Insert a new raw entry in the general queue.
500  */
501 static void
502 general_insertent(struct entry *entry)
503 {
504 
505 	TAILQ_INSERT_TAIL(&mainlst, entry, en_iter);
506 }
507 
508 /*
509  * Printout the body of an "objdump -d" assembly function.
510  * It does simply stops when a new function is encountered,
511  * bringing back the file position in order to not mess up
512  * subsequent analysis.
513  * C lines and others not recognized are simply skipped.
514  */
515 static void
516 general_printasm(FILE *fp, struct aggent *agg)
517 {
518 	char buffer[LNBUFF];
519 	struct entry *obj;
520 	int nbytes;
521 	void *ptr;
522 
523 	while (fgets(buffer, LNBUFF, fp) != NULL) {
524 		if ((nbytes = newfunction(buffer)) != 0) {
525 			fseek(fp, nbytes * -1, SEEK_CUR);
526 			break;
527 		}
528 		if (!isasminline(buffer))
529 			continue;
530 		if (sscanf(buffer, " %p:", &ptr) != 1)
531 			continue;
532 		obj = general_findent((uintptr_t)ptr);
533 		if (obj == NULL)
534 			printf("\t| %s", buffer);
535 		else
536 			printf("%.2f%%\t| %s",
537 			    (float)obj->en_nsamples * 100 / agg->ag_nsamples,
538 			    buffer);
539 	}
540 }
541 
542 /*
543  * Printout the body of an "objdump -S" function.
544  * It does simply stops when a new function is encountered,
545  * bringing back the file position in order to not mess up
546  * subsequent analysis.
547  * It expect from the starting to the end to find, always, valid blocks
548  * (see below for an explanation of the "block" concept).
549  */
550 static int
551 general_printc(FILE *fp, struct aggent *agg)
552 {
553 	char buffer[LNBUFF];
554 
555 	while (fgets(buffer, LNBUFF, fp) != NULL) {
556 		fseek(fp, strlen(buffer) * -1, SEEK_CUR);
557 		if (newfunction(buffer) != 0)
558 			break;
559 		if (printblock(fp, agg) == -1)
560 			return (-1);
561 	}
562 	return (0);
563 }
564 
565 /*
566  * Printout a single block inside an "objdump -S" function.
567  * The block is composed of a first part in C and subsequent translation
568  * in assembly.
569  * This code also operates a second-level aggregation packing together
570  * samples relative to PCs into a (lower bottom) block with their
571  * C (higher half) counterpart.
572  */
573 static int
574 printblock(FILE *fp, struct aggent *agg)
575 {
576 	char buffer[LNBUFF];
577 	long lstart;
578 	struct entry *obj;
579 	u_int tnsamples;
580 	int done, nbytes, sentinel;
581 	void *ptr;
582 
583 	/*
584 	 * We expect the first thing of the block is C code, so simply give
585 	 * up if asm line is found.
586 	 */
587 	lstart = ftell(fp);
588 	sentinel = 0;
589 	for (;;) {
590 		if (fgets(buffer, LNBUFF, fp) == NULL)
591 			return (0);
592 		if (isasminline(buffer) != 0)
593 			break;
594 		sentinel = 1;
595 		nbytes = newfunction(buffer);
596 		if (nbytes != 0) {
597 			if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
598 				return (-1);
599 			return (0);
600 		}
601 	}
602 
603 	/*
604 	 * If the sentinel is not set, it means it did not match any
605 	 * "high half" for this code so simply give up.
606 	 * Operates the second-level aggregation.
607 	 */
608 	tnsamples = 0;
609 	do {
610 		if (sentinel == 0)
611 			return (-1);
612 		if (sscanf(buffer, " %p:", &ptr) != 1)
613 			return (-1);
614 		obj = general_findent((uintptr_t)ptr);
615 		if (obj != NULL)
616 			tnsamples += obj->en_nsamples;
617 	} while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0);
618 
619 	/* Rewind to the start of the block in order to start the printout. */
620 	if (fseek(fp, lstart, SEEK_SET) == -1)
621 		return (-1);
622 
623 	/* Again the high half of the block rappresenting the C part. */
624 	done = 0;
625 	while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) {
626 		if (tnsamples == 0 || done != 0)
627 			printf("\t| %s", buffer);
628 		else {
629 			done = 1;
630 			printf("%.2f%%\t| %s",
631 			    (float)tnsamples * 100 / agg->ag_nsamples, buffer);
632 		}
633 	}
634 
635 	/*
636 	 * Again the low half of the block rappresenting the asm
637 	 * translation part.
638 	 */
639 	for (;;) {
640 		if (fgets(buffer, LNBUFF, fp) == NULL)
641 			return (0);
642 		if (isasminline(buffer) == 0)
643 			break;
644 		nbytes = newfunction(buffer);
645 		if (nbytes != 0) {
646 			if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
647 				return (-1);
648 			return (0);
649 		}
650 	}
651 	if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1)
652 		return (-1);
653 	return (0);
654 }
655 
656 /*
657  * Helper printout functions.
658  */
659 static void
660 usage(const char *progname)
661 {
662 
663 	fprintf(stderr,
664 	    "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n",
665 	    progname);
666 	exit(EXIT_SUCCESS);
667 }
668 
669 int
670 main(int argc, char *argv[])
671 {
672 	char buffer[LNBUFF], fname[FNBUFF], tbfl[] = TMPPATH, tofl[] = TMPPATH;
673 	char tmpf[MAXPATHLEN * 2 + 50];
674 	float limit;
675 	char *bin, *exec, *kfile, *ofile;
676 	struct entry *obj;
677 	FILE *gfp, *bfp;
678 	void *ptr, *hstart, *hend;
679 	uintptr_t tmppc, ostart, oend;
680 	int cget, asmsrc;
681 
682 	exec = argv[0];
683 	ofile = NULL;
684 	bin = NULL;
685 	kfile = NULL;
686 	asmsrc = 0;
687 	limit = 0.5;
688 	while ((cget = getopt(argc, argv, "ahl:k:")) != -1)
689 		switch(cget) {
690 		case 'a':
691 			asmsrc = 1;
692 			break;
693 		case 'k':
694 			kfile = optarg;
695 			break;
696 		case 'l':
697 			limit = (float)atof(optarg);
698 			break;
699 		case 'h':
700 		case '?':
701 		default:
702 			usage(exec);
703 		}
704 	argc -= optind;
705 	argv += optind;
706 	if (argc != 2)
707 		usage(exec);
708 	ofile = argv[0];
709 	bin = argv[1];
710 
711 	if (access(bin, R_OK | F_OK) == -1)
712 		FATAL(exec, "%s: Impossible to locate the binary file\n",
713 		    exec);
714 	if (access(ofile, R_OK | F_OK) == -1)
715 		FATAL(exec, "%s: Impossible to locate the pmcstat file\n",
716 		    exec);
717 	if (kfile != NULL && access(kfile, R_OK | F_OK) == -1)
718 		FATAL(exec, "%s: Impossible to locate the kernel file\n",
719 		    exec);
720 
721 	bzero(tmpf, sizeof(tmpf));
722 	if (mkstemp(tofl) == -1)
723 		FATAL(exec, "%s: Impossible to create the tmp file\n",
724 		    exec);
725 	if (kfile != NULL)
726 		snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s",
727 		    kfile, ofile, tofl);
728 	else
729 		snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile,
730 		    tofl);
731 	if (system(tmpf) != 0)
732 		FATAL(exec, "%s: Impossible to create the tmp file\n",
733 		    exec);
734 
735 	gfp = fopen(tofl, "r");
736 	if (gfp == NULL)
737 		FATAL(exec, "%s: Impossible to open the map file\n",
738 		    exec);
739 
740 	/*
741 	 * Make the collection of raw entries from a pmcstat mapped file.
742 	 * The heuristic here wants strings in the form:
743 	 * "addr funcname startfaddr endfaddr".
744 	 */
745 	while (fgets(buffer, LNBUFF, gfp) != NULL) {
746 		if (isspace(buffer[0]))
747 			continue;
748 		if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname,
749 		    &hstart, &hend) != 4)
750 			FATAL(NULL,
751 			    "%s: Invalid scan of function in the map file\n",
752 			    exec);
753 		ostart = (uintptr_t)hstart;
754 		oend = (uintptr_t)hend;
755 		tmppc = (uintptr_t)ptr;
756 		totalsamples++;
757 		obj = general_findent(tmppc);
758 		if (obj != NULL) {
759 			entry_acqref(obj);
760 			continue;
761 		}
762 		obj = entry_create(fname, tmppc, ostart, oend);
763 		if (obj == NULL)
764 			FATAL(exec,
765 			    "%s: Impossible to create a new object\n", exec);
766 		general_insertent(obj);
767 	}
768 	if (fclose(gfp) == EOF)
769 		FATAL(exec, "%s: Impossible to close the filedesc\n",
770 		    exec);
771 	if (remove(tofl) == -1)
772                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
773                     exec);
774 
775 	/*
776 	 * Remove the loose end objects and feed the first-level aggregation
777 	 * queue.
778 	 */
779 	if (fqueue_insertgen() == -1)
780 		FATAL(exec, "%s: Impossible to generate an analysis\n",
781 		    exec);
782 	fqueue_compact(limit);
783 	if (fqueue_getall(bin, tbfl, asmsrc) == -1)
784 		FATAL(exec, "%s: Impossible to create the tmp file\n",
785 		    exec);
786 
787 	bfp = fopen(tbfl, "r");
788 	if (bfp == NULL)
789 		FATAL(exec, "%s: Impossible to open the binary file\n",
790 		    exec);
791 
792 	if (asmsrc != 0)
793 		asmparse(bfp);
794 	else if (cparse(bfp) == -1)
795 		FATAL(NULL, "%s: Invalid format for the C file\n", exec);
796 	if (fclose(bfp) == EOF)
797                 FATAL(exec, "%s: Impossible to close the filedesc\n",
798                     exec);
799 	if (remove(tbfl) == -1)
800                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
801                     exec);
802 	return (0);
803 }
804