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 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); 277 } 278 } 279 pclose(fp); 280 } 281 symcache = TAILQ_FIRST(&symlist); 282 } 283 284 static const char * 285 address_to_symbol(void *kptr) 286 { 287 static char buf[64]; 288 289 if (symcache == NULL || 290 (char *)kptr < symbegin || (char *)kptr >= symend 291 ) { 292 snprintf(buf, sizeof(buf), "%p", kptr); 293 return(buf); 294 } 295 while ((char *)symcache->symaddr < (char *)kptr) { 296 if (TAILQ_NEXT(symcache, link) == NULL) 297 break; 298 symcache = TAILQ_NEXT(symcache, link); 299 } 300 while ((char *)symcache->symaddr > (char *)kptr) { 301 if (symcache != TAILQ_FIRST(&symlist)) 302 symcache = TAILQ_PREV(symcache, symlist, link); 303 } 304 snprintf(buf, sizeof(buf), "%s+%d", symcache->symname, 305 (int)((char *)kptr - symcache->symaddr)); 306 return(buf); 307 } 308 309 static void 310 usage(void) 311 { 312 fprintf(stderr, "usage: pctrack [-nsi] [-c cpu] [-N execfile] " 313 "[-M corefile]\n"); 314 exit(1); 315 } 316