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>
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 *);
61 static struct nlist nl[] = {
63 { "_cputime_pcheader" },
64 { "_cputime_pctrack" },
68 static char corefile[PATH_MAX];
69 static char execfile[PATH_MAX];
70 static char errbuf[_POSIX2_LINE_MAX];
76 static int cflag = -1;
81 * Reads the cputime_pctrack[] structure from the kernel and displays
82 * the results in a human readable format.
85 main(int ac, char **av)
87 struct kinfo_pcheader pchead;
88 struct kinfo_pctrack pctrack;
97 * Parse commandline arguments.
99 while ((c = getopt(ac, av, "nsifc:N:M:")) != -1) {
102 if (strlcpy(execfile, optarg, sizeof(execfile))
104 errx(1, "%s: File name too long", optarg);
108 if (strlcpy(corefile, optarg, sizeof(corefile))
110 errx(1, "%s: File name too long", optarg);
114 cflag = strtol(optarg, NULL, 0);
133 if (sflag == 0 && iflag == 0) {
138 read_symbols(Nflag ? execfile : NULL);
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");
147 if (ac != 0 && strtod(av[0], NULL) > 0.0) {
148 repeat = (int)(strtod(av[0], NULL) * 1000000.0);
152 repeat = 1000000 / 10;
160 * Open our execfile and corefile, resolve needed symbols and read in
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));
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));
175 for (cpu = 0; cpu < ncpus; ++cpu) {
176 for (ntrack = 0; ntrack < pchead.pc_ntrack; ++ntrack) {
179 if (ntrack == PCTRACK_SYS && sflag == 0)
181 if (ntrack == PCTRACK_INT && iflag == 0)
183 if (cflag >= 0 && cflag != cpu)
186 offset = offsetof(struct kinfo_pctrack,
187 pc_array[pchead.pc_arysize]);
188 offset = (offset * pchead.pc_ntrack * cpu) +
190 if (kvm_read(kd, nl[2].n_value + offset, &pctrack, sizeof(pctrack)) < 0)
191 errx(1, "%s", kvm_geterr(kd));
193 printf("CPU %d %s:\n", cpu,
194 (ntrack == PCTRACK_SYS) ? "SYSTEM" :
195 (ntrack == PCTRACK_INT) ? "INTERRUPT" : "?"
198 do_output(cpu, ntrack, &pchead, &pctrack, pctrack.pc_index - pchead.pc_arysize);
201 int last_index = pctrack.pc_index;
202 kvm_read(kd, nl[2].n_value + offset, &pctrack,
204 do_output(cpu, ntrack, &pchead, &pctrack, last_index);
216 do_output(int cpu, int track, struct kinfo_pcheader *pchead, struct kinfo_pctrack *pctrack, int base_index)
221 if (pctrack->pc_index - base_index > pchead->pc_arysize) {
222 i = pctrack->pc_index - pchead->pc_arysize;
224 while (i < pctrack->pc_index) {
225 void *data = pctrack->pc_array[i & (pchead->pc_arysize - 1)];
227 printf("\t%p\n", data);
229 printf("\t%s\n", address_to_symbol(data));
235 TAILQ_ENTRY(symdata) link;
241 static TAILQ_HEAD(symlist, symdata) symlist;
242 static struct symdata *symcache;
243 static char *symbegin;
248 read_symbols(const char *execfile)
252 int buflen = sizeof(buf);
259 TAILQ_INIT(&symlist);
261 if (execfile == NULL) {
262 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
263 execfile = "/boot/kernel";
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);
287 symcache = TAILQ_FIRST(&symlist);
292 address_to_symbol(void *kptr)
296 if (symcache == NULL ||
297 (char *)kptr < symbegin || (char *)kptr >= symend
299 snprintf(buf, sizeof(buf), "%p", kptr);
302 while ((char *)symcache->symaddr < (char *)kptr) {
303 if (TAILQ_NEXT(symcache, link) == NULL)
305 symcache = TAILQ_NEXT(symcache, link);
307 while ((char *)symcache->symaddr > (char *)kptr) {
308 if (symcache != TAILQ_FIRST(&symlist))
309 symcache = TAILQ_PREV(symcache, symlist, link);
311 snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
312 (int)((char *)kptr - symcache->symaddr));
319 fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] "