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
63struct 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
72struct 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
81static struct aggent	*agg_create(const char *name, u_int nsamples,
82			    uintptr_t start, uintptr_t end);
83static void		 agg_destroy(struct aggent *agg) __unused;
84static void		 asmparse(FILE *fp);
85static int		 cparse(FILE *fp);
86static void		 entry_acqref(struct entry *entry);
87static struct entry	*entry_create(const char *name, uintptr_t pc,
88			    uintptr_t start, uintptr_t end);
89static void		 entry_destroy(struct entry *entry) __unused;
90static void		 fqueue_compact(float th);
91static void		 fqueue_deleteall(void);
92static struct aggent	*fqueue_findent_by_name(const char *name);
93static int		 fqueue_getall(const char *bin, char *temp, int asmf);
94static int		 fqueue_insertent(struct entry *entry);
95static int		 fqueue_insertgen(void);
96static void		 general_deleteall(void);
97static struct entry	*general_findent(uintptr_t pc);
98static void		 general_insertent(struct entry *entry);
99static void		 general_printasm(FILE *fp, struct aggent *agg);
100static int		 general_printc(FILE *fp, struct aggent *agg);
101static int		 printblock(FILE *fp, struct aggent *agg);
102static void		 usage(const char *progname) __dead2;
103
104static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
105static 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 */
111static float totalsamples;
112
113/*
114 * Identifies a string cointaining objdump's assembly printout.
115 */
116static inline int
117isasminline(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 */
133static inline int
134newfunction(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 */
151static struct aggent *
152agg_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 */
174static void
175agg_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 */
189static void
190asmparse(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 */
228static int
229cparse(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 */
261static void
262entry_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 */
271static struct entry *
272entry_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 */
294static void
295entry_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 */
307static void
308fqueue_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 */
326static void
327fqueue_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 */
343static int
344fqueue_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 */
406static struct aggent *
407fqueue_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 */
420static int
421fqueue_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 */
454static int
455fqueue_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 */
468static void
469general_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 */
482static struct entry *
483general_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 */
496static void
497general_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 */
510static void
511general_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 */
545static int
546general_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 */
568static int
569printblock(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 */
654static void
655usage(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
664int
665main(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