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