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: stable/10/usr.sbin/pmcannotate/pmcannotate.c 266889 2014-05-30 15:00:50Z gnn $");
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 512
45 #define LNBUFF 512
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
isasminline(const char * str)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
newfunction(const char * str)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 *
agg_create(const char * name,u_int nsamples,uintptr_t start,uintptr_t end)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
agg_destroy(struct aggent * agg)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
asmparse(FILE * fp)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
cparse(FILE * fp)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
entry_acqref(struct entry * entry)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 *
entry_create(const char * name,uintptr_t pc,uintptr_t start,uintptr_t end)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
entry_destroy(struct entry * entry)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
fqueue_compact(float th)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
fqueue_deleteall(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
fqueue_insertent(struct entry * entry)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 *
fqueue_findent_by_name(const char * name)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
fqueue_getall(const char * bin,char * temp,int asmf)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
fqueue_insertgen(void)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
general_deleteall(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 *
general_findent(uintptr_t pc)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
general_insertent(struct entry * entry)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
general_printasm(FILE * fp,struct aggent * agg)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
general_printc(FILE * fp,struct aggent * agg)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
printblock(FILE * fp,struct aggent * agg)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
usage(const char * progname)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
main(int argc,char * argv[])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 }
803