]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pmcannotate/pmcannotate.c
Update llvm, clang and lldb to trunk r257626, and update build glue.
[FreeBSD/FreeBSD.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 /* NB: Make sure FNBUFF is as large as LNBUFF, otherwise it could overflow */
45 #define FNBUFF  512
46 #define LNBUFF  512
47
48 #define TMPPATH "/tmp/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
63 struct 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
72 struct 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
81 static struct aggent    *agg_create(const char *name, u_int nsamples,
82                             uintptr_t start, uintptr_t end);
83 static void              agg_destroy(struct aggent *agg) __unused;
84 static void              asmparse(FILE *fp);
85 static int               cparse(FILE *fp);
86 static void              entry_acqref(struct entry *entry);
87 static struct entry     *entry_create(const char *name, uintptr_t pc,
88                             uintptr_t start, uintptr_t end);
89 static void              entry_destroy(struct entry *entry) __unused;
90 static void              fqueue_compact(float th);
91 static void              fqueue_deleteall(void);
92 static struct aggent    *fqueue_findent_by_name(const char *name);
93 static int               fqueue_getall(const char *bin, char *temp, int asmf);
94 static int               fqueue_insertent(struct entry *entry);
95 static int               fqueue_insertgen(void);
96 static void              general_deleteall(void);
97 static struct entry     *general_findent(uintptr_t pc);
98 static void              general_insertent(struct entry *entry);
99 static void              general_printasm(FILE *fp, struct aggent *agg);
100 static int               general_printc(FILE *fp, struct aggent *agg);
101 static int               printblock(FILE *fp, struct aggent *agg);
102 static void              usage(const char *progname) __dead2;
103
104 static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
105 static 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  */
111 static float totalsamples;
112
113 /*
114  * Identifies a string cointaining objdump's assembly printout.
115  */
116 static inline int
117 isasminline(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  */
133 static inline int
134 newfunction(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  */
151 static struct aggent *
152 agg_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  */
174 static void
175 agg_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  */
189 static void
190 asmparse(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  */
228 static int
229 cparse(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  */
261 static void
262 entry_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  */
271 static struct entry *
272 entry_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  */
294 static void
295 entry_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  */
307 static void
308 fqueue_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  */
326 static void
327 fqueue_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  */
343 static int
344 fqueue_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  */
406 static struct aggent *
407 fqueue_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  */
420 static int
421 fqueue_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                 /*
435                  * Fix-up the end address in order to show it in the objdump's
436                  * trace.
437                  */
438                 end++;
439                 if (asmf)
440                         snprintf(tmpf, sizeof(tmpf),
441                             "objdump --start-address=%p "
442                             "--stop-address=%p -d %s >> %s", (void *)start,
443                             (void *)end, bin, temp);
444                 else
445                         snprintf(tmpf, sizeof(tmpf),
446                             "objdump --start-address=%p "
447                             "--stop-address=%p -S %s >> %s", (void *)start,
448                             (void *)end, bin, temp);
449                 if (system(tmpf) != 0)
450                         return (-1);
451         }
452         return (0);
453 }
454
455 /*
456  * Insert all the raw entries present in the general queue
457  * into the first-level aggregations queue.
458  */
459 static int
460 fqueue_insertgen(void)
461 {
462         struct entry *obj;
463
464         TAILQ_FOREACH(obj, &mainlst, en_iter)
465                 if (fqueue_insertent(obj) == -1)
466                         return (-1);
467         return (0);
468 }
469
470 /*
471  * Flush the raw entries general queue.
472  */
473 static void
474 general_deleteall(void)
475 {
476         struct entry *obj;
477
478         while (TAILQ_EMPTY(&mainlst) == 0) {
479                 obj = TAILQ_FIRST(&mainlst);
480                 TAILQ_REMOVE(&mainlst, obj, en_iter);
481         }
482 }
483
484 /*
485  * Lookup a raw entry by the PC.
486  */
487 static struct entry *
488 general_findent(uintptr_t pc)
489 {
490         struct entry *obj;
491
492         TAILQ_FOREACH(obj, &mainlst, en_iter)
493                 if (obj->en_pc == pc)
494                         return (obj);
495         return (NULL);
496 }
497
498 /*
499  * Insert a new raw entry in the general queue.
500  */
501 static void
502 general_insertent(struct entry *entry)
503 {
504
505         TAILQ_INSERT_TAIL(&mainlst, entry, en_iter);
506 }
507
508 /*
509  * Printout the body of an "objdump -d" assembly function.
510  * It does simply stops when a new function is encountered,
511  * bringing back the file position in order to not mess up
512  * subsequent analysis.
513  * C lines and others not recognized are simply skipped.
514  */
515 static void
516 general_printasm(FILE *fp, struct aggent *agg)
517 {
518         char buffer[LNBUFF];
519         struct entry *obj;
520         int nbytes;
521         void *ptr;
522
523         while (fgets(buffer, LNBUFF, fp) != NULL) {
524                 if ((nbytes = newfunction(buffer)) != 0) {
525                         fseek(fp, nbytes * -1, SEEK_CUR);
526                         break;
527                 }
528                 if (!isasminline(buffer))
529                         continue;
530                 if (sscanf(buffer, " %p:", &ptr) != 1)
531                         continue;
532                 obj = general_findent((uintptr_t)ptr);
533                 if (obj == NULL)
534                         printf("\t| %s", buffer);
535                 else
536                         printf("%.2f%%\t| %s",
537                             (float)obj->en_nsamples * 100 / agg->ag_nsamples,
538                             buffer);
539         }
540 }
541
542 /*
543  * Printout the body of an "objdump -S" function.
544  * It does simply stops when a new function is encountered,
545  * bringing back the file position in order to not mess up
546  * subsequent analysis.
547  * It expect from the starting to the end to find, always, valid blocks
548  * (see below for an explanation of the "block" concept).
549  */
550 static int
551 general_printc(FILE *fp, struct aggent *agg)
552 {
553         char buffer[LNBUFF];
554
555         while (fgets(buffer, LNBUFF, fp) != NULL) {
556                 fseek(fp, strlen(buffer) * -1, SEEK_CUR);
557                 if (newfunction(buffer) != 0)
558                         break;
559                 if (printblock(fp, agg) == -1)
560                         return (-1);
561         }
562         return (0);
563 }
564
565 /*
566  * Printout a single block inside an "objdump -S" function.
567  * The block is composed of a first part in C and subsequent translation
568  * in assembly.
569  * This code also operates a second-level aggregation packing together
570  * samples relative to PCs into a (lower bottom) block with their
571  * C (higher half) counterpart.
572  */
573 static int
574 printblock(FILE *fp, struct aggent *agg)
575 {
576         char buffer[LNBUFF];
577         long lstart;
578         struct entry *obj;
579         u_int tnsamples;
580         int done, nbytes, sentinel;
581         void *ptr;
582
583         /*
584          * We expect the first thing of the block is C code, so simply give
585          * up if asm line is found.
586          */
587         lstart = ftell(fp);
588         sentinel = 0;
589         for (;;) {
590                 if (fgets(buffer, LNBUFF, fp) == NULL)
591                         return (0);
592                 if (isasminline(buffer) != 0)
593                         break;
594                 sentinel = 1;
595                 nbytes = newfunction(buffer);
596                 if (nbytes != 0) {
597                         if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
598                                 return (-1);
599                         return (0);
600                 }
601         }
602
603         /*
604          * If the sentinel is not set, it means it did not match any
605          * "high half" for this code so simply give up.
606          * Operates the second-level aggregation.
607          */
608         tnsamples = 0;
609         do {
610                 if (sentinel == 0)
611                         return (-1);
612                 if (sscanf(buffer, " %p:", &ptr) != 1)
613                         return (-1);
614                 obj = general_findent((uintptr_t)ptr);
615                 if (obj != NULL)
616                         tnsamples += obj->en_nsamples;
617         } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0);
618
619         /* Rewind to the start of the block in order to start the printout. */
620         if (fseek(fp, lstart, SEEK_SET) == -1)
621                 return (-1);
622
623         /* Again the high half of the block rappresenting the C part. */
624         done = 0;
625         while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) {
626                 if (tnsamples == 0 || done != 0)
627                         printf("\t| %s", buffer);
628                 else {
629                         done = 1;
630                         printf("%.2f%%\t| %s",
631                             (float)tnsamples * 100 / agg->ag_nsamples, buffer);
632                 }
633         }
634
635         /*
636          * Again the low half of the block rappresenting the asm
637          * translation part.
638          */
639         for (;;) {
640                 if (fgets(buffer, LNBUFF, fp) == NULL)
641                         return (0);
642                 if (isasminline(buffer) == 0)
643                         break;
644                 nbytes = newfunction(buffer);
645                 if (nbytes != 0) {
646                         if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
647                                 return (-1);
648                         return (0);
649                 }
650         }
651         if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1)
652                 return (-1);
653         return (0);
654 }
655
656 /*
657  * Helper printout functions.
658  */
659 static void
660 usage(const char *progname)
661 {
662
663         fprintf(stderr,
664             "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n",
665             progname);
666         exit(EXIT_SUCCESS);
667 }
668
669 int
670 main(int argc, char *argv[])
671 {
672         char buffer[LNBUFF], fname[FNBUFF], tbfl[] = TMPPATH, tofl[] = TMPPATH;
673         char tmpf[MAXPATHLEN * 2 + 50];
674         float limit;
675         char *bin, *exec, *kfile, *ofile;
676         struct entry *obj;
677         FILE *gfp, *bfp;
678         void *ptr, *hstart, *hend;
679         uintptr_t tmppc, ostart, oend;
680         int cget, asmsrc;
681
682         exec = argv[0];
683         ofile = NULL;
684         bin = NULL;
685         kfile = NULL;
686         asmsrc = 0;
687         limit = 0.5;
688         while ((cget = getopt(argc, argv, "ahl:k:")) != -1)
689                 switch(cget) {
690                 case 'a':
691                         asmsrc = 1;
692                         break;
693                 case 'k':
694                         kfile = optarg;
695                         break;
696                 case 'l':
697                         limit = (float)atof(optarg);
698                         break;
699                 case 'h':
700                 case '?':
701                 default:
702                         usage(exec);
703                 }
704         argc -= optind;
705         argv += optind;
706         if (argc != 2)
707                 usage(exec);
708         ofile = argv[0];
709         bin = argv[1];
710
711         if (access(bin, R_OK | F_OK) == -1)
712                 FATAL(exec, "%s: Impossible to locate the binary file\n",
713                     exec);
714         if (access(ofile, R_OK | F_OK) == -1)
715                 FATAL(exec, "%s: Impossible to locate the pmcstat file\n",
716                     exec);
717         if (kfile != NULL && access(kfile, R_OK | F_OK) == -1)
718                 FATAL(exec, "%s: Impossible to locate the kernel file\n",
719                     exec);
720
721         bzero(tmpf, sizeof(tmpf));
722         if (mkstemp(tofl) == -1)
723                 FATAL(exec, "%s: Impossible to create the tmp file\n",
724                     exec);
725         if (kfile != NULL)
726                 snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s",
727                     kfile, ofile, tofl);
728         else
729                 snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile,
730                     tofl);
731         if (system(tmpf) != 0)
732                 FATAL(exec, "%s: Impossible to create the tmp file\n",
733                     exec);
734
735         gfp = fopen(tofl, "r");
736         if (gfp == NULL)
737                 FATAL(exec, "%s: Impossible to open the map file\n",
738                     exec);
739
740         /*
741          * Make the collection of raw entries from a pmcstat mapped file.
742          * The heuristic here wants strings in the form:
743          * "addr funcname startfaddr endfaddr".
744          */
745         while (fgets(buffer, LNBUFF, gfp) != NULL) {
746                 if (isspace(buffer[0]))
747                         continue;
748                 if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname,
749                     &hstart, &hend) != 4)
750                         FATAL(NULL,
751                             "%s: Invalid scan of function in the map file\n",
752                             exec);
753                 ostart = (uintptr_t)hstart;
754                 oend = (uintptr_t)hend;
755                 tmppc = (uintptr_t)ptr;
756                 totalsamples++;
757                 obj = general_findent(tmppc);
758                 if (obj != NULL) {
759                         entry_acqref(obj);
760                         continue;
761                 }
762                 obj = entry_create(fname, tmppc, ostart, oend);
763                 if (obj == NULL)
764                         FATAL(exec,
765                             "%s: Impossible to create a new object\n", exec);
766                 general_insertent(obj);
767         }
768         if (fclose(gfp) == EOF)
769                 FATAL(exec, "%s: Impossible to close the filedesc\n",
770                     exec);
771         if (remove(tofl) == -1)
772                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
773                     exec);
774
775         /*
776          * Remove the loose end objects and feed the first-level aggregation
777          * queue.
778          */
779         if (fqueue_insertgen() == -1)
780                 FATAL(exec, "%s: Impossible to generate an analysis\n",
781                     exec);
782         fqueue_compact(limit);
783         if (fqueue_getall(bin, tbfl, asmsrc) == -1)
784                 FATAL(exec, "%s: Impossible to create the tmp file\n",
785                     exec);
786
787         bfp = fopen(tbfl, "r");
788         if (bfp == NULL)
789                 FATAL(exec, "%s: Impossible to open the binary file\n",
790                     exec);
791
792         if (asmsrc != 0)
793                 asmparse(bfp);
794         else if (cparse(bfp) == -1)
795                 FATAL(NULL, "%s: Invalid format for the C file\n", exec);
796         if (fclose(bfp) == EOF)
797                 FATAL(exec, "%s: Impossible to close the filedesc\n",
798                     exec);
799         if (remove(tbfl) == -1)
800                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
801                     exec);
802         return (0);
803 }