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