2 * Copyright (c) 2002 Jake Burkholder
3 * Copyright (c) 2004 Robert Watson
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
27 * $DragonFly: src/usr.bin/pctrack/pctrack.c,v 1.2 2008/09/02 11:50:46 matthias Exp $
30 #include <sys/cdefs.h>
32 #include <sys/types.h>
34 #include <sys/kinfo.h>
37 #include <sys/queue.h>
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 *);
58 static struct nlist nl
[] = {
59 { .n_name
= "_ncpus" },
60 { .n_name
= "_cputime_pcheader" },
61 { .n_name
= "_cputime_pctrack" },
65 static char corefile
[PATH_MAX
];
66 static char execfile
[PATH_MAX
];
67 static char errbuf
[_POSIX2_LINE_MAX
];
73 static int cflag
= -1;
78 * Reads the cputime_pctrack[] structure from the kernel and displays
79 * the results in a human readable format.
82 main(int ac
, char **av
)
84 struct kinfo_pcheader pchead
;
85 struct kinfo_pctrack pctrack
;
94 * Parse commandline arguments.
96 while ((c
= getopt(ac
, av
, "nsifc:N:M:")) != -1) {
99 if (strlcpy(execfile
, optarg
, sizeof(execfile
))
101 errx(1, "%s: File name too long", optarg
);
105 if (strlcpy(corefile
, optarg
, sizeof(corefile
))
107 errx(1, "%s: File name too long", optarg
);
111 cflag
= strtol(optarg
, NULL
, 0);
130 if (sflag
== 0 && iflag
== 0) {
135 read_symbols(Nflag
? execfile
: NULL
);
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");
144 if (ac
!= 0 && strtod(av
[0], NULL
) > 0.0) {
145 repeat
= (int)(strtod(av
[0], NULL
) * 1000000.0);
149 repeat
= 1000000 / 10;
157 * Open our execfile and corefile, resolve needed symbols and read in
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
));
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
));
172 for (cpu
= 0; cpu
< ncpus
; ++cpu
) {
173 for (ntrack
= 0; ntrack
< pchead
.pc_ntrack
; ++ntrack
) {
176 if (ntrack
== PCTRACK_SYS
&& sflag
== 0)
178 if (ntrack
== PCTRACK_INT
&& iflag
== 0)
180 if (cflag
>= 0 && cflag
!= cpu
)
183 offset
= offsetof(struct kinfo_pctrack
,
184 pc_array
[pchead
.pc_arysize
]);
185 offset
= (offset
* pchead
.pc_ntrack
* cpu
) +
187 if (kvm_read(kd
, nl
[2].n_value
+ offset
, &pctrack
, sizeof(pctrack
)) < 0)
188 errx(1, "%s", kvm_geterr(kd
));
190 printf("CPU %d %s:\n", cpu
,
191 (ntrack
== PCTRACK_SYS
) ? "SYSTEM" :
192 (ntrack
== PCTRACK_INT
) ? "INTERRUPT" : "?"
195 do_output(cpu
, ntrack
, &pchead
, &pctrack
, pctrack
.pc_index
- pchead
.pc_arysize
);
198 int last_index
= pctrack
.pc_index
;
199 kvm_read(kd
, nl
[2].n_value
+ offset
, &pctrack
,
201 do_output(cpu
, ntrack
, &pchead
, &pctrack
, last_index
);
213 do_output(int cpu __unused
, int track __unused
, struct kinfo_pcheader
*pchead
, struct kinfo_pctrack
*pctrack
, int base_index
)
218 if (pctrack
->pc_index
- base_index
> pchead
->pc_arysize
) {
219 i
= pctrack
->pc_index
- pchead
->pc_arysize
;
221 while (i
< pctrack
->pc_index
) {
222 void *data
= pctrack
->pc_array
[i
& (pchead
->pc_arysize
- 1)];
224 printf("\t%p\n", data
);
226 printf("\t%s\n", address_to_symbol(data
));
232 TAILQ_ENTRY(symdata
) link
;
238 static TAILQ_HEAD(symlist
, symdata
) symlist
;
239 static struct symdata
*symcache
;
240 static char *symbegin
;
244 read_symbols(const char *file
)
248 size_t buflen
= sizeof(buf
);
255 TAILQ_INIT(&symlist
);
258 if (sysctlbyname("kern.bootfile", buf
, &buflen
, NULL
, 0) < 0)
259 file
= "/boot/kernel";
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
);
283 symcache
= TAILQ_FIRST(&symlist
);
287 address_to_symbol(void *kptr
)
291 if (symcache
== NULL
||
292 (char *)kptr
< symbegin
|| (char *)kptr
>= symend
294 snprintf(buf
, sizeof(buf
), "%p", kptr
);
297 while ((char *)symcache
->symaddr
< (char *)kptr
) {
298 if (TAILQ_NEXT(symcache
, link
) == NULL
)
300 symcache
= TAILQ_NEXT(symcache
, link
);
302 while ((char *)symcache
->symaddr
> (char *)kptr
) {
303 if (symcache
!= TAILQ_FIRST(&symlist
))
304 symcache
= TAILQ_PREV(symcache
, symlist
, link
);
306 snprintf(buf
, sizeof(buf
), "%s+%d", symcache
->symname
,
307 (int)((char *)kptr
- symcache
->symaddr
));
314 fprintf(stderr
, "usage: pctrack [-nsi] [-c cpu] [-N execfile] "