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