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