nrelease - fix/improve livecd
[dragonfly.git] / usr.bin / pctrack / pctrack.c
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
262         symend = NULL;
263         symbegin = (void *)(intptr_t)-1;
264
265         snprintf(cmd, sizeof(cmd), "nm -n %s", file);
266         if ((fp = popen(cmd, "r")) != NULL) {
267                 while (fgets(buf, sizeof(buf), fp) != NULL) {
268                     s1 = strtok(buf, " \t\n");
269                     s2 = strtok(NULL, " \t\n");
270                     s3 = strtok(NULL, " \t\n");
271                     if (s1 && s2 && s3) {
272                         sym = malloc(sizeof(struct symdata));
273                         sym->symaddr = (char *)strtoul(s1, NULL, 16);
274                         sym->symtype = s2[0];
275                         sym->symname = strdup(s3);
276                         if (s2[0] == 't' || s2[0] == 'T') {
277                                 if (symbegin > sym->symaddr)
278                                         symbegin = sym->symaddr;
279                                 if (symend < sym->symaddr)
280                                         symend = sym->symaddr;
281                         }
282                         TAILQ_INSERT_TAIL(&symlist, sym, link);
283                     }
284                 }
285                 pclose(fp);
286         }
287         symcache = TAILQ_FIRST(&symlist);
288 }
289
290 static const char *
291 address_to_symbol(void *kptr)
292 {
293         static char buf[64];
294
295         if (symcache == NULL ||
296            (char *)kptr < symbegin || (char *)kptr >= symend
297         ) {
298                 snprintf(buf, sizeof(buf), "%p", kptr);
299                 return(buf);
300         }
301         while ((char *)symcache->symaddr < (char *)kptr) {
302                 if (TAILQ_NEXT(symcache, link) == NULL)
303                         break;
304                 symcache = TAILQ_NEXT(symcache, link);
305         }
306         while ((char *)symcache->symaddr > (char *)kptr) {
307                 if (symcache != TAILQ_FIRST(&symlist))
308                         symcache = TAILQ_PREV(symcache, symlist, link);
309         }
310         snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
311                 (int)((char *)kptr - symcache->symaddr));
312         return(buf);
313 }
314
315 static void
316 usage(void)
317 {
318         fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] "
319                         "[-M corefile]\n");
320         exit(1);
321 }