pg: Add missing dummy stack frames for mcount for x86_64.
[dragonfly.git] / usr.bin / pctrack / pctrack.c
blob6729625e0d65792b8ff2983c1db21551fd331f00
1 /*-
2 * Copyright (c) 2002 Jake Burkholder
3 * Copyright (c) 2004 Robert Watson
4 * All rights reserved.
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.
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.
27 * $DragonFly: src/usr.bin/pctrack/pctrack.c,v 1.2 2008/09/02 11:50:46 matthias Exp $
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>
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>
49 #define SBUFLEN 128
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 *);
56 static struct nlist nl[] = {
57 { .n_name = "_ncpus" },
58 { .n_name = "_cputime_pcheader" },
59 { .n_name = "_cputime_pctrack" },
60 { .n_name = NULL }
63 static char corefile[PATH_MAX];
64 static char execfile[PATH_MAX];
65 static char errbuf[_POSIX2_LINE_MAX];
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;
76 * Reads the cputime_pctrack[] structure from the kernel and displays
77 * the results in a human readable format.
79 int
80 main(int ac, char **av)
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;
92 * Parse commandline arguments.
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();
128 if (sflag == 0 && iflag == 0) {
129 sflag = 1;
130 iflag = 1;
132 if (nflag == 0)
133 read_symbols(Nflag ? execfile : NULL);
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);
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;
151 if (ac != 0)
152 usage();
155 * Open our execfile and corefile, resolve needed symbols and read in
156 * the trace buffer.
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));
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));
169 again:
170 for (cpu = 0; cpu < ncpus; ++cpu) {
171 for (ntrack = 0; ntrack < pchead.pc_ntrack; ++ntrack) {
172 int offset;
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;
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));
188 printf("CPU %d %s:\n", cpu,
189 (ntrack == PCTRACK_SYS) ? "SYSTEM" :
190 (ntrack == PCTRACK_INT) ? "INTERRUPT" : "?"
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);
203 if (repeat) {
204 usleep(repeat);
205 goto again;
207 return(0);
210 static void
211 do_output(int cpu __unused, int track __unused, struct kinfo_pcheader *pchead, struct kinfo_pctrack *pctrack, int base_index)
213 int i;
215 i = base_index;
216 if (pctrack->pc_index - base_index > pchead->pc_arysize) {
217 i = pctrack->pc_index - pchead->pc_arysize;
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;
229 struct symdata {
230 TAILQ_ENTRY(symdata) link;
231 const char *symname;
232 char *symaddr;
233 char symtype;
236 static TAILQ_HEAD(symlist, symdata) symlist;
237 static struct symdata *symcache;
238 static char *symbegin;
239 static char *symend;
241 static void
242 read_symbols(const char *file)
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;
253 TAILQ_INIT(&symlist);
255 if (file == NULL) {
256 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0)
257 file = "/boot/kernel";
258 else
259 file = buf;
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);
279 pclose(fp);
281 symcache = TAILQ_FIRST(&symlist);
284 static const char *
285 address_to_symbol(void *kptr)
287 static char buf[64];
289 if (symcache == NULL ||
290 (char *)kptr < symbegin || (char *)kptr >= symend
292 snprintf(buf, sizeof(buf), "%p", kptr);
293 return(buf);
295 while ((char *)symcache->symaddr < (char *)kptr) {
296 if (TAILQ_NEXT(symcache, link) == NULL)
297 break;
298 symcache = TAILQ_NEXT(symcache, link);
300 while ((char *)symcache->symaddr > (char *)kptr) {
301 if (symcache != TAILQ_FIRST(&symlist))
302 symcache = TAILQ_PREV(symcache, symlist, link);
304 snprintf(buf, sizeof(buf), "%s+%d", symcache->symname,
305 (int)((char *)kptr - symcache->symaddr));
306 return(buf);
309 static void
310 usage(void)
312 fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] "
313 "[-M corefile]\n");
314 exit(1);