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