1185347Sattilio/*-
2185347Sattilio * Copyright (c) 2008 Nokia Corporation
3185347Sattilio * All rights reserved.
4185347Sattilio *
5185347Sattilio * This software was developed by Attilio Rao for the IPSO project under
6185347Sattilio * contract to Nokia Corporation.
7185347Sattilio *
8185347Sattilio * Redistribution and use in source and binary forms, with or without
9185347Sattilio * modification, are permitted provided that the following conditions
10185347Sattilio * are met:
11185347Sattilio * 1. Redistributions of source code must retain the above copyright
12185347Sattilio *    notice unmodified, this list of conditions, and the following
13185347Sattilio *    disclaimer.
14185347Sattilio * 2. Redistributions in binary form must reproduce the above copyright
15185347Sattilio *    notice, this list of conditions and the following disclaimer in the
16185347Sattilio *    documentation and/or other materials provided with the distribution.
17185347Sattilio *
18185347Sattilio * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19185347Sattilio * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20185347Sattilio * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21185347Sattilio * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22185347Sattilio * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23185347Sattilio * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24185347Sattilio * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25185347Sattilio * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26185347Sattilio * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27185347Sattilio * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28185347Sattilio *
29185347Sattilio */
30185347Sattilio
31185347Sattilio#include <sys/cdefs.h>
32185347Sattilio__FBSDID("$FreeBSD$");
33185347Sattilio
34185347Sattilio#include <sys/param.h>
35185347Sattilio#include <sys/queue.h>
36185347Sattilio
37185347Sattilio#include <ctype.h>
38185347Sattilio#include <stdio.h>
39185347Sattilio#include <stdlib.h>
40185347Sattilio#include <string.h>
41185347Sattilio
42185347Sattilio#include <unistd.h>
43185347Sattilio
44266889Sgnn#define	FNBUFF	512
45266889Sgnn#define	LNBUFF	512
46185347Sattilio
47185347Sattilio#define	TMPPATH	"/tmp/pmcannotate.XXXXXX"
48185347Sattilio
49185347Sattilio#define	FATAL(ptr, x ...) do {						\
50185347Sattilio	fqueue_deleteall();						\
51185347Sattilio	general_deleteall();						\
52185347Sattilio	if ((ptr) != NULL)						\
53185347Sattilio		perror(ptr);						\
54185347Sattilio	fprintf(stderr, ##x);						\
55185347Sattilio	remove(tbfl);							\
56185347Sattilio	remove(tofl);							\
57185347Sattilio	exit(EXIT_FAILURE);						\
58185347Sattilio} while (0)
59185347Sattilio
60185347Sattilio#define	PERCSAMP(x)	((x) * 100 / totalsamples)
61185347Sattilio
62185347Sattiliostruct entry {
63185347Sattilio        TAILQ_ENTRY(entry)	en_iter;
64185347Sattilio        char		*en_name;
65185347Sattilio	uintptr_t	en_pc;
66185347Sattilio	uintptr_t	en_ostart;
67185347Sattilio	uintptr_t	en_oend;
68185347Sattilio	u_int		en_nsamples;
69185347Sattilio};
70185347Sattilio
71185347Sattiliostruct aggent {
72185347Sattilio	TAILQ_ENTRY(aggent)	ag_fiter;
73185347Sattilio	long		ag_offset;
74185347Sattilio	uintptr_t	ag_ostart;
75185347Sattilio	uintptr_t	ag_oend;
76185347Sattilio	char		*ag_name;
77185347Sattilio	u_int		ag_nsamples;
78185347Sattilio};
79185347Sattilio
80185347Sattiliostatic struct aggent	*agg_create(const char *name, u_int nsamples,
81185347Sattilio			    uintptr_t start, uintptr_t end);
82185347Sattiliostatic void		 agg_destroy(struct aggent *agg) __unused;
83185347Sattiliostatic void		 asmparse(FILE *fp);
84185347Sattiliostatic int		 cparse(FILE *fp);
85185347Sattiliostatic void		 entry_acqref(struct entry *entry);
86185347Sattiliostatic struct entry	*entry_create(const char *name, uintptr_t pc,
87185347Sattilio			    uintptr_t start, uintptr_t end);
88185347Sattiliostatic void		 entry_destroy(struct entry *entry) __unused;
89185347Sattiliostatic void		 fqueue_compact(float th);
90185347Sattiliostatic void		 fqueue_deleteall(void);
91185347Sattiliostatic struct aggent	*fqueue_findent_by_name(const char *name);
92185347Sattiliostatic int		 fqueue_getall(const char *bin, char *temp, int asmf);
93185347Sattiliostatic int		 fqueue_insertent(struct entry *entry);
94185347Sattiliostatic int		 fqueue_insertgen(void);
95185347Sattiliostatic void		 general_deleteall(void);
96185347Sattiliostatic struct entry	*general_findent(uintptr_t pc);
97185347Sattiliostatic void		 general_insertent(struct entry *entry);
98185347Sattiliostatic void		 general_printasm(FILE *fp, struct aggent *agg);
99185347Sattiliostatic int		 general_printc(FILE *fp, struct aggent *agg);
100185347Sattiliostatic int		 printblock(FILE *fp, struct aggent *agg);
101185347Sattiliostatic void		 usage(const char *progname) __dead2;
102185347Sattilio
103185347Sattiliostatic TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
104185347Sattiliostatic TAILQ_HEAD(, aggent) fqueue = TAILQ_HEAD_INITIALIZER(fqueue);
105185347Sattilio
106185347Sattilio/*
107185347Sattilio * Use a float value in order to automatically promote operations
108185347Sattilio * to return a float value rather than use casts.
109185347Sattilio */
110185347Sattiliostatic float totalsamples;
111185347Sattilio
112185347Sattilio/*
113185347Sattilio * Identifies a string cointaining objdump's assembly printout.
114185347Sattilio */
115185347Sattiliostatic inline int
116185347Sattilioisasminline(const char *str)
117185347Sattilio{
118185347Sattilio	void *ptr;
119185347Sattilio	int nbytes;
120185347Sattilio
121185347Sattilio	if (sscanf(str, " %p%n", &ptr, &nbytes) != 1)
122185347Sattilio		return (0);
123185347Sattilio	if (str[nbytes] != ':' || isspace(str[nbytes + 1]) == 0)
124185347Sattilio		return (0);
125185347Sattilio	return (1);
126185347Sattilio}
127185347Sattilio
128185347Sattilio/*
129185347Sattilio * Identifies a string containing objdump's assembly printout
130185347Sattilio * for a new function.
131185347Sattilio */
132185347Sattiliostatic inline int
133185347Sattilionewfunction(const char *str)
134185347Sattilio{
135185347Sattilio	char fname[FNBUFF];
136185347Sattilio	void *ptr;
137185347Sattilio	int nbytes;
138185347Sattilio
139185347Sattilio	if (isspace(str[0]))
140185347Sattilio		return (0);
141185347Sattilio	if (sscanf(str, "%p <%[^>:]>:%n", &ptr, fname, &nbytes) != 2)
142185347Sattilio		return (0);
143185347Sattilio	return (nbytes);
144185347Sattilio}
145185347Sattilio
146185347Sattilio/*
147185347Sattilio * Create a new first-level aggregation object for a specified
148185347Sattilio * function.
149185347Sattilio */
150185347Sattiliostatic struct aggent *
151185347Sattilioagg_create(const char *name, u_int nsamples, uintptr_t start, uintptr_t end)
152185347Sattilio{
153185347Sattilio	struct aggent *agg;
154185347Sattilio
155185347Sattilio	agg = calloc(1, sizeof(struct aggent));
156185347Sattilio	if (agg == NULL)
157185347Sattilio		return (NULL);
158185347Sattilio	agg->ag_name = strdup(name);
159185347Sattilio	if (agg->ag_name == NULL) {
160185347Sattilio		free(agg);
161185347Sattilio		return (NULL);
162185347Sattilio	}
163185347Sattilio	agg->ag_nsamples = nsamples;
164185347Sattilio	agg->ag_ostart = start;
165185347Sattilio	agg->ag_oend = end;
166185347Sattilio	return (agg);
167185347Sattilio}
168185347Sattilio
169185347Sattilio/*
170185347Sattilio * Destroy a first-level aggregation object for a specified
171185347Sattilio * function.
172185347Sattilio */
173185347Sattiliostatic void
174185347Sattilioagg_destroy(struct aggent *agg)
175185347Sattilio{
176185347Sattilio
177185347Sattilio	free(agg->ag_name);
178185347Sattilio	free(agg);
179185347Sattilio}
180185347Sattilio
181185347Sattilio/*
182185347Sattilio * Analyze the "objdump -d" output, locate functions and start
183185347Sattilio * printing out the assembly functions content.
184185347Sattilio * We do not use newfunction() because we actually need the
185185347Sattilio * function name in available form, but the heurstic used is
186185347Sattilio * the same.
187185347Sattilio */
188185347Sattiliostatic void
189185347Sattilioasmparse(FILE *fp)
190185347Sattilio{
191185347Sattilio	char buffer[LNBUFF], fname[FNBUFF];
192185347Sattilio	struct aggent *agg;
193185347Sattilio	void *ptr;
194185347Sattilio
195185347Sattilio	while (fgets(buffer, LNBUFF, fp) != NULL) {
196185347Sattilio		if (isspace(buffer[0]))
197185347Sattilio			continue;
198185347Sattilio		if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2)
199185347Sattilio			continue;
200185347Sattilio		agg = fqueue_findent_by_name(fname);
201185347Sattilio		if (agg == NULL)
202185347Sattilio			continue;
203185347Sattilio		agg->ag_offset = ftell(fp);
204185347Sattilio	}
205185347Sattilio
206185347Sattilio	TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
207185347Sattilio		if (fseek(fp, agg->ag_offset, SEEK_SET) == -1)
208185347Sattilio			return;
209185347Sattilio		printf("Profile trace for function: %s() [%.2f%%]\n",
210185347Sattilio		    agg->ag_name, PERCSAMP(agg->ag_nsamples));
211185347Sattilio		general_printasm(fp, agg);
212185347Sattilio		printf("\n");
213185347Sattilio	}
214185347Sattilio}
215185347Sattilio
216185347Sattilio/*
217185347Sattilio * Analyze the "objdump -S" output, locate functions and start
218185347Sattilio * printing out the C functions content.
219185347Sattilio * We do not use newfunction() because we actually need the
220185347Sattilio * function name in available form, but the heurstic used is
221185347Sattilio * the same.
222185347Sattilio * In order to maintain the printout sorted, on the first pass it
223185347Sattilio * simply stores the file offsets in order to fastly moved later
224185347Sattilio * (when the file is hot-cached also) when the real printout will
225185347Sattilio * happen.
226185347Sattilio */
227185347Sattiliostatic int
228185347Sattiliocparse(FILE *fp)
229185347Sattilio{
230185347Sattilio	char buffer[LNBUFF], fname[FNBUFF];
231185347Sattilio	struct aggent *agg;
232185347Sattilio	void *ptr;
233185347Sattilio
234185347Sattilio	while (fgets(buffer, LNBUFF, fp) != NULL) {
235185347Sattilio		if (isspace(buffer[0]))
236185347Sattilio			continue;
237185347Sattilio		if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2)
238185347Sattilio			continue;
239185347Sattilio		agg = fqueue_findent_by_name(fname);
240185347Sattilio		if (agg == NULL)
241185347Sattilio			continue;
242185347Sattilio		agg->ag_offset = ftell(fp);
243185347Sattilio	}
244185347Sattilio
245185347Sattilio	TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
246185347Sattilio		if (fseek(fp, agg->ag_offset, SEEK_SET) == -1)
247185347Sattilio			return (-1);
248185347Sattilio		printf("Profile trace for function: %s() [%.2f%%]\n",
249185347Sattilio		    agg->ag_name, PERCSAMP(agg->ag_nsamples));
250185347Sattilio		if (general_printc(fp, agg) == -1)
251185347Sattilio			return (-1);
252185347Sattilio		printf("\n");
253185347Sattilio	}
254185347Sattilio	return (0);
255185347Sattilio}
256185347Sattilio
257185347Sattilio/*
258185347Sattilio * Bump the number of samples for any raw entry.
259185347Sattilio */
260185347Sattiliostatic void
261185347Sattilioentry_acqref(struct entry *entry)
262185347Sattilio{
263185347Sattilio
264185347Sattilio	entry->en_nsamples++;
265185347Sattilio}
266185347Sattilio
267185347Sattilio/*
268185347Sattilio * Create a new raw entry object for a specified function.
269185347Sattilio */
270185347Sattiliostatic struct entry *
271185347Sattilioentry_create(const char *name, uintptr_t pc, uintptr_t start, uintptr_t end)
272185347Sattilio{
273185347Sattilio	struct entry *obj;
274185347Sattilio
275185347Sattilio	obj = calloc(1, sizeof(struct entry));
276185347Sattilio	if (obj == NULL)
277185347Sattilio		return (NULL);
278185347Sattilio	obj->en_name = strdup(name);
279185347Sattilio	if (obj->en_name == NULL) {
280185347Sattilio		free(obj);
281185347Sattilio		return (NULL);
282185347Sattilio	}
283185347Sattilio	obj->en_pc = pc;
284185347Sattilio	obj->en_ostart = start;
285185347Sattilio	obj->en_oend = end;
286185347Sattilio	obj->en_nsamples = 1;
287185347Sattilio	return (obj);
288185347Sattilio}
289185347Sattilio
290185347Sattilio/*
291185347Sattilio * Destroy a raw entry object for a specified function.
292185347Sattilio */
293185347Sattiliostatic void
294185347Sattilioentry_destroy(struct entry *entry)
295185347Sattilio{
296185347Sattilio
297185347Sattilio	free(entry->en_name);
298185347Sattilio	free(entry);
299185347Sattilio}
300185347Sattilio
301185347Sattilio/*
302185347Sattilio * Specify a lower bound in percentage and drop from the
303185347Sattilio * first-level aggregation queue all the objects with a
304185347Sattilio * smaller impact.
305185347Sattilio */
306185347Sattiliostatic void
307185347Sattiliofqueue_compact(float th)
308185347Sattilio{
309185347Sattilio	u_int thi;
310185347Sattilio	struct aggent *agg, *tmpagg;
311185347Sattilio
312185347Sattilio	if (totalsamples == 0)
313185347Sattilio		return;
314185347Sattilio
315185347Sattilio	/* Revert the percentage calculation. */
316185347Sattilio	thi = th * totalsamples / 100;
317185347Sattilio	TAILQ_FOREACH_SAFE(agg, &fqueue, ag_fiter, tmpagg)
318185347Sattilio		if (agg->ag_nsamples < thi)
319185347Sattilio			TAILQ_REMOVE(&fqueue, agg, ag_fiter);
320185347Sattilio}
321185347Sattilio
322185347Sattilio/*
323185347Sattilio * Flush the first-level aggregates queue.
324185347Sattilio */
325185347Sattiliostatic void
326201227Sedfqueue_deleteall(void)
327185347Sattilio{
328185347Sattilio	struct aggent *agg;
329185347Sattilio
330185347Sattilio	while (TAILQ_EMPTY(&fqueue) == 0) {
331185347Sattilio		agg = TAILQ_FIRST(&fqueue);
332185347Sattilio		TAILQ_REMOVE(&fqueue, agg, ag_fiter);
333185347Sattilio	}
334185347Sattilio}
335185347Sattilio
336185347Sattilio/*
337185347Sattilio * Insert a raw entry into the aggregations queue.
338185347Sattilio * If the respective first-level aggregation object
339185347Sattilio * does not exist create it and maintain it sorted
340185347Sattilio * in respect of the number of samples.
341185347Sattilio */
342185347Sattiliostatic int
343185347Sattiliofqueue_insertent(struct entry *entry)
344185347Sattilio{
345185347Sattilio	struct aggent *obj, *tmp;
346185347Sattilio	int found;
347185347Sattilio
348185347Sattilio	found = 0;
349185347Sattilio	TAILQ_FOREACH(obj, &fqueue, ag_fiter)
350185347Sattilio		if (!strcmp(obj->ag_name, entry->en_name)) {
351185347Sattilio			found = 1;
352185347Sattilio			obj->ag_nsamples += entry->en_nsamples;
353185347Sattilio			break;
354185347Sattilio		}
355185347Sattilio
356185347Sattilio	/*
357213928Sbcr	 * If the first-level aggregation object already exists,
358185347Sattilio	 * just aggregate the samples and, if needed, resort
359185347Sattilio	 * it.
360185347Sattilio	 */
361185347Sattilio	if (found) {
362185347Sattilio		TAILQ_REMOVE(&fqueue, obj, ag_fiter);
363185347Sattilio		found = 0;
364185347Sattilio		TAILQ_FOREACH(tmp, &fqueue, ag_fiter)
365185347Sattilio			if (obj->ag_nsamples > tmp->ag_nsamples) {
366185347Sattilio				found = 1;
367185347Sattilio				break;
368185347Sattilio			}
369185347Sattilio		if (found)
370185347Sattilio			TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter);
371185347Sattilio		else
372185347Sattilio			TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter);
373185347Sattilio		return (0);
374185347Sattilio	}
375185347Sattilio
376185347Sattilio	/*
377185347Sattilio	 * If the first-level aggregation object does not
378185347Sattilio	 * exist, create it and put in the sorted queue.
379185347Sattilio	 * If this is the first object, we need to set the
380185347Sattilio	 * head of the queue.
381185347Sattilio	 */
382185347Sattilio	obj = agg_create(entry->en_name, entry->en_nsamples, entry->en_ostart,
383185347Sattilio	    entry->en_oend);
384185347Sattilio	if (obj == NULL)
385185347Sattilio		return (-1);
386185347Sattilio	if (TAILQ_EMPTY(&fqueue) != 0) {
387185347Sattilio		TAILQ_INSERT_HEAD(&fqueue, obj, ag_fiter);
388185347Sattilio		return (0);
389185347Sattilio	}
390185347Sattilio	TAILQ_FOREACH(tmp, &fqueue, ag_fiter)
391185347Sattilio		if (obj->ag_nsamples > tmp->ag_nsamples) {
392185347Sattilio			found = 1;
393185347Sattilio			break;
394185347Sattilio		}
395185347Sattilio	if (found)
396185347Sattilio		TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter);
397185347Sattilio	else
398185347Sattilio		TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter);
399185347Sattilio	return (0);
400185347Sattilio}
401185347Sattilio
402185347Sattilio/*
403185347Sattilio * Lookup a first-level aggregation object by name.
404185347Sattilio */
405185347Sattiliostatic struct aggent *
406185347Sattiliofqueue_findent_by_name(const char *name)
407185347Sattilio{
408185347Sattilio	struct aggent *obj;
409185347Sattilio
410185347Sattilio	TAILQ_FOREACH(obj, &fqueue, ag_fiter)
411185347Sattilio		if (!strcmp(obj->ag_name, name))
412185347Sattilio			return (obj);
413185347Sattilio	return (NULL);
414185347Sattilio}
415185347Sattilio
416185347Sattilio/*
417185347Sattilio * Return the number of object in the first-level aggregations queue.
418185347Sattilio */
419185347Sattiliostatic int
420185347Sattiliofqueue_getall(const char *bin, char *temp, int asmf)
421185347Sattilio{
422185347Sattilio	char tmpf[MAXPATHLEN * 2 + 50];
423185347Sattilio	struct aggent *agg;
424185347Sattilio	uintptr_t start, end;
425185347Sattilio
426185347Sattilio	if (mkstemp(temp) == -1)
427185347Sattilio		return (-1);
428185347Sattilio	TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
429185347Sattilio		bzero(tmpf, sizeof(tmpf));
430185347Sattilio		start = agg->ag_ostart;
431185347Sattilio		end = agg->ag_oend;
432185347Sattilio
433185347Sattilio		/*
434185347Sattilio		 * Fix-up the end address in order to show it in the objdump's
435185347Sattilio		 * trace.
436185347Sattilio		 */
437185347Sattilio		end++;
438185347Sattilio		if (asmf)
439185347Sattilio			snprintf(tmpf, sizeof(tmpf),
440185347Sattilio			    "objdump --start-address=%p "
441185347Sattilio			    "--stop-address=%p -d %s >> %s", (void *)start,
442185347Sattilio			    (void *)end, bin, temp);
443185347Sattilio		else
444185347Sattilio			snprintf(tmpf, sizeof(tmpf),
445185347Sattilio			    "objdump --start-address=%p "
446185347Sattilio			    "--stop-address=%p -S %s >> %s", (void *)start,
447185347Sattilio			    (void *)end, bin, temp);
448185347Sattilio		if (system(tmpf) != 0)
449185347Sattilio			return (-1);
450185347Sattilio	}
451185347Sattilio	return (0);
452185347Sattilio}
453185347Sattilio
454185347Sattilio/*
455185347Sattilio * Insert all the raw entries present in the general queue
456185347Sattilio * into the first-level aggregations queue.
457185347Sattilio */
458185347Sattiliostatic int
459185347Sattiliofqueue_insertgen(void)
460185347Sattilio{
461185347Sattilio	struct entry *obj;
462185347Sattilio
463185347Sattilio	TAILQ_FOREACH(obj, &mainlst, en_iter)
464185347Sattilio		if (fqueue_insertent(obj) == -1)
465185347Sattilio			return (-1);
466185347Sattilio	return (0);
467185347Sattilio}
468185347Sattilio
469185347Sattilio/*
470185347Sattilio * Flush the raw entries general queue.
471185347Sattilio */
472185347Sattiliostatic void
473201227Sedgeneral_deleteall(void)
474185347Sattilio{
475185347Sattilio	struct entry *obj;
476185347Sattilio
477185347Sattilio	while (TAILQ_EMPTY(&mainlst) == 0) {
478185347Sattilio		obj = TAILQ_FIRST(&mainlst);
479185347Sattilio		TAILQ_REMOVE(&mainlst, obj, en_iter);
480185347Sattilio	}
481185347Sattilio}
482185347Sattilio
483185347Sattilio/*
484185347Sattilio * Lookup a raw entry by the PC.
485185347Sattilio */
486185347Sattiliostatic struct entry *
487185347Sattiliogeneral_findent(uintptr_t pc)
488185347Sattilio{
489185347Sattilio	struct entry *obj;
490185347Sattilio
491185347Sattilio	TAILQ_FOREACH(obj, &mainlst, en_iter)
492185347Sattilio		if (obj->en_pc == pc)
493185347Sattilio			return (obj);
494185347Sattilio	return (NULL);
495185347Sattilio}
496185347Sattilio
497185347Sattilio/*
498185347Sattilio * Insert a new raw entry in the general queue.
499185347Sattilio */
500185347Sattiliostatic void
501185347Sattiliogeneral_insertent(struct entry *entry)
502185347Sattilio{
503185347Sattilio
504185347Sattilio	TAILQ_INSERT_TAIL(&mainlst, entry, en_iter);
505185347Sattilio}
506185347Sattilio
507185347Sattilio/*
508185347Sattilio * Printout the body of an "objdump -d" assembly function.
509185347Sattilio * It does simply stops when a new function is encountered,
510185347Sattilio * bringing back the file position in order to not mess up
511185347Sattilio * subsequent analysis.
512185347Sattilio * C lines and others not recognized are simply skipped.
513185347Sattilio */
514185347Sattiliostatic void
515185347Sattiliogeneral_printasm(FILE *fp, struct aggent *agg)
516185347Sattilio{
517185347Sattilio	char buffer[LNBUFF];
518185347Sattilio	struct entry *obj;
519185347Sattilio	int nbytes;
520185347Sattilio	void *ptr;
521185347Sattilio
522185347Sattilio	while (fgets(buffer, LNBUFF, fp) != NULL) {
523185347Sattilio		if ((nbytes = newfunction(buffer)) != 0) {
524185347Sattilio			fseek(fp, nbytes * -1, SEEK_CUR);
525185347Sattilio			break;
526185347Sattilio		}
527185347Sattilio		if (!isasminline(buffer))
528185347Sattilio			continue;
529185347Sattilio		if (sscanf(buffer, " %p:", &ptr) != 1)
530185347Sattilio			continue;
531185347Sattilio		obj = general_findent((uintptr_t)ptr);
532185347Sattilio		if (obj == NULL)
533185347Sattilio			printf("\t| %s", buffer);
534185347Sattilio		else
535185347Sattilio			printf("%.2f%%\t| %s",
536185347Sattilio			    (float)obj->en_nsamples * 100 / agg->ag_nsamples,
537185347Sattilio			    buffer);
538185347Sattilio	}
539185347Sattilio}
540185347Sattilio
541185347Sattilio/*
542185347Sattilio * Printout the body of an "objdump -S" function.
543185347Sattilio * It does simply stops when a new function is encountered,
544185347Sattilio * bringing back the file position in order to not mess up
545185347Sattilio * subsequent analysis.
546185347Sattilio * It expect from the starting to the end to find, always, valid blocks
547185347Sattilio * (see below for an explanation of the "block" concept).
548185347Sattilio */
549185347Sattiliostatic int
550185347Sattiliogeneral_printc(FILE *fp, struct aggent *agg)
551185347Sattilio{
552185347Sattilio	char buffer[LNBUFF];
553185347Sattilio
554185347Sattilio	while (fgets(buffer, LNBUFF, fp) != NULL) {
555185347Sattilio		fseek(fp, strlen(buffer) * -1, SEEK_CUR);
556185347Sattilio		if (newfunction(buffer) != 0)
557185347Sattilio			break;
558185347Sattilio		if (printblock(fp, agg) == -1)
559185347Sattilio			return (-1);
560185347Sattilio	}
561185347Sattilio	return (0);
562185347Sattilio}
563185347Sattilio
564185347Sattilio/*
565185347Sattilio * Printout a single block inside an "objdump -S" function.
566185347Sattilio * The block is composed of a first part in C and subsequent translation
567185347Sattilio * in assembly.
568185347Sattilio * This code also operates a second-level aggregation packing together
569185347Sattilio * samples relative to PCs into a (lower bottom) block with their
570185347Sattilio * C (higher half) counterpart.
571185347Sattilio */
572185347Sattiliostatic int
573185347Sattilioprintblock(FILE *fp, struct aggent *agg)
574185347Sattilio{
575185347Sattilio	char buffer[LNBUFF];
576185347Sattilio	long lstart;
577185347Sattilio	struct entry *obj;
578185347Sattilio	u_int tnsamples;
579185347Sattilio	int done, nbytes, sentinel;
580185347Sattilio	void *ptr;
581185347Sattilio
582185347Sattilio	/*
583185347Sattilio	 * We expect the first thing of the block is C code, so simply give
584185347Sattilio	 * up if asm line is found.
585185347Sattilio	 */
586185347Sattilio	lstart = ftell(fp);
587185347Sattilio	sentinel = 0;
588185347Sattilio	for (;;) {
589185347Sattilio		if (fgets(buffer, LNBUFF, fp) == NULL)
590185347Sattilio			return (0);
591185347Sattilio		if (isasminline(buffer) != 0)
592185347Sattilio			break;
593185347Sattilio		sentinel = 1;
594185347Sattilio		nbytes = newfunction(buffer);
595185347Sattilio		if (nbytes != 0) {
596185347Sattilio			if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
597185347Sattilio				return (-1);
598185347Sattilio			return (0);
599185347Sattilio		}
600185347Sattilio	}
601185347Sattilio
602185347Sattilio	/*
603185347Sattilio	 * If the sentinel is not set, it means it did not match any
604185347Sattilio	 * "high half" for this code so simply give up.
605185347Sattilio	 * Operates the second-level aggregation.
606185347Sattilio	 */
607185347Sattilio	tnsamples = 0;
608185347Sattilio	do {
609185347Sattilio		if (sentinel == 0)
610185347Sattilio			return (-1);
611185347Sattilio		if (sscanf(buffer, " %p:", &ptr) != 1)
612185347Sattilio			return (-1);
613185347Sattilio		obj = general_findent((uintptr_t)ptr);
614185347Sattilio		if (obj != NULL)
615185347Sattilio			tnsamples += obj->en_nsamples;
616185347Sattilio	} while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0);
617185347Sattilio
618185347Sattilio	/* Rewind to the start of the block in order to start the printout. */
619185347Sattilio	if (fseek(fp, lstart, SEEK_SET) == -1)
620185347Sattilio		return (-1);
621185347Sattilio
622185347Sattilio	/* Again the high half of the block rappresenting the C part. */
623185347Sattilio	done = 0;
624185347Sattilio	while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) {
625185347Sattilio		if (tnsamples == 0 || done != 0)
626185347Sattilio			printf("\t| %s", buffer);
627185347Sattilio		else {
628185347Sattilio			done = 1;
629185347Sattilio			printf("%.2f%%\t| %s",
630185347Sattilio			    (float)tnsamples * 100 / agg->ag_nsamples, buffer);
631185347Sattilio		}
632185347Sattilio	}
633185347Sattilio
634185347Sattilio	/*
635185347Sattilio	 * Again the low half of the block rappresenting the asm
636185347Sattilio	 * translation part.
637185347Sattilio	 */
638185347Sattilio	for (;;) {
639185347Sattilio		if (fgets(buffer, LNBUFF, fp) == NULL)
640185347Sattilio			return (0);
641185347Sattilio		if (isasminline(buffer) == 0)
642185347Sattilio			break;
643185347Sattilio		nbytes = newfunction(buffer);
644185347Sattilio		if (nbytes != 0) {
645185347Sattilio			if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
646185347Sattilio				return (-1);
647185347Sattilio			return (0);
648185347Sattilio		}
649185347Sattilio	}
650185347Sattilio	if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1)
651185347Sattilio		return (-1);
652185347Sattilio	return (0);
653185347Sattilio}
654185347Sattilio
655185347Sattilio/*
656185347Sattilio * Helper printout functions.
657185347Sattilio */
658185347Sattiliostatic void
659185347Sattiliousage(const char *progname)
660185347Sattilio{
661185347Sattilio
662185347Sattilio	fprintf(stderr,
663185347Sattilio	    "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n",
664185347Sattilio	    progname);
665185347Sattilio	exit(EXIT_SUCCESS);
666185347Sattilio}
667185347Sattilio
668185347Sattilioint
669185347Sattiliomain(int argc, char *argv[])
670185347Sattilio{
671185347Sattilio	char buffer[LNBUFF], fname[FNBUFF], tbfl[] = TMPPATH, tofl[] = TMPPATH;
672185347Sattilio	char tmpf[MAXPATHLEN * 2 + 50];
673185347Sattilio	float limit;
674185347Sattilio	char *bin, *exec, *kfile, *ofile;
675185347Sattilio	struct entry *obj;
676185347Sattilio	FILE *gfp, *bfp;
677185347Sattilio	void *ptr, *hstart, *hend;
678185347Sattilio	uintptr_t tmppc, ostart, oend;
679185347Sattilio	int cget, asmsrc;
680185347Sattilio
681185347Sattilio	exec = argv[0];
682185347Sattilio	ofile = NULL;
683185347Sattilio	bin = NULL;
684185347Sattilio	kfile = NULL;
685185347Sattilio	asmsrc = 0;
686185347Sattilio	limit = 0.5;
687185347Sattilio	while ((cget = getopt(argc, argv, "ahl:k:")) != -1)
688185347Sattilio		switch(cget) {
689185347Sattilio		case 'a':
690185347Sattilio			asmsrc = 1;
691185347Sattilio			break;
692185347Sattilio		case 'k':
693185347Sattilio			kfile = optarg;
694185347Sattilio			break;
695185347Sattilio		case 'l':
696185347Sattilio			limit = (float)atof(optarg);
697185347Sattilio			break;
698185347Sattilio		case 'h':
699185347Sattilio		case '?':
700185347Sattilio		default:
701185347Sattilio			usage(exec);
702185347Sattilio		}
703185347Sattilio	argc -= optind;
704185347Sattilio	argv += optind;
705185347Sattilio	if (argc != 2)
706185347Sattilio		usage(exec);
707185347Sattilio	ofile = argv[0];
708185347Sattilio	bin = argv[1];
709185347Sattilio
710185347Sattilio	if (access(bin, R_OK | F_OK) == -1)
711185347Sattilio		FATAL(exec, "%s: Impossible to locate the binary file\n",
712185347Sattilio		    exec);
713185347Sattilio	if (access(ofile, R_OK | F_OK) == -1)
714185347Sattilio		FATAL(exec, "%s: Impossible to locate the pmcstat file\n",
715185347Sattilio		    exec);
716185347Sattilio	if (kfile != NULL && access(kfile, R_OK | F_OK) == -1)
717185347Sattilio		FATAL(exec, "%s: Impossible to locate the kernel file\n",
718185347Sattilio		    exec);
719185347Sattilio
720185347Sattilio	bzero(tmpf, sizeof(tmpf));
721185347Sattilio	if (mkstemp(tofl) == -1)
722185347Sattilio		FATAL(exec, "%s: Impossible to create the tmp file\n",
723185347Sattilio		    exec);
724185347Sattilio	if (kfile != NULL)
725185347Sattilio		snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s",
726185347Sattilio		    kfile, ofile, tofl);
727185347Sattilio	else
728185347Sattilio		snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile,
729185347Sattilio		    tofl);
730185347Sattilio	if (system(tmpf) != 0)
731185347Sattilio		FATAL(exec, "%s: Impossible to create the tmp file\n",
732185347Sattilio		    exec);
733185347Sattilio
734185347Sattilio	gfp = fopen(tofl, "r");
735185347Sattilio	if (gfp == NULL)
736185347Sattilio		FATAL(exec, "%s: Impossible to open the map file\n",
737185347Sattilio		    exec);
738185347Sattilio
739185347Sattilio	/*
740185347Sattilio	 * Make the collection of raw entries from a pmcstat mapped file.
741185347Sattilio	 * The heuristic here wants strings in the form:
742185347Sattilio	 * "addr funcname startfaddr endfaddr".
743185347Sattilio	 */
744185347Sattilio	while (fgets(buffer, LNBUFF, gfp) != NULL) {
745185347Sattilio		if (isspace(buffer[0]))
746185347Sattilio			continue;
747185347Sattilio		if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname,
748185347Sattilio		    &hstart, &hend) != 4)
749185347Sattilio			FATAL(NULL,
750185347Sattilio			    "%s: Invalid scan of function in the map file\n",
751185347Sattilio			    exec);
752185347Sattilio		ostart = (uintptr_t)hstart;
753185347Sattilio		oend = (uintptr_t)hend;
754185347Sattilio		tmppc = (uintptr_t)ptr;
755185347Sattilio		totalsamples++;
756185347Sattilio		obj = general_findent(tmppc);
757185347Sattilio		if (obj != NULL) {
758185347Sattilio			entry_acqref(obj);
759185347Sattilio			continue;
760185347Sattilio		}
761185347Sattilio		obj = entry_create(fname, tmppc, ostart, oend);
762185347Sattilio		if (obj == NULL)
763185347Sattilio			FATAL(exec,
764185347Sattilio			    "%s: Impossible to create a new object\n", exec);
765185347Sattilio		general_insertent(obj);
766185347Sattilio	}
767185347Sattilio	if (fclose(gfp) == EOF)
768185347Sattilio		FATAL(exec, "%s: Impossible to close the filedesc\n",
769185347Sattilio		    exec);
770185347Sattilio	if (remove(tofl) == -1)
771185347Sattilio                FATAL(exec, "%s: Impossible to remove the tmpfile\n",
772185347Sattilio                    exec);
773185347Sattilio
774185347Sattilio	/*
775185347Sattilio	 * Remove the loose end objects and feed the first-level aggregation
776185347Sattilio	 * queue.
777185347Sattilio	 */
778185347Sattilio	if (fqueue_insertgen() == -1)
779185347Sattilio		FATAL(exec, "%s: Impossible to generate an analysis\n",
780185347Sattilio		    exec);
781185347Sattilio	fqueue_compact(limit);
782185347Sattilio	if (fqueue_getall(bin, tbfl, asmsrc) == -1)
783185347Sattilio		FATAL(exec, "%s: Impossible to create the tmp file\n",
784185347Sattilio		    exec);
785185347Sattilio
786185347Sattilio	bfp = fopen(tbfl, "r");
787185347Sattilio	if (bfp == NULL)
788185347Sattilio		FATAL(exec, "%s: Impossible to open the binary file\n",
789185347Sattilio		    exec);
790185347Sattilio
791185347Sattilio	if (asmsrc != 0)
792185347Sattilio		asmparse(bfp);
793185347Sattilio	else if (cparse(bfp) == -1)
794185347Sattilio		FATAL(NULL, "%s: Invalid format for the C file\n", exec);
795185347Sattilio	if (fclose(bfp) == EOF)
796185347Sattilio                FATAL(exec, "%s: Impossible to close the filedesc\n",
797185347Sattilio                    exec);
798185347Sattilio	if (remove(tbfl) == -1)
799185347Sattilio                FATAL(exec, "%s: Impossible to remove the tmpfile\n",
800185347Sattilio                    exec);
801185347Sattilio	return (0);
802185347Sattilio}
803