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 * $FreeBSD: src/usr.bin/ktrdump/ktrdump.c,v 1.10 2005/05/21 09:55:06 ru Exp $ 28 * $DragonFly: src/usr.bin/ktrdump/ktrdump.c,v 1.3 2005/06/21 06:50:28 dillon Exp $ 29 */ 30 31 #include <sys/cdefs.h> 32 33 #include <sys/types.h> 34 #include <sys/ktr.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 <unistd.h> 49 50 #define SBUFLEN 128 51 52 extern char *optarg; 53 extern int optind; 54 55 static void usage(void); 56 static void print_header(FILE *fo, int row); 57 static void print_entry(FILE *fo, kvm_t *kd, int n, int i, struct ktr_entry *entry); 58 static struct ktr_info *kvm_ktrinfo(kvm_t *kd, void *kptr); 59 static const char *kvm_string(kvm_t *kd, const char *kptr); 60 static const char *trunc_path(const char *str, int maxlen); 61 static void read_symbols(const char *execfile); 62 static const char *address_to_symbol(void *kptr); 63 64 static struct nlist nl[] = { 65 { "_ktr_version" }, 66 { "_ktr_entries" }, 67 { "_ktr_idx" }, 68 { "_ktr_buf" }, 69 { "_ncpus" }, 70 { NULL } 71 }; 72 73 static int cflag; 74 static int fflag; 75 static int iflag; 76 static int nflag; 77 static int qflag; 78 static int rflag; 79 static int tflag; 80 static int xflag; 81 static int pflag; 82 static int Mflag; 83 static int Nflag; 84 static int64_t last_timestamp; 85 86 static char corefile[PATH_MAX]; 87 static char execfile[PATH_MAX]; 88 89 static char desc[SBUFLEN]; 90 static char errbuf[_POSIX2_LINE_MAX]; 91 static char fbuf[PATH_MAX]; 92 static char obuf[PATH_MAX]; 93 94 /* 95 * Reads the ktr trace buffer from kernel memory and prints the trace entries. 96 */ 97 int 98 main(int ac, char **av) 99 { 100 struct ktr_entry **ktr_buf; 101 uintmax_t tlast, tnow; 102 struct stat sb; 103 kvm_t *kd; 104 FILE *fo; 105 char *p; 106 int version; 107 int entries; 108 int *ktr_idx; 109 int ncpus; 110 int did_display_flag = 0; 111 int in; 112 int c; 113 int i; 114 int n; 115 116 /* 117 * Parse commandline arguments. 118 */ 119 fo = stdout; 120 while ((c = getopt(ac, av, "acfiqrtxpN:M:o:")) != -1) { 121 switch (c) { 122 case 'a': 123 cflag = 1; 124 iflag = 1; 125 tflag = 1; 126 xflag = 1; 127 fflag = 1; 128 pflag = 1; 129 break; 130 case 'c': 131 cflag = 1; 132 break; 133 case 'N': 134 if (strlcpy(execfile, optarg, sizeof(execfile)) 135 >= sizeof(execfile)) 136 errx(1, "%s: File name too long", optarg); 137 Nflag = 1; 138 break; 139 case 'f': 140 fflag = 1; 141 break; 142 case 'i': 143 iflag = 1; 144 break; 145 case 'M': 146 if (strlcpy(corefile, optarg, sizeof(corefile)) 147 >= sizeof(corefile)) 148 errx(1, "%s: File name too long", optarg); 149 Mflag = 1; 150 break; 151 case 'n': 152 nflag = 1; 153 break; 154 case 'o': 155 if ((fo = fopen(optarg, "w")) == NULL) 156 err(1, "%s", optarg); 157 break; 158 case 'p': 159 pflag++; 160 break; 161 case 'q': 162 qflag++; 163 break; 164 case 'r': 165 rflag = 1; 166 break; 167 case 't': 168 tflag = 1; 169 break; 170 case 'x': 171 xflag = 1; 172 break; 173 case '?': 174 default: 175 usage(); 176 } 177 } 178 if (cflag + iflag + tflag + xflag + fflag + pflag == 0) { 179 cflag = 1; 180 iflag = 1; 181 tflag = 1; 182 fflag = 1; 183 pflag = 1; 184 } 185 186 ac -= optind; 187 av += optind; 188 if (ac != 0) 189 usage(); 190 191 /* 192 * Open our execfile and corefile, resolve needed symbols and read in 193 * the trace buffer. 194 */ 195 if ((kd = kvm_openfiles(Nflag ? execfile : NULL, 196 Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL) 197 errx(1, "%s", errbuf); 198 if (kvm_nlist(kd, nl) != 0) 199 errx(1, "%s", kvm_geterr(kd)); 200 if (kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1) 201 errx(1, "%s", kvm_geterr(kd)); 202 if (kvm_read(kd, nl[4].n_value, &ncpus, sizeof(ncpus)) == -1) 203 errx(1, "%s", kvm_geterr(kd)); 204 205 if (version != KTR_VERSION) 206 errx(1, "ktr version mismatch"); 207 if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries)) == -1) 208 errx(1, "%s", kvm_geterr(kd)); 209 ktr_buf = malloc(sizeof(*ktr_buf) * ncpus); 210 ktr_idx = malloc(sizeof(*ktr_idx) * ncpus); 211 212 if (nflag == 0) 213 read_symbols(Nflag ? execfile : NULL); 214 215 if (kvm_read(kd, nl[2].n_value, ktr_idx, sizeof(*ktr_idx) * ncpus) == -1) 216 errx(1, "%s", kvm_geterr(kd)); 217 if (kvm_read(kd, nl[3].n_value, ktr_buf, sizeof(*ktr_buf) * ncpus) == -1) 218 errx(1, "%s", kvm_geterr(kd)); 219 for (n = 0; n < ncpus; ++n) { 220 void *kptr = ktr_buf[n]; 221 ktr_buf[n] = malloc(sizeof(**ktr_buf) * entries); 222 if (kvm_read(kd, (uintptr_t)kptr, ktr_buf[n], sizeof(**ktr_buf) * entries) == -1) 223 errx(1, "%s", kvm_geterr(kd)); 224 } 225 226 /* 227 * Now tear through the trace buffer. 228 */ 229 for (n = 0; n < ncpus; ++n) { 230 last_timestamp = 0; 231 for (i = 0; i < entries; ++i) { 232 print_header(fo, i); 233 print_entry(fo, kd, n, i, &ktr_buf[n][i]); 234 } 235 } 236 return (0); 237 } 238 239 static void 240 print_header(FILE *fo, int row) 241 { 242 if (qflag == 0 && row % 20 == 0) { 243 fprintf(fo, "%-6s ", "index"); 244 if (cflag) 245 fprintf(fo, "%-3s ", "cpu"); 246 if (tflag || rflag) 247 fprintf(fo, "%-16s ", "timestamp"); 248 if (xflag) { 249 if (nflag) 250 fprintf(fo, "%-10s %-10s", "caller2", "caller1"); 251 else 252 fprintf(fo, "%-20s %-20s", "caller2", "caller1"); 253 } 254 if (iflag) 255 fprintf(fo, "%-20s ", "ID"); 256 if (fflag) 257 fprintf(fo, "%10s%-30s ", "", "file and line"); 258 if (pflag) 259 fprintf(fo, "%s", "trace"); 260 fprintf(fo, "\n"); 261 } 262 } 263 264 static void 265 print_entry(FILE *fo, kvm_t *kd, int n, int i, struct ktr_entry *entry) 266 { 267 struct ktr_info *info = NULL; 268 269 fprintf(fo, " %5d ", i); 270 if (cflag) 271 fprintf(fo, "%-3d ", n); 272 if (tflag || rflag) { 273 if (rflag) 274 fprintf(fo, "%-16lld ", entry->ktr_timestamp - 275 last_timestamp); 276 else 277 fprintf(fo, "%-16lld ", entry->ktr_timestamp); 278 } 279 if (xflag) { 280 if (nflag) { 281 fprintf(fo, "%p %p ", 282 entry->ktr_caller2, entry->ktr_caller1); 283 } else { 284 fprintf(fo, "%-20s ", 285 address_to_symbol(entry->ktr_caller2)); 286 fprintf(fo, "%-20s ", 287 address_to_symbol(entry->ktr_caller1)); 288 } 289 } 290 if (iflag) { 291 info = kvm_ktrinfo(kd, entry->ktr_info); 292 if (info) 293 fprintf(fo, "%-20s ", kvm_string(kd, info->kf_name)); 294 else 295 fprintf(fo, "%-20s ", "<empty>"); 296 } 297 if (fflag) 298 fprintf(fo, "%34s:%-4d ", trunc_path(kvm_string(kd, entry->ktr_file), 34), entry->ktr_line); 299 if (pflag) { 300 if (info == NULL) 301 info = kvm_ktrinfo(kd, entry->ktr_info); 302 if (info) { 303 fprintf(fo, kvm_string(kd, info->kf_format), 304 entry->ktr_data[0], entry->ktr_data[1], 305 entry->ktr_data[2], entry->ktr_data[3], 306 entry->ktr_data[4], entry->ktr_data[5], 307 entry->ktr_data[6], entry->ktr_data[7], 308 entry->ktr_data[8], entry->ktr_data[9]); 309 } else { 310 fprintf(fo, ""); 311 } 312 } 313 fprintf(fo, "\n"); 314 last_timestamp = entry->ktr_timestamp; 315 } 316 317 static 318 struct ktr_info * 319 kvm_ktrinfo(kvm_t *kd, void *kptr) 320 { 321 static struct ktr_info save_info; 322 static void *save_kptr; 323 324 if (kptr == NULL) 325 return(NULL); 326 if (save_kptr != kptr) { 327 if (kvm_read(kd, (uintptr_t)kptr, &save_info, sizeof(save_info)) == -1) { 328 bzero(&save_info, sizeof(save_info)); 329 } else { 330 save_kptr = kptr; 331 } 332 } 333 return(&save_info); 334 } 335 336 static 337 const char * 338 kvm_string(kvm_t *kd, const char *kptr) 339 { 340 static char save_str[128]; 341 static const char *save_kptr; 342 int l; 343 int n; 344 345 if (kptr == NULL) 346 return("?"); 347 if (save_kptr != kptr) { 348 save_kptr = kptr; 349 l = 0; 350 while (l < sizeof(save_str) - 1) { 351 n = 256 - ((intptr_t)(kptr + l) & 255); 352 if (n > sizeof(save_str) - l - 1) 353 n = sizeof(save_str) - l - 1; 354 if (kvm_read(kd, (uintptr_t)(kptr + l), save_str + l, n) < 0) 355 break; 356 while (l < sizeof(save_str) && n) { 357 if (save_str[l] == 0) 358 break; 359 --n; 360 ++l; 361 } 362 if (n) 363 break; 364 } 365 save_str[l] = 0; 366 } 367 return(save_str); 368 } 369 370 static 371 const char * 372 trunc_path(const char *str, int maxlen) 373 { 374 int len = strlen(str); 375 376 if (len > maxlen) 377 return(str + len - maxlen); 378 else 379 return(str); 380 } 381 382 struct symdata { 383 TAILQ_ENTRY(symdata) link; 384 const char *symname; 385 char *symaddr; 386 char symtype; 387 }; 388 389 static TAILQ_HEAD(symlist, symdata) symlist; 390 static struct symdata *symcache; 391 static char *symbegin; 392 static char *symend; 393 394 static 395 void 396 read_symbols(const char *execfile) 397 { 398 char buf[256]; 399 char cmd[256]; 400 int buflen = sizeof(buf); 401 FILE *fp; 402 struct symdata *sym; 403 char *s1; 404 char *s2; 405 char *s3; 406 407 TAILQ_INIT(&symlist); 408 409 if (execfile == NULL) { 410 if (sysctlbyname("kern.bootfile", buf, &buflen, NULL, 0) < 0) 411 execfile = "/kernel"; 412 else 413 execfile = buf; 414 } 415 snprintf(cmd, sizeof(cmd), "nm -n %s", execfile); 416 if ((fp = popen(cmd, "r")) != NULL) { 417 while (fgets(buf, sizeof(buf), fp) != NULL) { 418 s1 = strtok(buf, " \t\n"); 419 s2 = strtok(NULL, " \t\n"); 420 s3 = strtok(NULL, " \t\n"); 421 if (s1 && s2 && s3) { 422 sym = malloc(sizeof(struct symdata)); 423 sym->symaddr = (char *)strtoul(s1, NULL, 16); 424 sym->symtype = s2[0]; 425 sym->symname = strdup(s3); 426 if (strcmp(s3, "kernbase") == 0) 427 symbegin = sym->symaddr; 428 if (strcmp(s3, "end") == 0) 429 symend = sym->symaddr; 430 TAILQ_INSERT_TAIL(&symlist, sym, link); 431 } 432 } 433 pclose(fp); 434 } 435 symcache = TAILQ_FIRST(&symlist); 436 } 437 438 static 439 const char * 440 address_to_symbol(void *kptr) 441 { 442 static char buf[64]; 443 444 if (symcache == NULL || 445 (char *)kptr < symbegin || (char *)kptr >= symend 446 ) { 447 snprintf(buf, sizeof(buf), "%p", kptr); 448 return(buf); 449 } 450 while ((char *)symcache->symaddr < (char *)kptr) { 451 if (TAILQ_NEXT(symcache, link) == NULL) 452 break; 453 symcache = TAILQ_NEXT(symcache, link); 454 } 455 while ((char *)symcache->symaddr > (char *)kptr) { 456 if (symcache != TAILQ_FIRST(&symlist)) 457 symcache = TAILQ_PREV(symcache, symlist, link); 458 } 459 snprintf(buf, sizeof(buf), "%s+%d", symcache->symname, 460 (int)((char *)kptr - symcache->symaddr)); 461 return(buf); 462 } 463 464 static void 465 usage(void) 466 { 467 fprintf(stderr, "usage: ktrdump [-acfinpqrtx] [-N execfile] " 468 "[-M corefile] [-o outfile]\n"); 469 exit(1); 470 } 471