xref: /freebsd/usr.sbin/pmcannotate/pmcannotate.c (revision de11299323e0a647ae2164f74ca966862e7c4dd8)
11b4822a4SAttilio Rao /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
41b4822a4SAttilio Rao  * Copyright (c) 2008 Nokia Corporation
51b4822a4SAttilio Rao  * All rights reserved.
61b4822a4SAttilio Rao  *
71b4822a4SAttilio Rao  * This software was developed by Attilio Rao for the IPSO project under
81b4822a4SAttilio Rao  * contract to Nokia Corporation.
91b4822a4SAttilio Rao  *
101b4822a4SAttilio Rao  * Redistribution and use in source and binary forms, with or without
111b4822a4SAttilio Rao  * modification, are permitted provided that the following conditions
121b4822a4SAttilio Rao  * are met:
131b4822a4SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
141b4822a4SAttilio Rao  *    notice unmodified, this list of conditions, and the following
151b4822a4SAttilio Rao  *    disclaimer.
161b4822a4SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
171b4822a4SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
181b4822a4SAttilio Rao  *    documentation and/or other materials provided with the distribution.
191b4822a4SAttilio Rao  *
201b4822a4SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
211b4822a4SAttilio Rao  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
221b4822a4SAttilio Rao  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
231b4822a4SAttilio Rao  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
241b4822a4SAttilio Rao  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
251b4822a4SAttilio Rao  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
261b4822a4SAttilio Rao  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
271b4822a4SAttilio Rao  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
281b4822a4SAttilio Rao  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
291b4822a4SAttilio Rao  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
301b4822a4SAttilio Rao  *
311b4822a4SAttilio Rao  */
321b4822a4SAttilio Rao 
331b4822a4SAttilio Rao #include <sys/param.h>
341b4822a4SAttilio Rao #include <sys/queue.h>
351b4822a4SAttilio Rao 
361b4822a4SAttilio Rao #include <ctype.h>
37*de112993SJohn Baldwin #include <err.h>
38b4cc0838SAndrew Gallatin #include <paths.h>
391b4822a4SAttilio Rao #include <stdio.h>
401b4822a4SAttilio Rao #include <stdlib.h>
411b4822a4SAttilio Rao #include <string.h>
421b4822a4SAttilio Rao 
431b4822a4SAttilio Rao #include <unistd.h>
441b4822a4SAttilio Rao 
4504abf5f1SJohn-Mark Gurney /* NB: Make sure FNBUFF is as large as LNBUFF, otherwise it could overflow */
46caaeeed4SGeorge V. Neville-Neil #define	FNBUFF	512
47caaeeed4SGeorge V. Neville-Neil #define	LNBUFF	512
481b4822a4SAttilio Rao 
49b4cc0838SAndrew Gallatin #define	TMPNAME	"pmcannotate.XXXXXX"
501b4822a4SAttilio Rao 
511b4822a4SAttilio Rao #define	FATAL(ptr, x ...) do {						\
521b4822a4SAttilio Rao 	fqueue_deleteall();						\
531b4822a4SAttilio Rao 	general_deleteall();						\
541b4822a4SAttilio Rao 	if ((ptr) != NULL)						\
551b4822a4SAttilio Rao 		perror(ptr);						\
561b4822a4SAttilio Rao 	fprintf(stderr, ##x);						\
571b4822a4SAttilio Rao 	remove(tbfl);							\
581b4822a4SAttilio Rao 	remove(tofl);							\
591b4822a4SAttilio Rao 	exit(EXIT_FAILURE);						\
601b4822a4SAttilio Rao } while (0)
611b4822a4SAttilio Rao 
62*de112993SJohn Baldwin #define	PERCSAMP(x)	((float)(x) * 100 / totalsamples)
631b4822a4SAttilio Rao 
641b4822a4SAttilio Rao struct entry {
651b4822a4SAttilio Rao         TAILQ_ENTRY(entry)	en_iter;
661b4822a4SAttilio Rao         char		*en_name;
671b4822a4SAttilio Rao 	uintptr_t	en_pc;
681b4822a4SAttilio Rao 	uintptr_t	en_ostart;
691b4822a4SAttilio Rao 	uintptr_t	en_oend;
701b4822a4SAttilio Rao 	u_int		en_nsamples;
711b4822a4SAttilio Rao };
721b4822a4SAttilio Rao 
731b4822a4SAttilio Rao struct aggent {
741b4822a4SAttilio Rao 	TAILQ_ENTRY(aggent)	ag_fiter;
751b4822a4SAttilio Rao 	long		ag_offset;
761b4822a4SAttilio Rao 	uintptr_t	ag_ostart;
771b4822a4SAttilio Rao 	uintptr_t	ag_oend;
781b4822a4SAttilio Rao 	char		*ag_name;
791b4822a4SAttilio Rao 	u_int		ag_nsamples;
801b4822a4SAttilio Rao };
811b4822a4SAttilio Rao 
821b4822a4SAttilio Rao static struct aggent	*agg_create(const char *name, u_int nsamples,
831b4822a4SAttilio Rao 			    uintptr_t start, uintptr_t end);
841b4822a4SAttilio Rao static void		 agg_destroy(struct aggent *agg) __unused;
851b4822a4SAttilio Rao static void		 asmparse(FILE *fp);
861b4822a4SAttilio Rao static int		 cparse(FILE *fp);
871b4822a4SAttilio Rao static void		 entry_acqref(struct entry *entry);
881b4822a4SAttilio Rao static struct entry	*entry_create(const char *name, uintptr_t pc,
891b4822a4SAttilio Rao 			    uintptr_t start, uintptr_t end);
901b4822a4SAttilio Rao static void		 entry_destroy(struct entry *entry) __unused;
911b4822a4SAttilio Rao static void		 fqueue_compact(float th);
921b4822a4SAttilio Rao static void		 fqueue_deleteall(void);
931b4822a4SAttilio Rao static struct aggent	*fqueue_findent_by_name(const char *name);
941b4822a4SAttilio Rao static int		 fqueue_getall(const char *bin, char *temp, int asmf);
951b4822a4SAttilio Rao static int		 fqueue_insertent(struct entry *entry);
961b4822a4SAttilio Rao static int		 fqueue_insertgen(void);
971b4822a4SAttilio Rao static void		 general_deleteall(void);
981b4822a4SAttilio Rao static struct entry	*general_findent(uintptr_t pc);
991b4822a4SAttilio Rao static void		 general_insertent(struct entry *entry);
1001b4822a4SAttilio Rao static void		 general_printasm(FILE *fp, struct aggent *agg);
1011b4822a4SAttilio Rao static int		 general_printc(FILE *fp, struct aggent *agg);
1021b4822a4SAttilio Rao static int		 printblock(FILE *fp, struct aggent *agg);
1031b4822a4SAttilio Rao static void		 usage(const char *progname) __dead2;
1041b4822a4SAttilio Rao 
1051b4822a4SAttilio Rao static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
1061b4822a4SAttilio Rao static TAILQ_HEAD(, aggent) fqueue = TAILQ_HEAD_INITIALIZER(fqueue);
1071b4822a4SAttilio Rao 
1081b4822a4SAttilio Rao /*
1091b4822a4SAttilio Rao  * Use a float value in order to automatically promote operations
1101b4822a4SAttilio Rao  * to return a float value rather than use casts.
1111b4822a4SAttilio Rao  */
112*de112993SJohn Baldwin static u_int totalsamples;
113*de112993SJohn Baldwin 
114*de112993SJohn Baldwin static enum { RAW, BLOCK_PERCENT, GLOBAL_PERCENT } print_mode;
1151b4822a4SAttilio Rao 
1161b4822a4SAttilio Rao /*
1171b4822a4SAttilio Rao  * Identifies a string cointaining objdump's assembly printout.
1181b4822a4SAttilio Rao  */
1191b4822a4SAttilio Rao static inline int
isasminline(const char * str)1201b4822a4SAttilio Rao isasminline(const char *str)
1211b4822a4SAttilio Rao {
1221b4822a4SAttilio Rao 	void *ptr;
1231b4822a4SAttilio Rao 	int nbytes;
1241b4822a4SAttilio Rao 
1251b4822a4SAttilio Rao 	if (sscanf(str, " %p%n", &ptr, &nbytes) != 1)
1261b4822a4SAttilio Rao 		return (0);
1271b4822a4SAttilio Rao 	if (str[nbytes] != ':' || isspace(str[nbytes + 1]) == 0)
1281b4822a4SAttilio Rao 		return (0);
1291b4822a4SAttilio Rao 	return (1);
1301b4822a4SAttilio Rao }
1311b4822a4SAttilio Rao 
1321b4822a4SAttilio Rao /*
1331b4822a4SAttilio Rao  * Identifies a string containing objdump's assembly printout
1341b4822a4SAttilio Rao  * for a new function.
1351b4822a4SAttilio Rao  */
1361b4822a4SAttilio Rao static inline int
newfunction(const char * str)1371b4822a4SAttilio Rao newfunction(const char *str)
1381b4822a4SAttilio Rao {
1391b4822a4SAttilio Rao 	char fname[FNBUFF];
1401b4822a4SAttilio Rao 	void *ptr;
1411b4822a4SAttilio Rao 	int nbytes;
1421b4822a4SAttilio Rao 
1431b4822a4SAttilio Rao 	if (isspace(str[0]))
1441b4822a4SAttilio Rao 		return (0);
1451b4822a4SAttilio Rao 	if (sscanf(str, "%p <%[^>:]>:%n", &ptr, fname, &nbytes) != 2)
1461b4822a4SAttilio Rao 		return (0);
1471b4822a4SAttilio Rao 	return (nbytes);
1481b4822a4SAttilio Rao }
1491b4822a4SAttilio Rao 
1501b4822a4SAttilio Rao /*
1511b4822a4SAttilio Rao  * Create a new first-level aggregation object for a specified
1521b4822a4SAttilio Rao  * function.
1531b4822a4SAttilio Rao  */
1541b4822a4SAttilio Rao static struct aggent *
agg_create(const char * name,u_int nsamples,uintptr_t start,uintptr_t end)1551b4822a4SAttilio Rao agg_create(const char *name, u_int nsamples, uintptr_t start, uintptr_t end)
1561b4822a4SAttilio Rao {
1571b4822a4SAttilio Rao 	struct aggent *agg;
1581b4822a4SAttilio Rao 
1591b4822a4SAttilio Rao 	agg = calloc(1, sizeof(struct aggent));
1601b4822a4SAttilio Rao 	if (agg == NULL)
1611b4822a4SAttilio Rao 		return (NULL);
1621b4822a4SAttilio Rao 	agg->ag_name = strdup(name);
1631b4822a4SAttilio Rao 	if (agg->ag_name == NULL) {
1641b4822a4SAttilio Rao 		free(agg);
1651b4822a4SAttilio Rao 		return (NULL);
1661b4822a4SAttilio Rao 	}
1671b4822a4SAttilio Rao 	agg->ag_nsamples = nsamples;
1681b4822a4SAttilio Rao 	agg->ag_ostart = start;
1691b4822a4SAttilio Rao 	agg->ag_oend = end;
1701b4822a4SAttilio Rao 	return (agg);
1711b4822a4SAttilio Rao }
1721b4822a4SAttilio Rao 
1731b4822a4SAttilio Rao /*
1741b4822a4SAttilio Rao  * Destroy a first-level aggregation object for a specified
1751b4822a4SAttilio Rao  * function.
1761b4822a4SAttilio Rao  */
1771b4822a4SAttilio Rao static void
agg_destroy(struct aggent * agg)1781b4822a4SAttilio Rao agg_destroy(struct aggent *agg)
1791b4822a4SAttilio Rao {
1801b4822a4SAttilio Rao 
1811b4822a4SAttilio Rao 	free(agg->ag_name);
1821b4822a4SAttilio Rao 	free(agg);
1831b4822a4SAttilio Rao }
1841b4822a4SAttilio Rao 
1851b4822a4SAttilio Rao /*
1861b4822a4SAttilio Rao  * Analyze the "objdump -d" output, locate functions and start
1871b4822a4SAttilio Rao  * printing out the assembly functions content.
1881b4822a4SAttilio Rao  * We do not use newfunction() because we actually need the
1891b4822a4SAttilio Rao  * function name in available form, but the heurstic used is
1901b4822a4SAttilio Rao  * the same.
1911b4822a4SAttilio Rao  */
1921b4822a4SAttilio Rao static void
asmparse(FILE * fp)1931b4822a4SAttilio Rao asmparse(FILE *fp)
1941b4822a4SAttilio Rao {
1951b4822a4SAttilio Rao 	char buffer[LNBUFF], fname[FNBUFF];
1961b4822a4SAttilio Rao 	struct aggent *agg;
1971b4822a4SAttilio Rao 	void *ptr;
1981b4822a4SAttilio Rao 
1991b4822a4SAttilio Rao 	while (fgets(buffer, LNBUFF, fp) != NULL) {
2001b4822a4SAttilio Rao 		if (isspace(buffer[0]))
2011b4822a4SAttilio Rao 			continue;
2021b4822a4SAttilio Rao 		if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2)
2031b4822a4SAttilio Rao 			continue;
2041b4822a4SAttilio Rao 		agg = fqueue_findent_by_name(fname);
2051b4822a4SAttilio Rao 		if (agg == NULL)
2061b4822a4SAttilio Rao 			continue;
2071b4822a4SAttilio Rao 		agg->ag_offset = ftell(fp);
2081b4822a4SAttilio Rao 	}
2091b4822a4SAttilio Rao 
2101b4822a4SAttilio Rao 	TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
2111b4822a4SAttilio Rao 		if (fseek(fp, agg->ag_offset, SEEK_SET) == -1)
2121b4822a4SAttilio Rao 			return;
2131b4822a4SAttilio Rao 		printf("Profile trace for function: %s() [%.2f%%]\n",
2141b4822a4SAttilio Rao 		    agg->ag_name, PERCSAMP(agg->ag_nsamples));
2151b4822a4SAttilio Rao 		general_printasm(fp, agg);
2161b4822a4SAttilio Rao 		printf("\n");
2171b4822a4SAttilio Rao 	}
2181b4822a4SAttilio Rao }
2191b4822a4SAttilio Rao 
2201b4822a4SAttilio Rao /*
2211b4822a4SAttilio Rao  * Analyze the "objdump -S" output, locate functions and start
2221b4822a4SAttilio Rao  * printing out the C functions content.
2231b4822a4SAttilio Rao  * We do not use newfunction() because we actually need the
2241b4822a4SAttilio Rao  * function name in available form, but the heurstic used is
2251b4822a4SAttilio Rao  * the same.
2261b4822a4SAttilio Rao  * In order to maintain the printout sorted, on the first pass it
2271b4822a4SAttilio Rao  * simply stores the file offsets in order to fastly moved later
2281b4822a4SAttilio Rao  * (when the file is hot-cached also) when the real printout will
2291b4822a4SAttilio Rao  * happen.
2301b4822a4SAttilio Rao  */
2311b4822a4SAttilio Rao static int
cparse(FILE * fp)2321b4822a4SAttilio Rao cparse(FILE *fp)
2331b4822a4SAttilio Rao {
2341b4822a4SAttilio Rao 	char buffer[LNBUFF], fname[FNBUFF];
2351b4822a4SAttilio Rao 	struct aggent *agg;
2361b4822a4SAttilio Rao 	void *ptr;
2371b4822a4SAttilio Rao 
2381b4822a4SAttilio Rao 	while (fgets(buffer, LNBUFF, fp) != NULL) {
2391b4822a4SAttilio Rao 		if (isspace(buffer[0]))
2401b4822a4SAttilio Rao 			continue;
2411b4822a4SAttilio Rao 		if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2)
2421b4822a4SAttilio Rao 			continue;
2431b4822a4SAttilio Rao 		agg = fqueue_findent_by_name(fname);
2441b4822a4SAttilio Rao 		if (agg == NULL)
2451b4822a4SAttilio Rao 			continue;
2461b4822a4SAttilio Rao 		agg->ag_offset = ftell(fp);
2471b4822a4SAttilio Rao 	}
2481b4822a4SAttilio Rao 
2491b4822a4SAttilio Rao 	TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
2501b4822a4SAttilio Rao 		if (fseek(fp, agg->ag_offset, SEEK_SET) == -1)
2511b4822a4SAttilio Rao 			return (-1);
2521b4822a4SAttilio Rao 		printf("Profile trace for function: %s() [%.2f%%]\n",
2531b4822a4SAttilio Rao 		    agg->ag_name, PERCSAMP(agg->ag_nsamples));
2541b4822a4SAttilio Rao 		if (general_printc(fp, agg) == -1)
2551b4822a4SAttilio Rao 			return (-1);
2561b4822a4SAttilio Rao 		printf("\n");
2571b4822a4SAttilio Rao 	}
2581b4822a4SAttilio Rao 	return (0);
2591b4822a4SAttilio Rao }
2601b4822a4SAttilio Rao 
2611b4822a4SAttilio Rao /*
2621b4822a4SAttilio Rao  * Bump the number of samples for any raw entry.
2631b4822a4SAttilio Rao  */
2641b4822a4SAttilio Rao static void
entry_acqref(struct entry * entry)2651b4822a4SAttilio Rao entry_acqref(struct entry *entry)
2661b4822a4SAttilio Rao {
2671b4822a4SAttilio Rao 
2681b4822a4SAttilio Rao 	entry->en_nsamples++;
2691b4822a4SAttilio Rao }
2701b4822a4SAttilio Rao 
2711b4822a4SAttilio Rao /*
2721b4822a4SAttilio Rao  * Create a new raw entry object for a specified function.
2731b4822a4SAttilio Rao  */
2741b4822a4SAttilio Rao static struct entry *
entry_create(const char * name,uintptr_t pc,uintptr_t start,uintptr_t end)2751b4822a4SAttilio Rao entry_create(const char *name, uintptr_t pc, uintptr_t start, uintptr_t end)
2761b4822a4SAttilio Rao {
2771b4822a4SAttilio Rao 	struct entry *obj;
2781b4822a4SAttilio Rao 
2791b4822a4SAttilio Rao 	obj = calloc(1, sizeof(struct entry));
2801b4822a4SAttilio Rao 	if (obj == NULL)
2811b4822a4SAttilio Rao 		return (NULL);
2821b4822a4SAttilio Rao 	obj->en_name = strdup(name);
2831b4822a4SAttilio Rao 	if (obj->en_name == NULL) {
2841b4822a4SAttilio Rao 		free(obj);
2851b4822a4SAttilio Rao 		return (NULL);
2861b4822a4SAttilio Rao 	}
2871b4822a4SAttilio Rao 	obj->en_pc = pc;
2881b4822a4SAttilio Rao 	obj->en_ostart = start;
2891b4822a4SAttilio Rao 	obj->en_oend = end;
2901b4822a4SAttilio Rao 	obj->en_nsamples = 1;
2911b4822a4SAttilio Rao 	return (obj);
2921b4822a4SAttilio Rao }
2931b4822a4SAttilio Rao 
2941b4822a4SAttilio Rao /*
2951b4822a4SAttilio Rao  * Destroy a raw entry object for a specified function.
2961b4822a4SAttilio Rao  */
2971b4822a4SAttilio Rao static void
entry_destroy(struct entry * entry)2981b4822a4SAttilio Rao entry_destroy(struct entry *entry)
2991b4822a4SAttilio Rao {
3001b4822a4SAttilio Rao 
3011b4822a4SAttilio Rao 	free(entry->en_name);
3021b4822a4SAttilio Rao 	free(entry);
3031b4822a4SAttilio Rao }
3041b4822a4SAttilio Rao 
3051b4822a4SAttilio Rao /*
3061b4822a4SAttilio Rao  * Specify a lower bound in percentage and drop from the
3071b4822a4SAttilio Rao  * first-level aggregation queue all the objects with a
3081b4822a4SAttilio Rao  * smaller impact.
3091b4822a4SAttilio Rao  */
3101b4822a4SAttilio Rao static void
fqueue_compact(float th)3111b4822a4SAttilio Rao fqueue_compact(float th)
3121b4822a4SAttilio Rao {
3131b4822a4SAttilio Rao 	u_int thi;
3141b4822a4SAttilio Rao 	struct aggent *agg, *tmpagg;
3151b4822a4SAttilio Rao 
3161b4822a4SAttilio Rao 	if (totalsamples == 0)
3171b4822a4SAttilio Rao 		return;
3181b4822a4SAttilio Rao 
3191b4822a4SAttilio Rao 	/* Revert the percentage calculation. */
3201b4822a4SAttilio Rao 	thi = th * totalsamples / 100;
3211b4822a4SAttilio Rao 	TAILQ_FOREACH_SAFE(agg, &fqueue, ag_fiter, tmpagg)
3221b4822a4SAttilio Rao 		if (agg->ag_nsamples < thi)
3231b4822a4SAttilio Rao 			TAILQ_REMOVE(&fqueue, agg, ag_fiter);
3241b4822a4SAttilio Rao }
3251b4822a4SAttilio Rao 
3261b4822a4SAttilio Rao /*
3271b4822a4SAttilio Rao  * Flush the first-level aggregates queue.
3281b4822a4SAttilio Rao  */
3291b4822a4SAttilio Rao static void
fqueue_deleteall(void)33010bc3a7fSEd Schouten fqueue_deleteall(void)
3311b4822a4SAttilio Rao {
3321b4822a4SAttilio Rao 	struct aggent *agg;
3331b4822a4SAttilio Rao 
3341b4822a4SAttilio Rao 	while (TAILQ_EMPTY(&fqueue) == 0) {
3351b4822a4SAttilio Rao 		agg = TAILQ_FIRST(&fqueue);
3361b4822a4SAttilio Rao 		TAILQ_REMOVE(&fqueue, agg, ag_fiter);
3371b4822a4SAttilio Rao 	}
3381b4822a4SAttilio Rao }
3391b4822a4SAttilio Rao 
3401b4822a4SAttilio Rao /*
3411b4822a4SAttilio Rao  * Insert a raw entry into the aggregations queue.
3421b4822a4SAttilio Rao  * If the respective first-level aggregation object
3431b4822a4SAttilio Rao  * does not exist create it and maintain it sorted
3441b4822a4SAttilio Rao  * in respect of the number of samples.
3451b4822a4SAttilio Rao  */
3461b4822a4SAttilio Rao static int
fqueue_insertent(struct entry * entry)3471b4822a4SAttilio Rao fqueue_insertent(struct entry *entry)
3481b4822a4SAttilio Rao {
3491b4822a4SAttilio Rao 	struct aggent *obj, *tmp;
3501b4822a4SAttilio Rao 	int found;
3511b4822a4SAttilio Rao 
3521b4822a4SAttilio Rao 	found = 0;
3531b4822a4SAttilio Rao 	TAILQ_FOREACH(obj, &fqueue, ag_fiter)
3541b4822a4SAttilio Rao 		if (!strcmp(obj->ag_name, entry->en_name)) {
3551b4822a4SAttilio Rao 			found = 1;
3561b4822a4SAttilio Rao 			obj->ag_nsamples += entry->en_nsamples;
3571b4822a4SAttilio Rao 			break;
3581b4822a4SAttilio Rao 		}
3591b4822a4SAttilio Rao 
3601b4822a4SAttilio Rao 	/*
36154ac64abSBenedict Reuschling 	 * If the first-level aggregation object already exists,
3621b4822a4SAttilio Rao 	 * just aggregate the samples and, if needed, resort
3631b4822a4SAttilio Rao 	 * it.
3641b4822a4SAttilio Rao 	 */
3651b4822a4SAttilio Rao 	if (found) {
3661b4822a4SAttilio Rao 		TAILQ_REMOVE(&fqueue, obj, ag_fiter);
3671b4822a4SAttilio Rao 		found = 0;
3681b4822a4SAttilio Rao 		TAILQ_FOREACH(tmp, &fqueue, ag_fiter)
3691b4822a4SAttilio Rao 			if (obj->ag_nsamples > tmp->ag_nsamples) {
3701b4822a4SAttilio Rao 				found = 1;
3711b4822a4SAttilio Rao 				break;
3721b4822a4SAttilio Rao 			}
3731b4822a4SAttilio Rao 		if (found)
3741b4822a4SAttilio Rao 			TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter);
3751b4822a4SAttilio Rao 		else
3761b4822a4SAttilio Rao 			TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter);
3771b4822a4SAttilio Rao 		return (0);
3781b4822a4SAttilio Rao 	}
3791b4822a4SAttilio Rao 
3801b4822a4SAttilio Rao 	/*
3811b4822a4SAttilio Rao 	 * If the first-level aggregation object does not
3821b4822a4SAttilio Rao 	 * exist, create it and put in the sorted queue.
3831b4822a4SAttilio Rao 	 * If this is the first object, we need to set the
3841b4822a4SAttilio Rao 	 * head of the queue.
3851b4822a4SAttilio Rao 	 */
3861b4822a4SAttilio Rao 	obj = agg_create(entry->en_name, entry->en_nsamples, entry->en_ostart,
3871b4822a4SAttilio Rao 	    entry->en_oend);
3881b4822a4SAttilio Rao 	if (obj == NULL)
3891b4822a4SAttilio Rao 		return (-1);
3901b4822a4SAttilio Rao 	if (TAILQ_EMPTY(&fqueue) != 0) {
3911b4822a4SAttilio Rao 		TAILQ_INSERT_HEAD(&fqueue, obj, ag_fiter);
3921b4822a4SAttilio Rao 		return (0);
3931b4822a4SAttilio Rao 	}
3941b4822a4SAttilio Rao 	TAILQ_FOREACH(tmp, &fqueue, ag_fiter)
3951b4822a4SAttilio Rao 		if (obj->ag_nsamples > tmp->ag_nsamples) {
3961b4822a4SAttilio Rao 			found = 1;
3971b4822a4SAttilio Rao 			break;
3981b4822a4SAttilio Rao 		}
3991b4822a4SAttilio Rao 	if (found)
4001b4822a4SAttilio Rao 		TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter);
4011b4822a4SAttilio Rao 	else
4021b4822a4SAttilio Rao 		TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter);
4031b4822a4SAttilio Rao 	return (0);
4041b4822a4SAttilio Rao }
4051b4822a4SAttilio Rao 
4061b4822a4SAttilio Rao /*
4071b4822a4SAttilio Rao  * Lookup a first-level aggregation object by name.
4081b4822a4SAttilio Rao  */
4091b4822a4SAttilio Rao static struct aggent *
fqueue_findent_by_name(const char * name)4101b4822a4SAttilio Rao fqueue_findent_by_name(const char *name)
4111b4822a4SAttilio Rao {
4121b4822a4SAttilio Rao 	struct aggent *obj;
4131b4822a4SAttilio Rao 
4141b4822a4SAttilio Rao 	TAILQ_FOREACH(obj, &fqueue, ag_fiter)
4151b4822a4SAttilio Rao 		if (!strcmp(obj->ag_name, name))
4161b4822a4SAttilio Rao 			return (obj);
4171b4822a4SAttilio Rao 	return (NULL);
4181b4822a4SAttilio Rao }
4191b4822a4SAttilio Rao 
4201b4822a4SAttilio Rao /*
4211b4822a4SAttilio Rao  * Return the number of object in the first-level aggregations queue.
4221b4822a4SAttilio Rao  */
4231b4822a4SAttilio Rao static int
fqueue_getall(const char * bin,char * temp,int asmf)4241b4822a4SAttilio Rao fqueue_getall(const char *bin, char *temp, int asmf)
4251b4822a4SAttilio Rao {
4261b4822a4SAttilio Rao 	char tmpf[MAXPATHLEN * 2 + 50];
4271b4822a4SAttilio Rao 	struct aggent *agg;
4281b4822a4SAttilio Rao 	uintptr_t start, end;
4291b4822a4SAttilio Rao 
4301b4822a4SAttilio Rao 	if (mkstemp(temp) == -1)
4311b4822a4SAttilio Rao 		return (-1);
4321b4822a4SAttilio Rao 	TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
4331b4822a4SAttilio Rao 		bzero(tmpf, sizeof(tmpf));
4341b4822a4SAttilio Rao 		start = agg->ag_ostart;
4351b4822a4SAttilio Rao 		end = agg->ag_oend;
4361b4822a4SAttilio Rao 
4371b4822a4SAttilio Rao 		if (asmf)
4381b4822a4SAttilio Rao 			snprintf(tmpf, sizeof(tmpf),
4391b4822a4SAttilio Rao 			    "objdump --start-address=%p "
4401b4822a4SAttilio Rao 			    "--stop-address=%p -d %s >> %s", (void *)start,
4411b4822a4SAttilio Rao 			    (void *)end, bin, temp);
4421b4822a4SAttilio Rao 		else
4431b4822a4SAttilio Rao 			snprintf(tmpf, sizeof(tmpf),
4441b4822a4SAttilio Rao 			    "objdump --start-address=%p "
4451b4822a4SAttilio Rao 			    "--stop-address=%p -S %s >> %s", (void *)start,
4461b4822a4SAttilio Rao 			    (void *)end, bin, temp);
4471b4822a4SAttilio Rao 		if (system(tmpf) != 0)
4481b4822a4SAttilio Rao 			return (-1);
4491b4822a4SAttilio Rao 	}
4501b4822a4SAttilio Rao 	return (0);
4511b4822a4SAttilio Rao }
4521b4822a4SAttilio Rao 
4531b4822a4SAttilio Rao /*
4541b4822a4SAttilio Rao  * Insert all the raw entries present in the general queue
4551b4822a4SAttilio Rao  * into the first-level aggregations queue.
4561b4822a4SAttilio Rao  */
4571b4822a4SAttilio Rao static int
fqueue_insertgen(void)4581b4822a4SAttilio Rao fqueue_insertgen(void)
4591b4822a4SAttilio Rao {
4601b4822a4SAttilio Rao 	struct entry *obj;
4611b4822a4SAttilio Rao 
4621b4822a4SAttilio Rao 	TAILQ_FOREACH(obj, &mainlst, en_iter)
4631b4822a4SAttilio Rao 		if (fqueue_insertent(obj) == -1)
4641b4822a4SAttilio Rao 			return (-1);
4651b4822a4SAttilio Rao 	return (0);
4661b4822a4SAttilio Rao }
4671b4822a4SAttilio Rao 
4681b4822a4SAttilio Rao /*
4691b4822a4SAttilio Rao  * Flush the raw entries general queue.
4701b4822a4SAttilio Rao  */
4711b4822a4SAttilio Rao static void
general_deleteall(void)47210bc3a7fSEd Schouten general_deleteall(void)
4731b4822a4SAttilio Rao {
4741b4822a4SAttilio Rao 	struct entry *obj;
4751b4822a4SAttilio Rao 
4761b4822a4SAttilio Rao 	while (TAILQ_EMPTY(&mainlst) == 0) {
4771b4822a4SAttilio Rao 		obj = TAILQ_FIRST(&mainlst);
4781b4822a4SAttilio Rao 		TAILQ_REMOVE(&mainlst, obj, en_iter);
4791b4822a4SAttilio Rao 	}
4801b4822a4SAttilio Rao }
4811b4822a4SAttilio Rao 
4821b4822a4SAttilio Rao /*
4831b4822a4SAttilio Rao  * Lookup a raw entry by the PC.
4841b4822a4SAttilio Rao  */
4851b4822a4SAttilio Rao static struct entry *
general_findent(uintptr_t pc)4861b4822a4SAttilio Rao general_findent(uintptr_t pc)
4871b4822a4SAttilio Rao {
4881b4822a4SAttilio Rao 	struct entry *obj;
4891b4822a4SAttilio Rao 
4901b4822a4SAttilio Rao 	TAILQ_FOREACH(obj, &mainlst, en_iter)
4911b4822a4SAttilio Rao 		if (obj->en_pc == pc)
4921b4822a4SAttilio Rao 			return (obj);
4931b4822a4SAttilio Rao 	return (NULL);
4941b4822a4SAttilio Rao }
4951b4822a4SAttilio Rao 
4961b4822a4SAttilio Rao /*
4971b4822a4SAttilio Rao  * Insert a new raw entry in the general queue.
4981b4822a4SAttilio Rao  */
4991b4822a4SAttilio Rao static void
general_insertent(struct entry * entry)5001b4822a4SAttilio Rao general_insertent(struct entry *entry)
5011b4822a4SAttilio Rao {
5021b4822a4SAttilio Rao 
5031b4822a4SAttilio Rao 	TAILQ_INSERT_TAIL(&mainlst, entry, en_iter);
5041b4822a4SAttilio Rao }
5051b4822a4SAttilio Rao 
5061b4822a4SAttilio Rao /*
507*de112993SJohn Baldwin  * Return a string either holding a percentage or the raw count value.
508*de112993SJohn Baldwin  */
509*de112993SJohn Baldwin static const char *
print_count(u_int nsamples,u_int totsamples)510*de112993SJohn Baldwin print_count(u_int nsamples, u_int totsamples)
511*de112993SJohn Baldwin {
512*de112993SJohn Baldwin 	static char buf[16];
513*de112993SJohn Baldwin 
514*de112993SJohn Baldwin 	switch (print_mode) {
515*de112993SJohn Baldwin 	case RAW:
516*de112993SJohn Baldwin 		snprintf(buf, sizeof(buf), "%u", nsamples);
517*de112993SJohn Baldwin 		break;
518*de112993SJohn Baldwin 	case BLOCK_PERCENT:
519*de112993SJohn Baldwin 		snprintf(buf, sizeof(buf), "%.2f%%", (float)nsamples * 100 /
520*de112993SJohn Baldwin 		    totsamples);
521*de112993SJohn Baldwin 		break;
522*de112993SJohn Baldwin 	case GLOBAL_PERCENT:
523*de112993SJohn Baldwin 		snprintf(buf, sizeof(buf), "%.2f%%", (float)nsamples * 100 /
524*de112993SJohn Baldwin 		    totalsamples);
525*de112993SJohn Baldwin 		break;
526*de112993SJohn Baldwin 	}
527*de112993SJohn Baldwin 	return (buf);
528*de112993SJohn Baldwin }
529*de112993SJohn Baldwin 
530*de112993SJohn Baldwin /*
5311b4822a4SAttilio Rao  * Printout the body of an "objdump -d" assembly function.
5321b4822a4SAttilio Rao  * It does simply stops when a new function is encountered,
5331b4822a4SAttilio Rao  * bringing back the file position in order to not mess up
5341b4822a4SAttilio Rao  * subsequent analysis.
5351b4822a4SAttilio Rao  * C lines and others not recognized are simply skipped.
5361b4822a4SAttilio Rao  */
5371b4822a4SAttilio Rao static void
general_printasm(FILE * fp,struct aggent * agg)5381b4822a4SAttilio Rao general_printasm(FILE *fp, struct aggent *agg)
5391b4822a4SAttilio Rao {
5401b4822a4SAttilio Rao 	char buffer[LNBUFF];
5411b4822a4SAttilio Rao 	struct entry *obj;
5421b4822a4SAttilio Rao 	int nbytes;
5431b4822a4SAttilio Rao 	void *ptr;
5441b4822a4SAttilio Rao 
5451b4822a4SAttilio Rao 	while (fgets(buffer, LNBUFF, fp) != NULL) {
5461b4822a4SAttilio Rao 		if ((nbytes = newfunction(buffer)) != 0) {
5471b4822a4SAttilio Rao 			fseek(fp, nbytes * -1, SEEK_CUR);
5481b4822a4SAttilio Rao 			break;
5491b4822a4SAttilio Rao 		}
5501b4822a4SAttilio Rao 		if (!isasminline(buffer))
5511b4822a4SAttilio Rao 			continue;
5521b4822a4SAttilio Rao 		if (sscanf(buffer, " %p:", &ptr) != 1)
5531b4822a4SAttilio Rao 			continue;
5541b4822a4SAttilio Rao 		obj = general_findent((uintptr_t)ptr);
5551b4822a4SAttilio Rao 		if (obj == NULL)
5561b4822a4SAttilio Rao 			printf("\t| %s", buffer);
5571b4822a4SAttilio Rao 		else
558*de112993SJohn Baldwin 			printf("%7s | %s",
559*de112993SJohn Baldwin 			    print_count(obj->en_nsamples, agg->ag_nsamples),
5601b4822a4SAttilio Rao 			    buffer);
5611b4822a4SAttilio Rao 	}
5621b4822a4SAttilio Rao }
5631b4822a4SAttilio Rao 
5641b4822a4SAttilio Rao /*
5651b4822a4SAttilio Rao  * Printout the body of an "objdump -S" function.
5661b4822a4SAttilio Rao  * It does simply stops when a new function is encountered,
5671b4822a4SAttilio Rao  * bringing back the file position in order to not mess up
5681b4822a4SAttilio Rao  * subsequent analysis.
5691b4822a4SAttilio Rao  * It expect from the starting to the end to find, always, valid blocks
5701b4822a4SAttilio Rao  * (see below for an explanation of the "block" concept).
5711b4822a4SAttilio Rao  */
5721b4822a4SAttilio Rao static int
general_printc(FILE * fp,struct aggent * agg)5731b4822a4SAttilio Rao general_printc(FILE *fp, struct aggent *agg)
5741b4822a4SAttilio Rao {
5751b4822a4SAttilio Rao 	char buffer[LNBUFF];
5761b4822a4SAttilio Rao 
5771b4822a4SAttilio Rao 	while (fgets(buffer, LNBUFF, fp) != NULL) {
5781b4822a4SAttilio Rao 		fseek(fp, strlen(buffer) * -1, SEEK_CUR);
5791b4822a4SAttilio Rao 		if (newfunction(buffer) != 0)
5801b4822a4SAttilio Rao 			break;
5811b4822a4SAttilio Rao 		if (printblock(fp, agg) == -1)
5821b4822a4SAttilio Rao 			return (-1);
5831b4822a4SAttilio Rao 	}
5841b4822a4SAttilio Rao 	return (0);
5851b4822a4SAttilio Rao }
5861b4822a4SAttilio Rao 
5871b4822a4SAttilio Rao /*
5881b4822a4SAttilio Rao  * Printout a single block inside an "objdump -S" function.
5891b4822a4SAttilio Rao  * The block is composed of a first part in C and subsequent translation
5901b4822a4SAttilio Rao  * in assembly.
5911b4822a4SAttilio Rao  * This code also operates a second-level aggregation packing together
5921b4822a4SAttilio Rao  * samples relative to PCs into a (lower bottom) block with their
5931b4822a4SAttilio Rao  * C (higher half) counterpart.
5941b4822a4SAttilio Rao  */
5951b4822a4SAttilio Rao static int
printblock(FILE * fp,struct aggent * agg)5961b4822a4SAttilio Rao printblock(FILE *fp, struct aggent *agg)
5971b4822a4SAttilio Rao {
5981b4822a4SAttilio Rao 	char buffer[LNBUFF];
5991b4822a4SAttilio Rao 	long lstart;
6001b4822a4SAttilio Rao 	struct entry *obj;
6011b4822a4SAttilio Rao 	u_int tnsamples;
6021b4822a4SAttilio Rao 	int done, nbytes, sentinel;
6031b4822a4SAttilio Rao 	void *ptr;
6041b4822a4SAttilio Rao 
6051b4822a4SAttilio Rao 	/*
6061b4822a4SAttilio Rao 	 * We expect the first thing of the block is C code, so simply give
6071b4822a4SAttilio Rao 	 * up if asm line is found.
6081b4822a4SAttilio Rao 	 */
6091b4822a4SAttilio Rao 	lstart = ftell(fp);
6101b4822a4SAttilio Rao 	sentinel = 0;
6111b4822a4SAttilio Rao 	for (;;) {
6121b4822a4SAttilio Rao 		if (fgets(buffer, LNBUFF, fp) == NULL)
6131b4822a4SAttilio Rao 			return (0);
6141b4822a4SAttilio Rao 		if (isasminline(buffer) != 0)
6151b4822a4SAttilio Rao 			break;
6161b4822a4SAttilio Rao 		sentinel = 1;
6171b4822a4SAttilio Rao 		nbytes = newfunction(buffer);
6181b4822a4SAttilio Rao 		if (nbytes != 0) {
6191b4822a4SAttilio Rao 			if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
6201b4822a4SAttilio Rao 				return (-1);
6211b4822a4SAttilio Rao 			return (0);
6221b4822a4SAttilio Rao 		}
6231b4822a4SAttilio Rao 	}
6241b4822a4SAttilio Rao 
6251b4822a4SAttilio Rao 	/*
6261b4822a4SAttilio Rao 	 * If the sentinel is not set, it means it did not match any
6271b4822a4SAttilio Rao 	 * "high half" for this code so simply give up.
6281b4822a4SAttilio Rao 	 * Operates the second-level aggregation.
6291b4822a4SAttilio Rao 	 */
6301b4822a4SAttilio Rao 	tnsamples = 0;
6311b4822a4SAttilio Rao 	do {
6321b4822a4SAttilio Rao 		if (sentinel == 0)
6331b4822a4SAttilio Rao 			return (-1);
6341b4822a4SAttilio Rao 		if (sscanf(buffer, " %p:", &ptr) != 1)
6351b4822a4SAttilio Rao 			return (-1);
6361b4822a4SAttilio Rao 		obj = general_findent((uintptr_t)ptr);
6371b4822a4SAttilio Rao 		if (obj != NULL)
6381b4822a4SAttilio Rao 			tnsamples += obj->en_nsamples;
6391b4822a4SAttilio Rao 	} while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0);
6401b4822a4SAttilio Rao 
6411b4822a4SAttilio Rao 	/* Rewind to the start of the block in order to start the printout. */
6421b4822a4SAttilio Rao 	if (fseek(fp, lstart, SEEK_SET) == -1)
6431b4822a4SAttilio Rao 		return (-1);
6441b4822a4SAttilio Rao 
6451b4822a4SAttilio Rao 	/* Again the high half of the block rappresenting the C part. */
6461b4822a4SAttilio Rao 	done = 0;
6471b4822a4SAttilio Rao 	while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) {
6481b4822a4SAttilio Rao 		if (tnsamples == 0 || done != 0)
6491b4822a4SAttilio Rao 			printf("\t| %s", buffer);
6501b4822a4SAttilio Rao 		else {
6511b4822a4SAttilio Rao 			done = 1;
652*de112993SJohn Baldwin 			printf("%7s | %s",
653*de112993SJohn Baldwin 			    print_count(tnsamples, agg->ag_nsamples), buffer);
6541b4822a4SAttilio Rao 		}
6551b4822a4SAttilio Rao 	}
6561b4822a4SAttilio Rao 
6571b4822a4SAttilio Rao 	/*
6581b4822a4SAttilio Rao 	 * Again the low half of the block rappresenting the asm
6591b4822a4SAttilio Rao 	 * translation part.
6601b4822a4SAttilio Rao 	 */
6611b4822a4SAttilio Rao 	for (;;) {
6621b4822a4SAttilio Rao 		if (fgets(buffer, LNBUFF, fp) == NULL)
6631b4822a4SAttilio Rao 			return (0);
6641b4822a4SAttilio Rao 		if (isasminline(buffer) == 0)
6651b4822a4SAttilio Rao 			break;
6661b4822a4SAttilio Rao 		nbytes = newfunction(buffer);
6671b4822a4SAttilio Rao 		if (nbytes != 0) {
6681b4822a4SAttilio Rao 			if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
6691b4822a4SAttilio Rao 				return (-1);
6701b4822a4SAttilio Rao 			return (0);
6711b4822a4SAttilio Rao 		}
6721b4822a4SAttilio Rao 	}
6731b4822a4SAttilio Rao 	if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1)
6741b4822a4SAttilio Rao 		return (-1);
6751b4822a4SAttilio Rao 	return (0);
6761b4822a4SAttilio Rao }
6771b4822a4SAttilio Rao 
6781b4822a4SAttilio Rao /*
6791b4822a4SAttilio Rao  * Helper printout functions.
6801b4822a4SAttilio Rao  */
6811b4822a4SAttilio Rao static void
usage(const char * progname)6821b4822a4SAttilio Rao usage(const char *progname)
6831b4822a4SAttilio Rao {
6841b4822a4SAttilio Rao 
6851b4822a4SAttilio Rao 	fprintf(stderr,
686*de112993SJohn Baldwin 	    "usage: %s [-a] [-h] [-k kfile] [-l lb] [-m mode] pmcraw.out binary\n",
6871b4822a4SAttilio Rao 	    progname);
6881b4822a4SAttilio Rao 	exit(EXIT_SUCCESS);
6891b4822a4SAttilio Rao }
6901b4822a4SAttilio Rao 
6911b4822a4SAttilio Rao int
main(int argc,char * argv[])6921b4822a4SAttilio Rao main(int argc, char *argv[])
6931b4822a4SAttilio Rao {
694b4cc0838SAndrew Gallatin 	char buffer[LNBUFF], fname[FNBUFF];
695b4cc0838SAndrew Gallatin 	char *tbfl, *tofl, *tmpdir;
6961b4822a4SAttilio Rao 	char tmpf[MAXPATHLEN * 2 + 50];
6971b4822a4SAttilio Rao 	float limit;
6981b4822a4SAttilio Rao 	char *bin, *exec, *kfile, *ofile;
6991b4822a4SAttilio Rao 	struct entry *obj;
7001b4822a4SAttilio Rao 	FILE *gfp, *bfp;
7011b4822a4SAttilio Rao 	void *ptr, *hstart, *hend;
7021b4822a4SAttilio Rao 	uintptr_t tmppc, ostart, oend;
7031b4822a4SAttilio Rao 	int cget, asmsrc;
7041b4822a4SAttilio Rao 
7051b4822a4SAttilio Rao 	exec = argv[0];
7061b4822a4SAttilio Rao 	ofile = NULL;
7071b4822a4SAttilio Rao 	bin = NULL;
7081b4822a4SAttilio Rao 	kfile = NULL;
7091b4822a4SAttilio Rao 	asmsrc = 0;
7101b4822a4SAttilio Rao 	limit = 0.5;
711*de112993SJohn Baldwin 	print_mode = BLOCK_PERCENT;
712*de112993SJohn Baldwin 	while ((cget = getopt(argc, argv, "ahl:m:k:")) != -1)
7131b4822a4SAttilio Rao 		switch(cget) {
7141b4822a4SAttilio Rao 		case 'a':
7151b4822a4SAttilio Rao 			asmsrc = 1;
7161b4822a4SAttilio Rao 			break;
7171b4822a4SAttilio Rao 		case 'k':
7181b4822a4SAttilio Rao 			kfile = optarg;
7191b4822a4SAttilio Rao 			break;
7201b4822a4SAttilio Rao 		case 'l':
7211b4822a4SAttilio Rao 			limit = (float)atof(optarg);
7221b4822a4SAttilio Rao 			break;
723*de112993SJohn Baldwin 		case 'm':
724*de112993SJohn Baldwin 			if (strcasecmp(optarg, "raw") == 0)
725*de112993SJohn Baldwin 				print_mode = RAW;
726*de112993SJohn Baldwin 			else if (strcasecmp(optarg, "global") == 0)
727*de112993SJohn Baldwin 				print_mode = GLOBAL_PERCENT;
728*de112993SJohn Baldwin 			else if (strcasecmp(optarg, "block") == 0)
729*de112993SJohn Baldwin 				print_mode = BLOCK_PERCENT;
730*de112993SJohn Baldwin 			else
731*de112993SJohn Baldwin 				errx(1, "Invalid mode %s", optarg);
732*de112993SJohn Baldwin 			break;
7331b4822a4SAttilio Rao 		case 'h':
7341b4822a4SAttilio Rao 		case '?':
7351b4822a4SAttilio Rao 		default:
7361b4822a4SAttilio Rao 			usage(exec);
7371b4822a4SAttilio Rao 		}
7381b4822a4SAttilio Rao 	argc -= optind;
7391b4822a4SAttilio Rao 	argv += optind;
7401b4822a4SAttilio Rao 	if (argc != 2)
7411b4822a4SAttilio Rao 		usage(exec);
7421b4822a4SAttilio Rao 	ofile = argv[0];
7431b4822a4SAttilio Rao 	bin = argv[1];
7441b4822a4SAttilio Rao 
7451b4822a4SAttilio Rao 	if (access(bin, R_OK | F_OK) == -1)
7461b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to locate the binary file\n",
7471b4822a4SAttilio Rao 		    exec);
7481b4822a4SAttilio Rao 	if (access(ofile, R_OK | F_OK) == -1)
7491b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to locate the pmcstat file\n",
7501b4822a4SAttilio Rao 		    exec);
7511b4822a4SAttilio Rao 	if (kfile != NULL && access(kfile, R_OK | F_OK) == -1)
7521b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to locate the kernel file\n",
7531b4822a4SAttilio Rao 		    exec);
7541b4822a4SAttilio Rao 
7551b4822a4SAttilio Rao 	bzero(tmpf, sizeof(tmpf));
756b4cc0838SAndrew Gallatin 	tmpdir = getenv("TMPDIR");
757b4cc0838SAndrew Gallatin 	if (tmpdir == NULL) {
758b4cc0838SAndrew Gallatin 		asprintf(&tbfl, "%s/%s", _PATH_TMP, TMPNAME);
759b4cc0838SAndrew Gallatin 		asprintf(&tofl, "%s/%s", _PATH_TMP, TMPNAME);
760b4cc0838SAndrew Gallatin 	} else {
761b4cc0838SAndrew Gallatin 		asprintf(&tbfl, "%s/%s", tmpdir, TMPNAME);
762b4cc0838SAndrew Gallatin 		asprintf(&tofl, "%s/%s", tmpdir, TMPNAME);
763b4cc0838SAndrew Gallatin 	}
764b4cc0838SAndrew Gallatin 	if (tofl == NULL || tbfl == NULL)
765b4cc0838SAndrew Gallatin 		FATAL(exec, "%s: Cannot create tempfile templates\n",
766b4cc0838SAndrew Gallatin 		    exec);
7671b4822a4SAttilio Rao 	if (mkstemp(tofl) == -1)
7681b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to create the tmp file\n",
7691b4822a4SAttilio Rao 		    exec);
7701b4822a4SAttilio Rao 	if (kfile != NULL)
7711b4822a4SAttilio Rao 		snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s",
7721b4822a4SAttilio Rao 		    kfile, ofile, tofl);
7731b4822a4SAttilio Rao 	else
7741b4822a4SAttilio Rao 		snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile,
7751b4822a4SAttilio Rao 		    tofl);
7761b4822a4SAttilio Rao 	if (system(tmpf) != 0)
7771b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to create the tmp file\n",
7781b4822a4SAttilio Rao 		    exec);
7791b4822a4SAttilio Rao 
7801b4822a4SAttilio Rao 	gfp = fopen(tofl, "r");
7811b4822a4SAttilio Rao 	if (gfp == NULL)
7821b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to open the map file\n",
7831b4822a4SAttilio Rao 		    exec);
7841b4822a4SAttilio Rao 
7851b4822a4SAttilio Rao 	/*
7861b4822a4SAttilio Rao 	 * Make the collection of raw entries from a pmcstat mapped file.
7871b4822a4SAttilio Rao 	 * The heuristic here wants strings in the form:
7881b4822a4SAttilio Rao 	 * "addr funcname startfaddr endfaddr".
7891b4822a4SAttilio Rao 	 */
7901b4822a4SAttilio Rao 	while (fgets(buffer, LNBUFF, gfp) != NULL) {
7911b4822a4SAttilio Rao 		if (isspace(buffer[0]))
7921b4822a4SAttilio Rao 			continue;
7931b4822a4SAttilio Rao 		if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname,
7941b4822a4SAttilio Rao 		    &hstart, &hend) != 4)
7951b4822a4SAttilio Rao 			FATAL(NULL,
7961b4822a4SAttilio Rao 			    "%s: Invalid scan of function in the map file\n",
7971b4822a4SAttilio Rao 			    exec);
7981b4822a4SAttilio Rao 		ostart = (uintptr_t)hstart;
7991b4822a4SAttilio Rao 		oend = (uintptr_t)hend;
8001b4822a4SAttilio Rao 		tmppc = (uintptr_t)ptr;
8011b4822a4SAttilio Rao 		totalsamples++;
8021b4822a4SAttilio Rao 		obj = general_findent(tmppc);
8031b4822a4SAttilio Rao 		if (obj != NULL) {
8041b4822a4SAttilio Rao 			entry_acqref(obj);
8051b4822a4SAttilio Rao 			continue;
8061b4822a4SAttilio Rao 		}
8071b4822a4SAttilio Rao 		obj = entry_create(fname, tmppc, ostart, oend);
8081b4822a4SAttilio Rao 		if (obj == NULL)
8091b4822a4SAttilio Rao 			FATAL(exec,
8101b4822a4SAttilio Rao 			    "%s: Impossible to create a new object\n", exec);
8111b4822a4SAttilio Rao 		general_insertent(obj);
8121b4822a4SAttilio Rao 	}
8131b4822a4SAttilio Rao 	if (fclose(gfp) == EOF)
8141b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to close the filedesc\n",
8151b4822a4SAttilio Rao 		    exec);
8161b4822a4SAttilio Rao 	if (remove(tofl) == -1)
8171b4822a4SAttilio Rao                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
8181b4822a4SAttilio Rao                     exec);
8191b4822a4SAttilio Rao 
8201b4822a4SAttilio Rao 	/*
8211b4822a4SAttilio Rao 	 * Remove the loose end objects and feed the first-level aggregation
8221b4822a4SAttilio Rao 	 * queue.
8231b4822a4SAttilio Rao 	 */
8241b4822a4SAttilio Rao 	if (fqueue_insertgen() == -1)
8251b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to generate an analysis\n",
8261b4822a4SAttilio Rao 		    exec);
8271b4822a4SAttilio Rao 	fqueue_compact(limit);
8281b4822a4SAttilio Rao 	if (fqueue_getall(bin, tbfl, asmsrc) == -1)
8291b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to create the tmp file\n",
8301b4822a4SAttilio Rao 		    exec);
8311b4822a4SAttilio Rao 
8321b4822a4SAttilio Rao 	bfp = fopen(tbfl, "r");
8331b4822a4SAttilio Rao 	if (bfp == NULL)
8341b4822a4SAttilio Rao 		FATAL(exec, "%s: Impossible to open the binary file\n",
8351b4822a4SAttilio Rao 		    exec);
8361b4822a4SAttilio Rao 
8371b4822a4SAttilio Rao 	if (asmsrc != 0)
8381b4822a4SAttilio Rao 		asmparse(bfp);
8391b4822a4SAttilio Rao 	else if (cparse(bfp) == -1)
8401b4822a4SAttilio Rao 		FATAL(NULL, "%s: Invalid format for the C file\n", exec);
8411b4822a4SAttilio Rao 	if (fclose(bfp) == EOF)
8421b4822a4SAttilio Rao                 FATAL(exec, "%s: Impossible to close the filedesc\n",
8431b4822a4SAttilio Rao                     exec);
8441b4822a4SAttilio Rao 	if (remove(tbfl) == -1)
8451b4822a4SAttilio Rao                 FATAL(exec, "%s: Impossible to remove the tmpfile\n",
8461b4822a4SAttilio Rao                     exec);
8471b4822a4SAttilio Rao 	return (0);
8481b4822a4SAttilio Rao }
849