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	512
45#define	LNBUFF	512
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
62struct 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
71struct 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
80static struct aggent	*agg_create(const char *name, u_int nsamples,
81			    uintptr_t start, uintptr_t end);
82static void		 agg_destroy(struct aggent *agg) __unused;
83static void		 asmparse(FILE *fp);
84static int		 cparse(FILE *fp);
85static void		 entry_acqref(struct entry *entry);
86static struct entry	*entry_create(const char *name, uintptr_t pc,
87			    uintptr_t start, uintptr_t end);
88static void		 entry_destroy(struct entry *entry) __unused;
89static void		 fqueue_compact(float th);
90static void		 fqueue_deleteall(void);
91static struct aggent	*fqueue_findent_by_name(const char *name);
92static int		 fqueue_getall(const char *bin, char *temp, int asmf);
93static int		 fqueue_insertent(struct entry *entry);
94static int		 fqueue_insertgen(void);
95static void		 general_deleteall(void);
96static struct entry	*general_findent(uintptr_t pc);
97static void		 general_insertent(struct entry *entry);
98static void		 general_printasm(FILE *fp, struct aggent *agg);
99static int		 general_printc(FILE *fp, struct aggent *agg);
100static int		 printblock(FILE *fp, struct aggent *agg);
101static void		 usage(const char *progname) __dead2;
102
103static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
104static 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 */
110static float totalsamples;
111
112/*
113 * Identifies a string cointaining objdump's assembly printout.
114 */
115static inline int
116isasminline(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 */
132static inline int
133newfunction(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 */
150static struct aggent *
151agg_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 */
173static void
174agg_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 */
188static void
189asmparse(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 */
227static int
228cparse(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 */
260static void
261entry_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 */
270static struct entry *
271entry_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 */
293static void
294entry_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 */
306static void
307fqueue_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 */
325static void
326fqueue_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 */
342static int
343fqueue_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 */
405static struct aggent *
406fqueue_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 */
419static int
420fqueue_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 */
458static int
459fqueue_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 */
472static void
473general_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 */
486static struct entry *
487general_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 */
500static void
501general_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 */
514static void
515general_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 */
549static int
550general_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 */
572static int
573printblock(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 */
658static void
659usage(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
668int
669main(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