Deleted Added
full compact
pmcannotate.c (185347) pmcannotate.c (201227)
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>
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: head/usr.sbin/pmcannotate/pmcannotate.c 185347 2008-11-26 21:44:57Z attilio $");
32__FBSDID("$FreeBSD: head/usr.sbin/pmcannotate/pmcannotate.c 201227 2009-12-29 22:53:27Z ed $");
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
62struct 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
71struct 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
80static struct aggent *agg_create(const char *name, u_int nsamples,
81 uintptr_t start, uintptr_t end);
82static void agg_destroy(struct aggent *agg) __unused;
83static void asmparse(FILE *fp);
84static int cparse(FILE *fp);
85static void entry_acqref(struct entry *entry);
86static struct entry *entry_create(const char *name, uintptr_t pc,
87 uintptr_t start, uintptr_t end);
88static void entry_destroy(struct entry *entry) __unused;
89static void fqueue_compact(float th);
90static void fqueue_deleteall(void);
91static struct aggent *fqueue_findent_by_name(const char *name);
92static int fqueue_getall(const char *bin, char *temp, int asmf);
93static int fqueue_insertent(struct entry *entry);
94static int fqueue_insertgen(void);
95static void general_deleteall(void);
96static struct entry *general_findent(uintptr_t pc);
97static void general_insertent(struct entry *entry);
98static void general_printasm(FILE *fp, struct aggent *agg);
99static int general_printc(FILE *fp, struct aggent *agg);
100static int printblock(FILE *fp, struct aggent *agg);
101static void usage(const char *progname) __dead2;
102
103static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
104static 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 */
110static float totalsamples;
111
112/*
113 * Identifies a string cointaining objdump's assembly printout.
114 */
115static inline int
116isasminline(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 */
134static inline int
135newfunction(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 */
152static struct aggent *
153agg_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 */
175static void
176agg_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 */
190static void
191asmparse(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 */
229static int
230cparse(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 */
262static void
263entry_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 */
272static struct entry *
273entry_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 */
295static void
296entry_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 */
308static void
309fqueue_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 */
327static void
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
62struct 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
71struct 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
80static struct aggent *agg_create(const char *name, u_int nsamples,
81 uintptr_t start, uintptr_t end);
82static void agg_destroy(struct aggent *agg) __unused;
83static void asmparse(FILE *fp);
84static int cparse(FILE *fp);
85static void entry_acqref(struct entry *entry);
86static struct entry *entry_create(const char *name, uintptr_t pc,
87 uintptr_t start, uintptr_t end);
88static void entry_destroy(struct entry *entry) __unused;
89static void fqueue_compact(float th);
90static void fqueue_deleteall(void);
91static struct aggent *fqueue_findent_by_name(const char *name);
92static int fqueue_getall(const char *bin, char *temp, int asmf);
93static int fqueue_insertent(struct entry *entry);
94static int fqueue_insertgen(void);
95static void general_deleteall(void);
96static struct entry *general_findent(uintptr_t pc);
97static void general_insertent(struct entry *entry);
98static void general_printasm(FILE *fp, struct aggent *agg);
99static int general_printc(FILE *fp, struct aggent *agg);
100static int printblock(FILE *fp, struct aggent *agg);
101static void usage(const char *progname) __dead2;
102
103static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
104static 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 */
110static float totalsamples;
111
112/*
113 * Identifies a string cointaining objdump's assembly printout.
114 */
115static inline int
116isasminline(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 */
134static inline int
135newfunction(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 */
152static struct aggent *
153agg_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 */
175static void
176agg_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 */
190static void
191asmparse(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 */
229static int
230cparse(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 */
262static void
263entry_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 */
272static struct entry *
273entry_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 */
295static void
296entry_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 */
308static void
309fqueue_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 */
327static void
328fqueue_deleteall()
328fqueue_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 */
344static int
345fqueue_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 firt-level aggregation object alredy exist,
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 */
407static struct aggent *
408fqueue_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 */
421static int
422fqueue_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 */
460static int
461fqueue_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 */
474static 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 */
344static int
345fqueue_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 firt-level aggregation object alredy exist,
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 */
407static struct aggent *
408fqueue_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 */
421static int
422fqueue_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 */
460static int
461fqueue_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 */
474static void
475general_deleteall()
475general_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 */
488static struct entry *
489general_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 */
502static void
503general_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 */
516static void
517general_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 */
551static int
552general_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 */
574static int
575printblock(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 */
660static void
661usage(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
670int
671main(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}
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 */
488static struct entry *
489general_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 */
502static void
503general_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 */
516static void
517general_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 */
551static int
552general_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 */
574static int
575printblock(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 */
660static void
661usage(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
670int
671main(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}