xref: /dragonfly/usr.bin/pctrack/pctrack.c (revision 409b4c59)
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.2 2008/09/02 11:50:46 matthias 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 static void usage(void);
54 static void do_output(int, int, struct kinfo_pcheader *, struct kinfo_pctrack *, int);
55 static void read_symbols(const char *);
56 static const char *address_to_symbol(void *);
57 
58 static struct nlist nl[] = {
59 	{ .n_name = "_ncpus" },
60 	{ .n_name = "_cputime_pcheader" },
61 	{ .n_name = "_cputime_pctrack" },
62 	{ .n_name = NULL }
63 };
64 
65 static char corefile[PATH_MAX];
66 static char execfile[PATH_MAX];
67 static char errbuf[_POSIX2_LINE_MAX];
68 
69 static int sflag;
70 static int iflag;
71 static int nflag;
72 static int fflag;
73 static int cflag = -1;
74 static int Nflag;
75 static int Mflag;
76 
77 /*
78  * Reads the cputime_pctrack[] structure from the kernel and displays
79  * the results in a human readable format.
80  */
81 int
82 main(int ac, char **av)
83 {
84 	struct kinfo_pcheader pchead;
85 	struct kinfo_pctrack pctrack;
86 	kvm_t *kd;
87 	int ntrack;
88 	int ncpus;
89 	int cpu;
90 	int repeat;
91 	int c;
92 
93 	/*
94 	 * Parse commandline arguments.
95 	 */
96 	while ((c = getopt(ac, av, "nsifc:N:M:")) != -1) {
97 		switch (c) {
98 		case 'N':
99 			if (strlcpy(execfile, optarg, sizeof(execfile))
100 			    >= sizeof(execfile))
101 				errx(1, "%s: File name too long", optarg);
102 			Nflag = 1;
103 			break;
104 		case 'M':
105 			if (strlcpy(corefile, optarg, sizeof(corefile))
106 			    >= sizeof(corefile))
107 				errx(1, "%s: File name too long", optarg);
108 			Mflag = 1;
109 			break;
110 		case 'c':
111 			cflag = strtol(optarg, NULL, 0);
112 			break;
113 		case 's':
114 			sflag = 1;
115 			break;
116 		case 'i':
117 			iflag = 1;
118 			break;
119 		case 'n':
120 			nflag = 1;
121 			break;
122 		case 'f':
123 			fflag = 1;
124 			break;
125 		default:
126 			usage();
127 		}
128 	}
129 
130 	if (sflag == 0 && iflag == 0) {
131 		sflag = 1;
132 		iflag = 1;
133 	}
134 	if (nflag == 0)
135 		read_symbols(Nflag ? execfile : NULL);
136 
137 	if (fflag && (cflag < 0 || sflag + iflag > 1)) {
138 		fprintf(stderr, "-f can only be specified with a particular cpu and just one of -i or -s\n");
139 		exit(1);
140 	}
141 
142 	ac -= optind;
143 	av += optind;
144 	if (ac != 0 && strtod(av[0], NULL) > 0.0) {
145 		repeat = (int)(strtod(av[0], NULL) * 1000000.0);
146 		++av;
147 		--ac;
148 	} else if (fflag) {
149 		repeat = 1000000 / 10;
150 	} else {
151 		repeat = 0;
152 	}
153 	if (ac != 0)
154 		usage();
155 
156 	/*
157 	 * Open our execfile and corefile, resolve needed symbols and read in
158 	 * the trace buffer.
159 	 */
160 	if ((kd = kvm_openfiles(Nflag ? execfile : NULL,
161 	    Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL)
162 		errx(1, "%s", errbuf);
163 	if (kvm_nlist(kd, nl) != 0)
164 		errx(1, "%s", kvm_geterr(kd));
165 
166 	if (kvm_read(kd, nl[0].n_value, &ncpus, sizeof(ncpus)) == -1)
167 		errx(1, "%s", kvm_geterr(kd));
168 	if (kvm_read(kd, nl[1].n_value, &pchead, sizeof(pchead)) == -1)
169 		errx(1, "%s", kvm_geterr(kd));
170 
171 again:
172 	for (cpu = 0; cpu < ncpus; ++cpu) {
173 		for (ntrack = 0; ntrack < pchead.pc_ntrack; ++ntrack) {
174 			int offset;
175 
176 			if (ntrack == PCTRACK_SYS && sflag == 0)
177 				continue;
178 			if (ntrack == PCTRACK_INT && iflag == 0)
179 				continue;
180 			if (cflag >= 0 && cflag != cpu)
181 				continue;
182 
183 			offset = offsetof(struct kinfo_pctrack,
184 					  pc_array[pchead.pc_arysize]);
185 			offset = (offset * pchead.pc_ntrack * cpu) +
186 				 (offset * ntrack);
187 			if (kvm_read(kd, nl[2].n_value + offset, &pctrack, sizeof(pctrack)) < 0)
188 				errx(1, "%s", kvm_geterr(kd));
189 
190 			printf("CPU %d %s:\n", cpu,
191 				(ntrack == PCTRACK_SYS) ? "SYSTEM" :
192 				(ntrack == PCTRACK_INT) ? "INTERRUPT" : "?"
193 			);
194 
195 			do_output(cpu, ntrack, &pchead, &pctrack, pctrack.pc_index - pchead.pc_arysize);
196 			while (fflag) {
197 				usleep(repeat);
198 				int last_index = pctrack.pc_index;
199 				kvm_read(kd, nl[2].n_value + offset, &pctrack,
200 					 sizeof(pctrack));
201 				do_output(cpu, ntrack, &pchead, &pctrack, last_index);
202 			}
203 		}
204 	}
205 	if (repeat) {
206 		usleep(repeat);
207 		goto again;
208 	}
209 	return(0);
210 }
211 
212 static void
213 do_output(int cpu __unused, int track __unused, struct kinfo_pcheader *pchead, struct kinfo_pctrack *pctrack, int base_index)
214 {
215 	int i;
216 
217 	i = base_index;
218 	if (pctrack->pc_index - base_index > pchead->pc_arysize) {
219 		i = pctrack->pc_index - pchead->pc_arysize;
220 	}
221 	while (i < pctrack->pc_index) {
222 		void *data = pctrack->pc_array[i & (pchead->pc_arysize - 1)];
223 		if (nflag)
224 			printf("\t%p\n", data);
225 		else
226 			printf("\t%s\n", address_to_symbol(data));
227 		++i;
228 	}
229 }
230 
231 struct symdata {
232 	TAILQ_ENTRY(symdata) link;
233 	const char *symname;
234 	char *symaddr;
235 	char symtype;
236 };
237 
238 static TAILQ_HEAD(symlist, symdata) symlist;
239 static struct symdata *symcache;
240 static char *symbegin;
241 static char *symend;
242 
243 static void
244 read_symbols(const char *file)
245 {
246 	char buf[256];
247 	char cmd[256];
248 	size_t buflen = sizeof(buf);
249 	FILE *fp;
250 	struct symdata *sym;
251 	char *s1;
252 	char *s2;
253 	char *s3;
254 
255 	TAILQ_INIT(&symlist);
256 
257 	if (file == NULL) {
258 		if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
259 			file = "/boot/kernel";
260 		else
261 			file = buf;
262 	}
263 	snprintf(cmd, sizeof(cmd), "nm -n %s", file);
264 	if ((fp = popen(cmd, "r")) != NULL) {
265 		while (fgets(buf, sizeof(buf), fp) != NULL) {
266 		    s1 = strtok(buf, " \t\n");
267 		    s2 = strtok(NULL, " \t\n");
268 		    s3 = strtok(NULL, " \t\n");
269 		    if (s1 && s2 && s3) {
270 			sym = malloc(sizeof(struct symdata));
271 			sym->symaddr = (char *)strtoul(s1, NULL, 16);
272 			sym->symtype = s2[0];
273 			sym->symname = strdup(s3);
274 			if (strcmp(s3, "kernbase") == 0)
275 				symbegin = sym->symaddr;
276 			if (strcmp(s3, "end") == 0)
277 				symend = sym->symaddr;
278 			TAILQ_INSERT_TAIL(&symlist, sym, link);
279 		    }
280 		}
281 		pclose(fp);
282 	}
283 	symcache = TAILQ_FIRST(&symlist);
284 }
285 
286 static const char *
287 address_to_symbol(void *kptr)
288 {
289 	static char buf[64];
290 
291 	if (symcache == NULL ||
292 	   (char *)kptr < symbegin || (char *)kptr >= symend
293 	) {
294 		snprintf(buf, sizeof(buf), "%p", kptr);
295 		return(buf);
296 	}
297 	while ((char *)symcache->symaddr < (char *)kptr) {
298 		if (TAILQ_NEXT(symcache, link) == NULL)
299 			break;
300 		symcache = TAILQ_NEXT(symcache, link);
301 	}
302 	while ((char *)symcache->symaddr > (char *)kptr) {
303 		if (symcache != TAILQ_FIRST(&symlist))
304 			symcache = TAILQ_PREV(symcache, symlist, link);
305 	}
306 	snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
307 		(int)((char *)kptr - symcache->symaddr));
308 	return(buf);
309 }
310 
311 static void
312 usage(void)
313 {
314 	fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] "
315 			"[-M corefile]\n");
316 	exit(1);
317 }
318