xref: /dflybsd-src/usr.bin/pctrack/pctrack.c (revision c141c298f124f382c1ba8e2bbf4ae5116c8ee4bf)
1402351a0SMatthew Dillon /*-
2402351a0SMatthew Dillon  * Copyright (c) 2002 Jake Burkholder
3402351a0SMatthew Dillon  * Copyright (c) 2004 Robert Watson
4402351a0SMatthew Dillon  * All rights reserved.
5402351a0SMatthew Dillon  *
6402351a0SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
7402351a0SMatthew Dillon  * modification, are permitted provided that the following conditions
8402351a0SMatthew Dillon  * are met:
9402351a0SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
10402351a0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
11402351a0SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
12402351a0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
13402351a0SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
14402351a0SMatthew Dillon  *
15402351a0SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16402351a0SMatthew Dillon  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17402351a0SMatthew Dillon  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18402351a0SMatthew Dillon  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19402351a0SMatthew Dillon  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20402351a0SMatthew Dillon  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21402351a0SMatthew Dillon  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22402351a0SMatthew Dillon  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23402351a0SMatthew Dillon  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24402351a0SMatthew Dillon  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25402351a0SMatthew Dillon  * SUCH DAMAGE.
26402351a0SMatthew Dillon  *
278e1c6f81SMatthias Schmidt  * $DragonFly: src/usr.bin/pctrack/pctrack.c,v 1.2 2008/09/02 11:50:46 matthias Exp $
28402351a0SMatthew Dillon  */
29402351a0SMatthew Dillon 
30e0ecab34SMatthew Dillon #include <sys/kinfo.h>
31402351a0SMatthew Dillon #include <sys/types.h>
32402351a0SMatthew Dillon #include <sys/ktr.h>
33402351a0SMatthew Dillon #include <sys/mman.h>
34402351a0SMatthew Dillon #include <sys/stat.h>
35402351a0SMatthew Dillon #include <sys/queue.h>
36402351a0SMatthew Dillon 
37402351a0SMatthew Dillon #include <err.h>
38402351a0SMatthew Dillon #include <fcntl.h>
39402351a0SMatthew Dillon #include <kvm.h>
40402351a0SMatthew Dillon #include <limits.h>
41402351a0SMatthew Dillon #include <nlist.h>
42402351a0SMatthew Dillon #include <stdint.h>
43402351a0SMatthew Dillon #include <stdio.h>
44402351a0SMatthew Dillon #include <stdlib.h>
45402351a0SMatthew Dillon #include <string.h>
46402351a0SMatthew Dillon #include <stddef.h>
47402351a0SMatthew Dillon #include <unistd.h>
48402351a0SMatthew Dillon 
49402351a0SMatthew Dillon #define	SBUFLEN	128
50402351a0SMatthew Dillon 
51402351a0SMatthew Dillon static void usage(void);
52402351a0SMatthew Dillon static void do_output(int, int, struct kinfo_pcheader *, struct kinfo_pctrack *, int);
53402351a0SMatthew Dillon static void read_symbols(const char *);
54402351a0SMatthew Dillon static const char *address_to_symbol(void *);
55402351a0SMatthew Dillon 
56402351a0SMatthew Dillon static struct nlist nl[] = {
57ccdab305SSascha Wildner 	{ .n_name = "_ncpus" },
58ccdab305SSascha Wildner 	{ .n_name = "_cputime_pcheader" },
59ccdab305SSascha Wildner 	{ .n_name = "_cputime_pctrack" },
60ccdab305SSascha Wildner 	{ .n_name = NULL }
61402351a0SMatthew Dillon };
62402351a0SMatthew Dillon 
63402351a0SMatthew Dillon static char corefile[PATH_MAX];
64402351a0SMatthew Dillon static char execfile[PATH_MAX];
65402351a0SMatthew Dillon static char errbuf[_POSIX2_LINE_MAX];
66402351a0SMatthew Dillon 
67402351a0SMatthew Dillon static int sflag;
68402351a0SMatthew Dillon static int iflag;
69402351a0SMatthew Dillon static int nflag;
70402351a0SMatthew Dillon static int fflag;
71402351a0SMatthew Dillon static int cflag = -1;
72402351a0SMatthew Dillon static int Nflag;
73402351a0SMatthew Dillon static int Mflag;
74402351a0SMatthew Dillon 
75402351a0SMatthew Dillon /*
76402351a0SMatthew Dillon  * Reads the cputime_pctrack[] structure from the kernel and displays
77402351a0SMatthew Dillon  * the results in a human readable format.
78402351a0SMatthew Dillon  */
79402351a0SMatthew Dillon int
main(int ac,char ** av)80402351a0SMatthew Dillon main(int ac, char **av)
81402351a0SMatthew Dillon {
82402351a0SMatthew Dillon 	struct kinfo_pcheader pchead;
83402351a0SMatthew Dillon 	struct kinfo_pctrack pctrack;
84402351a0SMatthew Dillon 	kvm_t *kd;
85402351a0SMatthew Dillon 	int ntrack;
86402351a0SMatthew Dillon 	int ncpus;
87402351a0SMatthew Dillon 	int cpu;
88402351a0SMatthew Dillon 	int repeat;
89402351a0SMatthew Dillon 	int c;
90402351a0SMatthew Dillon 
91402351a0SMatthew Dillon 	/*
92402351a0SMatthew Dillon 	 * Parse commandline arguments.
93402351a0SMatthew Dillon 	 */
94402351a0SMatthew Dillon 	while ((c = getopt(ac, av, "nsifc:N:M:")) != -1) {
95402351a0SMatthew Dillon 		switch (c) {
96402351a0SMatthew Dillon 		case 'N':
97402351a0SMatthew Dillon 			if (strlcpy(execfile, optarg, sizeof(execfile))
98402351a0SMatthew Dillon 			    >= sizeof(execfile))
99402351a0SMatthew Dillon 				errx(1, "%s: File name too long", optarg);
100402351a0SMatthew Dillon 			Nflag = 1;
101402351a0SMatthew Dillon 			break;
102402351a0SMatthew Dillon 		case 'M':
103402351a0SMatthew Dillon 			if (strlcpy(corefile, optarg, sizeof(corefile))
104402351a0SMatthew Dillon 			    >= sizeof(corefile))
105402351a0SMatthew Dillon 				errx(1, "%s: File name too long", optarg);
106402351a0SMatthew Dillon 			Mflag = 1;
107402351a0SMatthew Dillon 			break;
108402351a0SMatthew Dillon 		case 'c':
109402351a0SMatthew Dillon 			cflag = strtol(optarg, NULL, 0);
110402351a0SMatthew Dillon 			break;
111402351a0SMatthew Dillon 		case 's':
112402351a0SMatthew Dillon 			sflag = 1;
113402351a0SMatthew Dillon 			break;
114402351a0SMatthew Dillon 		case 'i':
115402351a0SMatthew Dillon 			iflag = 1;
116402351a0SMatthew Dillon 			break;
117402351a0SMatthew Dillon 		case 'n':
118402351a0SMatthew Dillon 			nflag = 1;
119402351a0SMatthew Dillon 			break;
120402351a0SMatthew Dillon 		case 'f':
121402351a0SMatthew Dillon 			fflag = 1;
122402351a0SMatthew Dillon 			break;
123402351a0SMatthew Dillon 		default:
124402351a0SMatthew Dillon 			usage();
125402351a0SMatthew Dillon 		}
126402351a0SMatthew Dillon 	}
127402351a0SMatthew Dillon 
128402351a0SMatthew Dillon 	if (sflag == 0 && iflag == 0) {
129402351a0SMatthew Dillon 		sflag = 1;
130402351a0SMatthew Dillon 		iflag = 1;
131402351a0SMatthew Dillon 	}
132402351a0SMatthew Dillon 	if (nflag == 0)
133402351a0SMatthew Dillon 		read_symbols(Nflag ? execfile : NULL);
134402351a0SMatthew Dillon 
135402351a0SMatthew Dillon 	if (fflag && (cflag < 0 || sflag + iflag > 1)) {
136402351a0SMatthew Dillon 		fprintf(stderr, "-f can only be specified with a particular cpu and just one of -i or -s\n");
137402351a0SMatthew Dillon 		exit(1);
138402351a0SMatthew Dillon 	}
139402351a0SMatthew Dillon 
140402351a0SMatthew Dillon 	ac -= optind;
141402351a0SMatthew Dillon 	av += optind;
142402351a0SMatthew Dillon 	if (ac != 0 && strtod(av[0], NULL) > 0.0) {
143402351a0SMatthew Dillon 		repeat = (int)(strtod(av[0], NULL) * 1000000.0);
144402351a0SMatthew Dillon 		++av;
145402351a0SMatthew Dillon 		--ac;
146402351a0SMatthew Dillon 	} else if (fflag) {
147402351a0SMatthew Dillon 		repeat = 1000000 / 10;
148402351a0SMatthew Dillon 	} else {
149402351a0SMatthew Dillon 		repeat = 0;
150402351a0SMatthew Dillon 	}
151402351a0SMatthew Dillon 	if (ac != 0)
152402351a0SMatthew Dillon 		usage();
153402351a0SMatthew Dillon 
154402351a0SMatthew Dillon 	/*
155402351a0SMatthew Dillon 	 * Open our execfile and corefile, resolve needed symbols and read in
156402351a0SMatthew Dillon 	 * the trace buffer.
157402351a0SMatthew Dillon 	 */
158402351a0SMatthew Dillon 	if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
159402351a0SMatthew Dillon 	    Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
160402351a0SMatthew Dillon 		errx(1, "%s", errbuf);
161402351a0SMatthew Dillon 	if (kvm_nlist(kd, nl) != 0)
162402351a0SMatthew Dillon 		errx(1, "%s", kvm_geterr(kd));
163402351a0SMatthew Dillon 
164402351a0SMatthew Dillon 	if (kvm_read(kd, nl[0].n_value, &ncpus, sizeof(ncpus)) == -1)
165402351a0SMatthew Dillon 		errx(1, "%s", kvm_geterr(kd));
166402351a0SMatthew Dillon 	if (kvm_read(kd, nl[1].n_value, &pchead, sizeof(pchead)) == -1)
167402351a0SMatthew Dillon 		errx(1, "%s", kvm_geterr(kd));
168402351a0SMatthew Dillon 
169402351a0SMatthew Dillon again:
170402351a0SMatthew Dillon 	for (cpu = 0; cpu < ncpus; ++cpu) {
171402351a0SMatthew Dillon 		for (ntrack = 0; ntrack < pchead.pc_ntrack; ++ntrack) {
172402351a0SMatthew Dillon 			int offset;
173402351a0SMatthew Dillon 
174402351a0SMatthew Dillon 			if (ntrack == PCTRACK_SYS && sflag == 0)
175402351a0SMatthew Dillon 				continue;
176402351a0SMatthew Dillon 			if (ntrack == PCTRACK_INT && iflag == 0)
177402351a0SMatthew Dillon 				continue;
178402351a0SMatthew Dillon 			if (cflag >= 0 && cflag != cpu)
179402351a0SMatthew Dillon 				continue;
180402351a0SMatthew Dillon 
181402351a0SMatthew Dillon 			offset = offsetof(struct kinfo_pctrack,
182402351a0SMatthew Dillon 					  pc_array[pchead.pc_arysize]);
183402351a0SMatthew Dillon 			offset = (offset * pchead.pc_ntrack * cpu) +
184402351a0SMatthew Dillon 				 (offset * ntrack);
185402351a0SMatthew Dillon 			if (kvm_read(kd, nl[2].n_value + offset, &pctrack, sizeof(pctrack)) < 0)
186402351a0SMatthew Dillon 				errx(1, "%s", kvm_geterr(kd));
187402351a0SMatthew Dillon 
188402351a0SMatthew Dillon 			printf("CPU %d %s:\n", cpu,
189402351a0SMatthew Dillon 				(ntrack == PCTRACK_SYS) ? "SYSTEM" :
190402351a0SMatthew Dillon 				(ntrack == PCTRACK_INT) ? "INTERRUPT" : "?"
191402351a0SMatthew Dillon 			);
192402351a0SMatthew Dillon 
193402351a0SMatthew Dillon 			do_output(cpu, ntrack, &pchead, &pctrack, pctrack.pc_index - pchead.pc_arysize);
194402351a0SMatthew Dillon 			while (fflag) {
195402351a0SMatthew Dillon 				usleep(repeat);
196402351a0SMatthew Dillon 				int last_index = pctrack.pc_index;
197402351a0SMatthew Dillon 				kvm_read(kd, nl[2].n_value + offset, &pctrack,
198402351a0SMatthew Dillon 					 sizeof(pctrack));
199402351a0SMatthew Dillon 				do_output(cpu, ntrack, &pchead, &pctrack, last_index);
200402351a0SMatthew Dillon 			}
201402351a0SMatthew Dillon 		}
202402351a0SMatthew Dillon 	}
203402351a0SMatthew Dillon 	if (repeat) {
204402351a0SMatthew Dillon 		usleep(repeat);
205402351a0SMatthew Dillon 		goto again;
206402351a0SMatthew Dillon 	}
207402351a0SMatthew Dillon 	return(0);
208402351a0SMatthew Dillon }
209402351a0SMatthew Dillon 
210402351a0SMatthew Dillon static void
do_output(int cpu __unused,int track __unused,struct kinfo_pcheader * pchead,struct kinfo_pctrack * pctrack,int base_index)211ccdab305SSascha Wildner do_output(int cpu __unused, int track __unused, struct kinfo_pcheader *pchead, struct kinfo_pctrack *pctrack, int base_index)
212402351a0SMatthew Dillon {
213402351a0SMatthew Dillon 	int i;
214402351a0SMatthew Dillon 
215402351a0SMatthew Dillon 	i = base_index;
216402351a0SMatthew Dillon 	if (pctrack->pc_index - base_index > pchead->pc_arysize) {
217402351a0SMatthew Dillon 		i = pctrack->pc_index - pchead->pc_arysize;
218402351a0SMatthew Dillon 	}
219402351a0SMatthew Dillon 	while (i < pctrack->pc_index) {
220402351a0SMatthew Dillon 		void *data = pctrack->pc_array[i & (pchead->pc_arysize - 1)];
221402351a0SMatthew Dillon 		if (nflag)
222402351a0SMatthew Dillon 			printf("\t%p\n", data);
223402351a0SMatthew Dillon 		else
224402351a0SMatthew Dillon 			printf("\t%s\n", address_to_symbol(data));
225402351a0SMatthew Dillon 		++i;
226402351a0SMatthew Dillon 	}
227402351a0SMatthew Dillon }
228402351a0SMatthew Dillon 
229402351a0SMatthew Dillon struct symdata {
230402351a0SMatthew Dillon 	TAILQ_ENTRY(symdata) link;
231402351a0SMatthew Dillon 	const char *symname;
232402351a0SMatthew Dillon 	char *symaddr;
233402351a0SMatthew Dillon 	char symtype;
234402351a0SMatthew Dillon };
235402351a0SMatthew Dillon 
236402351a0SMatthew Dillon static TAILQ_HEAD(symlist, symdata) symlist;
237402351a0SMatthew Dillon static struct symdata *symcache;
238402351a0SMatthew Dillon static char *symbegin;
239402351a0SMatthew Dillon static char *symend;
240402351a0SMatthew Dillon 
241ccdab305SSascha Wildner static void
read_symbols(const char * file)242ccdab305SSascha Wildner read_symbols(const char *file)
243402351a0SMatthew Dillon {
244402351a0SMatthew Dillon 	char buf[256];
245402351a0SMatthew Dillon 	char cmd[256];
246d9c15c02SSascha Wildner 	size_t buflen = sizeof(buf);
247402351a0SMatthew Dillon 	FILE *fp;
248402351a0SMatthew Dillon 	struct symdata *sym;
249402351a0SMatthew Dillon 	char *s1;
250402351a0SMatthew Dillon 	char *s2;
251402351a0SMatthew Dillon 	char *s3;
252402351a0SMatthew Dillon 
253402351a0SMatthew Dillon 	TAILQ_INIT(&symlist);
254402351a0SMatthew Dillon 
255ccdab305SSascha Wildner 	if (file == NULL) {
256402351a0SMatthew Dillon 		if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
257ccdab305SSascha Wildner 			file = "/boot/kernel";
258402351a0SMatthew Dillon 		else
259ccdab305SSascha Wildner 			file = buf;
260402351a0SMatthew Dillon 	}
261*c141c298SMatthew Dillon 
262*c141c298SMatthew Dillon 	symend = NULL;
263*c141c298SMatthew Dillon 	symbegin = (void *)(intptr_t)-1;
264*c141c298SMatthew Dillon 
265ccdab305SSascha Wildner 	snprintf(cmd, sizeof(cmd), "nm -n %s", file);
266402351a0SMatthew Dillon 	if ((fp = popen(cmd, "r")) != NULL) {
267402351a0SMatthew Dillon 		while (fgets(buf, sizeof(buf), fp) != NULL) {
268402351a0SMatthew Dillon 		    s1 = strtok(buf, " \t\n");
269402351a0SMatthew Dillon 		    s2 = strtok(NULL, " \t\n");
270402351a0SMatthew Dillon 		    s3 = strtok(NULL, " \t\n");
271402351a0SMatthew Dillon 		    if (s1 && s2 && s3) {
272402351a0SMatthew Dillon 			sym = malloc(sizeof(struct symdata));
273402351a0SMatthew Dillon 			sym->symaddr = (char *)strtoul(s1, NULL, 16);
274402351a0SMatthew Dillon 			sym->symtype = s2[0];
275402351a0SMatthew Dillon 			sym->symname = strdup(s3);
276*c141c298SMatthew Dillon 			if (s2[0] == 't' || s2[0] == 'T') {
277*c141c298SMatthew Dillon 				if (symbegin > sym->symaddr)
278402351a0SMatthew Dillon 					symbegin = sym->symaddr;
279*c141c298SMatthew Dillon 				if (symend < sym->symaddr)
280402351a0SMatthew Dillon 					symend = sym->symaddr;
281*c141c298SMatthew Dillon 			}
282402351a0SMatthew Dillon 			TAILQ_INSERT_TAIL(&symlist, sym, link);
283402351a0SMatthew Dillon 		    }
284402351a0SMatthew Dillon 		}
285402351a0SMatthew Dillon 		pclose(fp);
286402351a0SMatthew Dillon 	}
287402351a0SMatthew Dillon 	symcache = TAILQ_FIRST(&symlist);
288402351a0SMatthew Dillon }
289402351a0SMatthew Dillon 
290ccdab305SSascha Wildner static const char *
address_to_symbol(void * kptr)291402351a0SMatthew Dillon address_to_symbol(void *kptr)
292402351a0SMatthew Dillon {
293402351a0SMatthew Dillon 	static char buf[64];
294402351a0SMatthew Dillon 
295402351a0SMatthew Dillon 	if (symcache == NULL ||
296402351a0SMatthew Dillon 	   (char *)kptr < symbegin || (char *)kptr >= symend
297402351a0SMatthew Dillon 	) {
298402351a0SMatthew Dillon 		snprintf(buf, sizeof(buf), "%p", kptr);
299402351a0SMatthew Dillon 		return(buf);
300402351a0SMatthew Dillon 	}
301402351a0SMatthew Dillon 	while ((char *)symcache->symaddr < (char *)kptr) {
302402351a0SMatthew Dillon 		if (TAILQ_NEXT(symcache, link) == NULL)
303402351a0SMatthew Dillon 			break;
304402351a0SMatthew Dillon 		symcache = TAILQ_NEXT(symcache, link);
305402351a0SMatthew Dillon 	}
306402351a0SMatthew Dillon 	while ((char *)symcache->symaddr > (char *)kptr) {
307402351a0SMatthew Dillon 		if (symcache != TAILQ_FIRST(&symlist))
308402351a0SMatthew Dillon 			symcache = TAILQ_PREV(symcache, symlist, link);
309402351a0SMatthew Dillon 	}
310402351a0SMatthew Dillon 	snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
311402351a0SMatthew Dillon 		(int)((char *)kptr - symcache->symaddr));
312402351a0SMatthew Dillon 	return(buf);
313402351a0SMatthew Dillon }
314402351a0SMatthew Dillon 
315402351a0SMatthew Dillon static void
usage(void)316402351a0SMatthew Dillon usage(void)
317402351a0SMatthew Dillon {
318402351a0SMatthew Dillon 	fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] "
319402351a0SMatthew Dillon 			"[-M corefile]\n");
320402351a0SMatthew Dillon 	exit(1);
321402351a0SMatthew Dillon }
322