1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008 Nokia Corporation
5 * All rights reserved.
6 *
7 * This software was developed by Attilio Rao for the IPSO project under
8 * contract to Nokia Corporation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice unmodified, this list of conditions, and the following
15 * disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33 #include <sys/param.h>
34 #include <sys/queue.h>
35
36 #include <ctype.h>
37 #include <paths.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 TMPNAME "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
isasminline(const char * str)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
newfunction(const char * str)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 *
agg_create(const char * name,u_int nsamples,uintptr_t start,uintptr_t end)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
agg_destroy(struct aggent * agg)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
asmparse(FILE * fp)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
cparse(FILE * fp)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
entry_acqref(struct entry * entry)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 *
entry_create(const char * name,uintptr_t pc,uintptr_t start,uintptr_t end)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
entry_destroy(struct entry * entry)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
fqueue_compact(float th)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
fqueue_deleteall(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
fqueue_insertent(struct entry * entry)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 *
fqueue_findent_by_name(const char * name)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
fqueue_getall(const char * bin,char * temp,int asmf)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 if (asmf)
435 snprintf(tmpf, sizeof(tmpf),
436 "objdump --start-address=%p "
437 "--stop-address=%p -d %s >> %s", (void *)start,
438 (void *)end, bin, temp);
439 else
440 snprintf(tmpf, sizeof(tmpf),
441 "objdump --start-address=%p "
442 "--stop-address=%p -S %s >> %s", (void *)start,
443 (void *)end, bin, temp);
444 if (system(tmpf) != 0)
445 return (-1);
446 }
447 return (0);
448 }
449
450 /*
451 * Insert all the raw entries present in the general queue
452 * into the first-level aggregations queue.
453 */
454 static int
fqueue_insertgen(void)455 fqueue_insertgen(void)
456 {
457 struct entry *obj;
458
459 TAILQ_FOREACH(obj, &mainlst, en_iter)
460 if (fqueue_insertent(obj) == -1)
461 return (-1);
462 return (0);
463 }
464
465 /*
466 * Flush the raw entries general queue.
467 */
468 static void
general_deleteall(void)469 general_deleteall(void)
470 {
471 struct entry *obj;
472
473 while (TAILQ_EMPTY(&mainlst) == 0) {
474 obj = TAILQ_FIRST(&mainlst);
475 TAILQ_REMOVE(&mainlst, obj, en_iter);
476 }
477 }
478
479 /*
480 * Lookup a raw entry by the PC.
481 */
482 static struct entry *
general_findent(uintptr_t pc)483 general_findent(uintptr_t pc)
484 {
485 struct entry *obj;
486
487 TAILQ_FOREACH(obj, &mainlst, en_iter)
488 if (obj->en_pc == pc)
489 return (obj);
490 return (NULL);
491 }
492
493 /*
494 * Insert a new raw entry in the general queue.
495 */
496 static void
general_insertent(struct entry * entry)497 general_insertent(struct entry *entry)
498 {
499
500 TAILQ_INSERT_TAIL(&mainlst, entry, en_iter);
501 }
502
503 /*
504 * Printout the body of an "objdump -d" assembly function.
505 * It does simply stops when a new function is encountered,
506 * bringing back the file position in order to not mess up
507 * subsequent analysis.
508 * C lines and others not recognized are simply skipped.
509 */
510 static void
general_printasm(FILE * fp,struct aggent * agg)511 general_printasm(FILE *fp, struct aggent *agg)
512 {
513 char buffer[LNBUFF];
514 struct entry *obj;
515 int nbytes;
516 void *ptr;
517
518 while (fgets(buffer, LNBUFF, fp) != NULL) {
519 if ((nbytes = newfunction(buffer)) != 0) {
520 fseek(fp, nbytes * -1, SEEK_CUR);
521 break;
522 }
523 if (!isasminline(buffer))
524 continue;
525 if (sscanf(buffer, " %p:", &ptr) != 1)
526 continue;
527 obj = general_findent((uintptr_t)ptr);
528 if (obj == NULL)
529 printf("\t| %s", buffer);
530 else
531 printf("%.2f%%\t| %s",
532 (float)obj->en_nsamples * 100 / agg->ag_nsamples,
533 buffer);
534 }
535 }
536
537 /*
538 * Printout the body of an "objdump -S" function.
539 * It does simply stops when a new function is encountered,
540 * bringing back the file position in order to not mess up
541 * subsequent analysis.
542 * It expect from the starting to the end to find, always, valid blocks
543 * (see below for an explanation of the "block" concept).
544 */
545 static int
general_printc(FILE * fp,struct aggent * agg)546 general_printc(FILE *fp, struct aggent *agg)
547 {
548 char buffer[LNBUFF];
549
550 while (fgets(buffer, LNBUFF, fp) != NULL) {
551 fseek(fp, strlen(buffer) * -1, SEEK_CUR);
552 if (newfunction(buffer) != 0)
553 break;
554 if (printblock(fp, agg) == -1)
555 return (-1);
556 }
557 return (0);
558 }
559
560 /*
561 * Printout a single block inside an "objdump -S" function.
562 * The block is composed of a first part in C and subsequent translation
563 * in assembly.
564 * This code also operates a second-level aggregation packing together
565 * samples relative to PCs into a (lower bottom) block with their
566 * C (higher half) counterpart.
567 */
568 static int
printblock(FILE * fp,struct aggent * agg)569 printblock(FILE *fp, struct aggent *agg)
570 {
571 char buffer[LNBUFF];
572 long lstart;
573 struct entry *obj;
574 u_int tnsamples;
575 int done, nbytes, sentinel;
576 void *ptr;
577
578 /*
579 * We expect the first thing of the block is C code, so simply give
580 * up if asm line is found.
581 */
582 lstart = ftell(fp);
583 sentinel = 0;
584 for (;;) {
585 if (fgets(buffer, LNBUFF, fp) == NULL)
586 return (0);
587 if (isasminline(buffer) != 0)
588 break;
589 sentinel = 1;
590 nbytes = newfunction(buffer);
591 if (nbytes != 0) {
592 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
593 return (-1);
594 return (0);
595 }
596 }
597
598 /*
599 * If the sentinel is not set, it means it did not match any
600 * "high half" for this code so simply give up.
601 * Operates the second-level aggregation.
602 */
603 tnsamples = 0;
604 do {
605 if (sentinel == 0)
606 return (-1);
607 if (sscanf(buffer, " %p:", &ptr) != 1)
608 return (-1);
609 obj = general_findent((uintptr_t)ptr);
610 if (obj != NULL)
611 tnsamples += obj->en_nsamples;
612 } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0);
613
614 /* Rewind to the start of the block in order to start the printout. */
615 if (fseek(fp, lstart, SEEK_SET) == -1)
616 return (-1);
617
618 /* Again the high half of the block rappresenting the C part. */
619 done = 0;
620 while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) {
621 if (tnsamples == 0 || done != 0)
622 printf("\t| %s", buffer);
623 else {
624 done = 1;
625 printf("%.2f%%\t| %s",
626 (float)tnsamples * 100 / agg->ag_nsamples, buffer);
627 }
628 }
629
630 /*
631 * Again the low half of the block rappresenting the asm
632 * translation part.
633 */
634 for (;;) {
635 if (fgets(buffer, LNBUFF, fp) == NULL)
636 return (0);
637 if (isasminline(buffer) == 0)
638 break;
639 nbytes = newfunction(buffer);
640 if (nbytes != 0) {
641 if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
642 return (-1);
643 return (0);
644 }
645 }
646 if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1)
647 return (-1);
648 return (0);
649 }
650
651 /*
652 * Helper printout functions.
653 */
654 static void
usage(const char * progname)655 usage(const char *progname)
656 {
657
658 fprintf(stderr,
659 "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n",
660 progname);
661 exit(EXIT_SUCCESS);
662 }
663
664 int
main(int argc,char * argv[])665 main(int argc, char *argv[])
666 {
667 char buffer[LNBUFF], fname[FNBUFF];
668 char *tbfl, *tofl, *tmpdir;
669 char tmpf[MAXPATHLEN * 2 + 50];
670 float limit;
671 char *bin, *exec, *kfile, *ofile;
672 struct entry *obj;
673 FILE *gfp, *bfp;
674 void *ptr, *hstart, *hend;
675 uintptr_t tmppc, ostart, oend;
676 int cget, asmsrc;
677
678 exec = argv[0];
679 ofile = NULL;
680 bin = NULL;
681 kfile = NULL;
682 asmsrc = 0;
683 limit = 0.5;
684 while ((cget = getopt(argc, argv, "ahl:k:")) != -1)
685 switch(cget) {
686 case 'a':
687 asmsrc = 1;
688 break;
689 case 'k':
690 kfile = optarg;
691 break;
692 case 'l':
693 limit = (float)atof(optarg);
694 break;
695 case 'h':
696 case '?':
697 default:
698 usage(exec);
699 }
700 argc -= optind;
701 argv += optind;
702 if (argc != 2)
703 usage(exec);
704 ofile = argv[0];
705 bin = argv[1];
706
707 if (access(bin, R_OK | F_OK) == -1)
708 FATAL(exec, "%s: Impossible to locate the binary file\n",
709 exec);
710 if (access(ofile, R_OK | F_OK) == -1)
711 FATAL(exec, "%s: Impossible to locate the pmcstat file\n",
712 exec);
713 if (kfile != NULL && access(kfile, R_OK | F_OK) == -1)
714 FATAL(exec, "%s: Impossible to locate the kernel file\n",
715 exec);
716
717 bzero(tmpf, sizeof(tmpf));
718 tmpdir = getenv("TMPDIR");
719 if (tmpdir == NULL) {
720 asprintf(&tbfl, "%s/%s", _PATH_TMP, TMPNAME);
721 asprintf(&tofl, "%s/%s", _PATH_TMP, TMPNAME);
722 } else {
723 asprintf(&tbfl, "%s/%s", tmpdir, TMPNAME);
724 asprintf(&tofl, "%s/%s", tmpdir, TMPNAME);
725 }
726 if (tofl == NULL || tbfl == NULL)
727 FATAL(exec, "%s: Cannot create tempfile templates\n",
728 exec);
729 if (mkstemp(tofl) == -1)
730 FATAL(exec, "%s: Impossible to create the tmp file\n",
731 exec);
732 if (kfile != NULL)
733 snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s",
734 kfile, ofile, tofl);
735 else
736 snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile,
737 tofl);
738 if (system(tmpf) != 0)
739 FATAL(exec, "%s: Impossible to create the tmp file\n",
740 exec);
741
742 gfp = fopen(tofl, "r");
743 if (gfp == NULL)
744 FATAL(exec, "%s: Impossible to open the map file\n",
745 exec);
746
747 /*
748 * Make the collection of raw entries from a pmcstat mapped file.
749 * The heuristic here wants strings in the form:
750 * "addr funcname startfaddr endfaddr".
751 */
752 while (fgets(buffer, LNBUFF, gfp) != NULL) {
753 if (isspace(buffer[0]))
754 continue;
755 if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname,
756 &hstart, &hend) != 4)
757 FATAL(NULL,
758 "%s: Invalid scan of function in the map file\n",
759 exec);
760 ostart = (uintptr_t)hstart;
761 oend = (uintptr_t)hend;
762 tmppc = (uintptr_t)ptr;
763 totalsamples++;
764 obj = general_findent(tmppc);
765 if (obj != NULL) {
766 entry_acqref(obj);
767 continue;
768 }
769 obj = entry_create(fname, tmppc, ostart, oend);
770 if (obj == NULL)
771 FATAL(exec,
772 "%s: Impossible to create a new object\n", exec);
773 general_insertent(obj);
774 }
775 if (fclose(gfp) == EOF)
776 FATAL(exec, "%s: Impossible to close the filedesc\n",
777 exec);
778 if (remove(tofl) == -1)
779 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
780 exec);
781
782 /*
783 * Remove the loose end objects and feed the first-level aggregation
784 * queue.
785 */
786 if (fqueue_insertgen() == -1)
787 FATAL(exec, "%s: Impossible to generate an analysis\n",
788 exec);
789 fqueue_compact(limit);
790 if (fqueue_getall(bin, tbfl, asmsrc) == -1)
791 FATAL(exec, "%s: Impossible to create the tmp file\n",
792 exec);
793
794 bfp = fopen(tbfl, "r");
795 if (bfp == NULL)
796 FATAL(exec, "%s: Impossible to open the binary file\n",
797 exec);
798
799 if (asmsrc != 0)
800 asmparse(bfp);
801 else if (cparse(bfp) == -1)
802 FATAL(NULL, "%s: Invalid format for the C file\n", exec);
803 if (fclose(bfp) == EOF)
804 FATAL(exec, "%s: Impossible to close the filedesc\n",
805 exec);
806 if (remove(tbfl) == -1)
807 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
808 exec);
809 return (0);
810 }
811