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