]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/pmcannotate/pmcannotate.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / pmcannotate / pmcannotate.c
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 }