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