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.1 2006/06/08 18:48:30 dillon Exp $ 28 */ 29 30 #include <sys/cdefs.h> 31 32 #include <sys/types.h> 33 #include <sys/ktr.h> 34 #include <sys/kinfo.h> 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 #include <sys/queue.h> 38 39 #include <err.h> 40 #include <fcntl.h> 41 #include <kvm.h> 42 #include <limits.h> 43 #include <nlist.h> 44 #include <stdint.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <stddef.h> 49 #include <unistd.h> 50 51 #define SBUFLEN 128 52 53 extern char *optarg; 54 extern int optind; 55 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 *); 60 61 static struct nlist nl[] = { 62 { "_ncpus" }, 63 { "_cputime_pcheader" }, 64 { "_cputime_pctrack" }, 65 { NULL } 66 }; 67 68 static char corefile[PATH_MAX]; 69 static char execfile[PATH_MAX]; 70 static char errbuf[_POSIX2_LINE_MAX]; 71 72 static int sflag; 73 static int iflag; 74 static int nflag; 75 static int fflag; 76 static int cflag = -1; 77 static int Nflag; 78 static int Mflag; 79 80 /* 81 * Reads the cputime_pctrack[] structure from the kernel and displays 82 * the results in a human readable format. 83 */ 84 int 85 main(int ac, char **av) 86 { 87 struct kinfo_pcheader pchead; 88 struct kinfo_pctrack pctrack; 89 kvm_t *kd; 90 int ntrack; 91 int ncpus; 92 int cpu; 93 int repeat; 94 int c; 95 96 /* 97 * Parse commandline arguments. 98 */ 99 while ((c = getopt(ac, av, "nsifc:N:M:")) != -1) { 100 switch (c) { 101 case 'N': 102 if (strlcpy(execfile, optarg, sizeof(execfile)) 103 >= sizeof(execfile)) 104 errx(1, "%s: File name too long", optarg); 105 Nflag = 1; 106 break; 107 case 'M': 108 if (strlcpy(corefile, optarg, sizeof(corefile)) 109 >= sizeof(corefile)) 110 errx(1, "%s: File name too long", optarg); 111 Mflag = 1; 112 break; 113 case 'c': 114 cflag = strtol(optarg, NULL, 0); 115 break; 116 case 's': 117 sflag = 1; 118 break; 119 case 'i': 120 iflag = 1; 121 break; 122 case 'n': 123 nflag = 1; 124 break; 125 case 'f': 126 fflag = 1; 127 break; 128 default: 129 usage(); 130 } 131 } 132 133 if (sflag == 0 && iflag == 0) { 134 sflag = 1; 135 iflag = 1; 136 } 137 if (nflag == 0) 138 read_symbols(Nflag ? execfile : NULL); 139 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"); 142 exit(1); 143 } 144 145 ac -= optind; 146 av += optind; 147 if (ac != 0 && strtod(av[0], NULL) > 0.0) { 148 repeat = (int)(strtod(av[0], NULL) * 1000000.0); 149 ++av; 150 --ac; 151 } else if (fflag) { 152 repeat = 1000000 / 10; 153 } else { 154 repeat = 0; 155 } 156 if (ac != 0) 157 usage(); 158 159 /* 160 * Open our execfile and corefile, resolve needed symbols and read in 161 * the trace buffer. 162 */ 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)); 168 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)); 173 174 again: 175 for (cpu = 0; cpu < ncpus; ++cpu) { 176 for (ntrack = 0; ntrack < pchead.pc_ntrack; ++ntrack) { 177 int offset; 178 179 if (ntrack == PCTRACK_SYS && sflag == 0) 180 continue; 181 if (ntrack == PCTRACK_INT && iflag == 0) 182 continue; 183 if (cflag >= 0 && cflag != cpu) 184 continue; 185 186 offset = offsetof(struct kinfo_pctrack, 187 pc_array[pchead.pc_arysize]); 188 offset = (offset * pchead.pc_ntrack * cpu) + 189 (offset * ntrack); 190 if (kvm_read(kd, nl[2].n_value + offset, &pctrack, sizeof(pctrack)) < 0) 191 errx(1, "%s", kvm_geterr(kd)); 192 193 printf("CPU %d %s:\n", cpu, 194 (ntrack == PCTRACK_SYS) ? "SYSTEM" : 195 (ntrack == PCTRACK_INT) ? "INTERRUPT" : "?" 196 ); 197 198 do_output(cpu, ntrack, &pchead, &pctrack, pctrack.pc_index - pchead.pc_arysize); 199 while (fflag) { 200 usleep(repeat); 201 int last_index = pctrack.pc_index; 202 kvm_read(kd, nl[2].n_value + offset, &pctrack, 203 sizeof(pctrack)); 204 do_output(cpu, ntrack, &pchead, &pctrack, last_index); 205 } 206 } 207 } 208 if (repeat) { 209 usleep(repeat); 210 goto again; 211 } 212 return(0); 213 } 214 215 static void 216 do_output(int cpu, int track, struct kinfo_pcheader *pchead, struct kinfo_pctrack *pctrack, int base_index) 217 { 218 int i; 219 220 i = base_index; 221 if (pctrack->pc_index - base_index > pchead->pc_arysize) { 222 i = pctrack->pc_index - pchead->pc_arysize; 223 } 224 while (i < pctrack->pc_index) { 225 void *data = pctrack->pc_array[i & (pchead->pc_arysize - 1)]; 226 if (nflag) 227 printf("\t%p\n", data); 228 else 229 printf("\t%s\n", address_to_symbol(data)); 230 ++i; 231 } 232 } 233 234 struct symdata { 235 TAILQ_ENTRY(symdata) link; 236 const char *symname; 237 char *symaddr; 238 char symtype; 239 }; 240 241 static TAILQ_HEAD(symlist, symdata) symlist; 242 static struct symdata *symcache; 243 static char *symbegin; 244 static char *symend; 245 246 static 247 void 248 read_symbols(const char *execfile) 249 { 250 char buf[256]; 251 char cmd[256]; 252 int buflen = sizeof(buf); 253 FILE *fp; 254 struct symdata *sym; 255 char *s1; 256 char *s2; 257 char *s3; 258 259 TAILQ_INIT(&symlist); 260 261 if (execfile == NULL) { 262 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0) 263 execfile = "/kernel"; 264 else 265 execfile = buf; 266 } 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); 283 } 284 } 285 pclose(fp); 286 } 287 symcache = TAILQ_FIRST(&symlist); 288 } 289 290 static 291 const char * 292 address_to_symbol(void *kptr) 293 { 294 static char buf[64]; 295 296 if (symcache == NULL || 297 (char *)kptr < symbegin || (char *)kptr >= symend 298 ) { 299 snprintf(buf, sizeof(buf), "%p", kptr); 300 return(buf); 301 } 302 while ((char *)symcache->symaddr < (char *)kptr) { 303 if (TAILQ_NEXT(symcache, link) == NULL) 304 break; 305 symcache = TAILQ_NEXT(symcache, link); 306 } 307 while ((char *)symcache->symaddr > (char *)kptr) { 308 if (symcache != TAILQ_FIRST(&symlist)) 309 symcache = TAILQ_PREV(symcache, symlist, link); 310 } 311 snprintf(buf, sizeof(buf), "%s+%d", symcache->symname, 312 (int)((char *)kptr - symcache->symaddr)); 313 return(buf); 314 } 315 316 static void 317 usage(void) 318 { 319 fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] " 320 "[-M corefile]\n"); 321 exit(1); 322 } 323