xref: /dragonfly/usr.bin/pctrack/pctrack.c (revision 0bb9290e)
1 /*-
2  * Copyright (c) 2002 Jake Burkholder
3  * Copyright (c) 2004 Robert Watson
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $DragonFly: src/usr.bin/pctrack/pctrack.c,v 1.1 2006/06/08 18:48:30 dillon Exp $
28  */
29 
30 #include <sys/cdefs.h>
31 
32 #include <sys/types.h>
33 #include <sys/ktr.h>
34 #include <sys/kinfo.h>
35 #include <sys/mman.h>
36 #include <sys/stat.h>
37 #include <sys/queue.h>
38 
39 #include <err.h>
40 #include <fcntl.h>
41 #include <kvm.h>
42 #include <limits.h>
43 #include <nlist.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <stddef.h>
49 #include <unistd.h>
50 
51 #define	SBUFLEN	128
52 
53 extern char *optarg;
54 extern int optind;
55 
56 static void usage(void);
57 static void do_output(int, int, struct kinfo_pcheader *, struct kinfo_pctrack *, int);
58 static void read_symbols(const char *);
59 static const char *address_to_symbol(void *);
60 
61 static struct nlist nl[] = {
62 	{ "_ncpus" },
63 	{ "_cputime_pcheader" },
64 	{ "_cputime_pctrack" },
65 	{ NULL }
66 };
67 
68 static char corefile[PATH_MAX];
69 static char execfile[PATH_MAX];
70 static char errbuf[_POSIX2_LINE_MAX];
71 
72 static int sflag;
73 static int iflag;
74 static int nflag;
75 static int fflag;
76 static int cflag = -1;
77 static int Nflag;
78 static int Mflag;
79 
80 /*
81  * Reads the cputime_pctrack[] structure from the kernel and displays
82  * the results in a human readable format.
83  */
84 int
85 main(int ac, char **av)
86 {
87 	struct kinfo_pcheader pchead;
88 	struct kinfo_pctrack pctrack;
89 	kvm_t *kd;
90 	int ntrack;
91 	int ncpus;
92 	int cpu;
93 	int repeat;
94 	int c;
95 
96 	/*
97 	 * Parse commandline arguments.
98 	 */
99 	while ((c = getopt(ac, av, "nsifc:N:M:")) != -1) {
100 		switch (c) {
101 		case 'N':
102 			if (strlcpy(execfile, optarg, sizeof(execfile))
103 			    >= sizeof(execfile))
104 				errx(1, "%s: File name too long", optarg);
105 			Nflag = 1;
106 			break;
107 		case 'M':
108 			if (strlcpy(corefile, optarg, sizeof(corefile))
109 			    >= sizeof(corefile))
110 				errx(1, "%s: File name too long", optarg);
111 			Mflag = 1;
112 			break;
113 		case 'c':
114 			cflag = strtol(optarg, NULL, 0);
115 			break;
116 		case 's':
117 			sflag = 1;
118 			break;
119 		case 'i':
120 			iflag = 1;
121 			break;
122 		case 'n':
123 			nflag = 1;
124 			break;
125 		case 'f':
126 			fflag = 1;
127 			break;
128 		default:
129 			usage();
130 		}
131 	}
132 
133 	if (sflag == 0 && iflag == 0) {
134 		sflag = 1;
135 		iflag = 1;
136 	}
137 	if (nflag == 0)
138 		read_symbols(Nflag ? execfile : NULL);
139 
140 	if (fflag && (cflag < 0 || sflag + iflag > 1)) {
141 		fprintf(stderr, "-f can only be specified with a particular cpu and just one of -i or -s\n");
142 		exit(1);
143 	}
144 
145 	ac -= optind;
146 	av += optind;
147 	if (ac != 0 && strtod(av[0], NULL) > 0.0) {
148 		repeat = (int)(strtod(av[0], NULL) * 1000000.0);
149 		++av;
150 		--ac;
151 	} else if (fflag) {
152 		repeat = 1000000 / 10;
153 	} else {
154 		repeat = 0;
155 	}
156 	if (ac != 0)
157 		usage();
158 
159 	/*
160 	 * Open our execfile and corefile, resolve needed symbols and read in
161 	 * the trace buffer.
162 	 */
163 	if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
164 	    Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
165 		errx(1, "%s", errbuf);
166 	if (kvm_nlist(kd, nl) != 0)
167 		errx(1, "%s", kvm_geterr(kd));
168 
169 	if (kvm_read(kd, nl[0].n_value, &ncpus, sizeof(ncpus)) == -1)
170 		errx(1, "%s", kvm_geterr(kd));
171 	if (kvm_read(kd, nl[1].n_value, &pchead, sizeof(pchead)) == -1)
172 		errx(1, "%s", kvm_geterr(kd));
173 
174 again:
175 	for (cpu = 0; cpu < ncpus; ++cpu) {
176 		for (ntrack = 0; ntrack < pchead.pc_ntrack; ++ntrack) {
177 			int offset;
178 
179 			if (ntrack == PCTRACK_SYS && sflag == 0)
180 				continue;
181 			if (ntrack == PCTRACK_INT && iflag == 0)
182 				continue;
183 			if (cflag >= 0 && cflag != cpu)
184 				continue;
185 
186 			offset = offsetof(struct kinfo_pctrack,
187 					  pc_array[pchead.pc_arysize]);
188 			offset = (offset * pchead.pc_ntrack * cpu) +
189 				 (offset * ntrack);
190 			if (kvm_read(kd, nl[2].n_value + offset, &pctrack, sizeof(pctrack)) < 0)
191 				errx(1, "%s", kvm_geterr(kd));
192 
193 			printf("CPU %d %s:\n", cpu,
194 				(ntrack == PCTRACK_SYS) ? "SYSTEM" :
195 				(ntrack == PCTRACK_INT) ? "INTERRUPT" : "?"
196 			);
197 
198 			do_output(cpu, ntrack, &pchead, &pctrack, pctrack.pc_index - pchead.pc_arysize);
199 			while (fflag) {
200 				usleep(repeat);
201 				int last_index = pctrack.pc_index;
202 				kvm_read(kd, nl[2].n_value + offset, &pctrack,
203 					 sizeof(pctrack));
204 				do_output(cpu, ntrack, &pchead, &pctrack, last_index);
205 			}
206 		}
207 	}
208 	if (repeat) {
209 		usleep(repeat);
210 		goto again;
211 	}
212 	return(0);
213 }
214 
215 static void
216 do_output(int cpu, int track, struct kinfo_pcheader *pchead, struct kinfo_pctrack *pctrack, int base_index)
217 {
218 	int i;
219 
220 	i = base_index;
221 	if (pctrack->pc_index - base_index > pchead->pc_arysize) {
222 		i = pctrack->pc_index - pchead->pc_arysize;
223 	}
224 	while (i < pctrack->pc_index) {
225 		void *data = pctrack->pc_array[i & (pchead->pc_arysize - 1)];
226 		if (nflag)
227 			printf("\t%p\n", data);
228 		else
229 			printf("\t%s\n", address_to_symbol(data));
230 		++i;
231 	}
232 }
233 
234 struct symdata {
235 	TAILQ_ENTRY(symdata) link;
236 	const char *symname;
237 	char *symaddr;
238 	char symtype;
239 };
240 
241 static TAILQ_HEAD(symlist, symdata) symlist;
242 static struct symdata *symcache;
243 static char *symbegin;
244 static char *symend;
245 
246 static
247 void
248 read_symbols(const char *execfile)
249 {
250 	char buf[256];
251 	char cmd[256];
252 	int buflen = sizeof(buf);
253 	FILE *fp;
254 	struct symdata *sym;
255 	char *s1;
256 	char *s2;
257 	char *s3;
258 
259 	TAILQ_INIT(&symlist);
260 
261 	if (execfile == NULL) {
262 		if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
263 			execfile = "/kernel";
264 		else
265 			execfile = buf;
266 	}
267 	snprintf(cmd, sizeof(cmd), "nm -n %s", execfile);
268 	if ((fp = popen(cmd, "r")) != NULL) {
269 		while (fgets(buf, sizeof(buf), fp) != NULL) {
270 		    s1 = strtok(buf, " \t\n");
271 		    s2 = strtok(NULL, " \t\n");
272 		    s3 = strtok(NULL, " \t\n");
273 		    if (s1 && s2 && s3) {
274 			sym = malloc(sizeof(struct symdata));
275 			sym->symaddr = (char *)strtoul(s1, NULL, 16);
276 			sym->symtype = s2[0];
277 			sym->symname = strdup(s3);
278 			if (strcmp(s3, "kernbase") == 0)
279 				symbegin = sym->symaddr;
280 			if (strcmp(s3, "end") == 0)
281 				symend = sym->symaddr;
282 			TAILQ_INSERT_TAIL(&symlist, sym, link);
283 		    }
284 		}
285 		pclose(fp);
286 	}
287 	symcache = TAILQ_FIRST(&symlist);
288 }
289 
290 static
291 const char *
292 address_to_symbol(void *kptr)
293 {
294 	static char buf[64];
295 
296 	if (symcache == NULL ||
297 	   (char *)kptr < symbegin || (char *)kptr >= symend
298 	) {
299 		snprintf(buf, sizeof(buf), "%p", kptr);
300 		return(buf);
301 	}
302 	while ((char *)symcache->symaddr < (char *)kptr) {
303 		if (TAILQ_NEXT(symcache, link) == NULL)
304 			break;
305 		symcache = TAILQ_NEXT(symcache, link);
306 	}
307 	while ((char *)symcache->symaddr > (char *)kptr) {
308 		if (symcache != TAILQ_FIRST(&symlist))
309 			symcache = TAILQ_PREV(symcache, symlist, link);
310 	}
311 	snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
312 		(int)((char *)kptr - symcache->symaddr));
313 	return(buf);
314 }
315 
316 static void
317 usage(void)
318 {
319 	fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] "
320 			"[-M corefile]\n");
321 	exit(1);
322 }
323