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 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD$"); 30 31#include <sys/types.h> 32#include <sys/ktr.h> 33#include <sys/mman.h> 34#include <sys/stat.h> 35 36#include <err.h> 37#include <fcntl.h> 38#include <kvm.h> 39#include <limits.h> 40#include <nlist.h> 41#include <stdint.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <unistd.h> 46 47#define SBUFLEN 128 48#define USAGE \ 49 "usage: ktrdump [-cfqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n" 50 51static void usage(void); 52 53static struct nlist nl[] = { 54 { "_ktr_version" }, 55 { "_ktr_entries" }, 56 { "_ktr_idx" }, 57 { "_ktr_buf" }, 58 { NULL } 59}; 60 61static int cflag; 62static int fflag; 63static int Mflag; 64static int Nflag; 65static int qflag; 66static int rflag; 67static int tflag; 68static int iflag; 69static int hflag; 70 71static char corefile[PATH_MAX]; 72static char execfile[PATH_MAX]; 73 74static char desc[SBUFLEN]; 75static char errbuf[_POSIX2_LINE_MAX]; 76static char fbuf[PATH_MAX]; 77static char obuf[PATH_MAX]; 78static char sbuf[KTR_PARMS][SBUFLEN]; 79 80/* 81 * Reads the ktr trace buffer from kernel memory and prints the trace entries. 82 */ 83int 84main(int ac, char **av) 85{ 86 u_long parms[KTR_PARMS]; 87 struct ktr_entry *buf; 88 uintmax_t tlast, tnow; 89 unsigned long bufptr; 90 struct stat sb; 91 kvm_t *kd; 92 FILE *out; 93 char *p; 94 int version; 95 int entries; 96 int index; 97 int parm; 98 int in; 99 int c; 100 int i = 0; 101 102 /* 103 * Parse commandline arguments. 104 */ 105 out = stdout; 106 while ((c = getopt(ac, av, "cfqrtHe:i:m:M:N:o:")) != -1) 107 switch (c) { 108 case 'c': 109 cflag = 1; 110 break; 111 case 'N': 112 case 'e': 113 if (strlcpy(execfile, optarg, sizeof(execfile)) 114 >= sizeof(execfile)) 115 errx(1, "%s: File name too long", optarg); 116 Nflag = 1; 117 break; 118 case 'f': 119 fflag = 1; 120 break; 121 case 'i': 122 iflag = 1; 123 if ((in = open(optarg, O_RDONLY)) == -1) 124 err(1, "%s", optarg); 125 break; 126 case 'M': 127 case 'm': 128 if (strlcpy(corefile, optarg, sizeof(corefile)) 129 >= sizeof(corefile)) 130 errx(1, "%s: File name too long", optarg); 131 Mflag = 1; 132 break; 133 case 'o': 134 if ((out = fopen(optarg, "w")) == NULL) 135 err(1, "%s", optarg); 136 break; 137 case 'q': 138 qflag++; 139 break; 140 case 'r': 141 rflag = 1; 142 break; 143 case 't': 144 tflag = 1; 145 break; 146 case 'H': 147 hflag = 1; 148 break; 149 case '?': 150 default: 151 usage(); 152 } 153 ac -= optind; 154 av += optind; 155 if (ac != 0) 156 usage(); 157 158 /* 159 * Open our execfile and corefile, resolve needed symbols and read in 160 * the trace buffer. 161 */ 162 if ((kd = kvm_openfiles(Nflag ? execfile : NULL, 163 Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL) 164 errx(1, "%s", errbuf); 165 if (kvm_nlist(kd, nl) != 0 || 166 kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1) 167 errx(1, "%s", kvm_geterr(kd)); 168 if (version != KTR_VERSION) 169 errx(1, "ktr version mismatch"); 170 if (iflag) { 171 if (fstat(in, &sb) == -1) 172 errx(1, "stat"); 173 entries = sb.st_size / sizeof(*buf); 174 index = 0; 175 buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0); 176 if (buf == MAP_FAILED) 177 errx(1, "mmap"); 178 } else { 179 if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries)) 180 == -1) 181 errx(1, "%s", kvm_geterr(kd)); 182 if ((buf = malloc(sizeof(*buf) * entries)) == NULL) 183 err(1, NULL); 184 if (kvm_read(kd, nl[2].n_value, &index, sizeof(index)) == -1 || 185 kvm_read(kd, nl[3].n_value, &bufptr, 186 sizeof(bufptr)) == -1 || 187 kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1) 188 errx(1, "%s", kvm_geterr(kd)); 189 } 190 191 /* 192 * Print a nice header. 193 */ 194 if (!qflag) { 195 fprintf(out, "%-6s ", "index"); 196 if (cflag) 197 fprintf(out, "%-3s ", "cpu"); 198 if (tflag) 199 fprintf(out, "%-16s ", "timestamp"); 200 if (fflag) 201 fprintf(out, "%-40s ", "file and line"); 202 if (hflag) 203 fprintf(out, "%-18s ", "tid"); 204 fprintf(out, "%s", "trace"); 205 fprintf(out, "\n"); 206 207 fprintf(out, "------ "); 208 if (cflag) 209 fprintf(out, "--- "); 210 if (tflag) 211 fprintf(out, "---------------- "); 212 if (fflag) 213 fprintf(out, 214 "---------------------------------------- "); 215 if (hflag) 216 fprintf(out, "------------------ "); 217 fprintf(out, "----- "); 218 fprintf(out, "\n"); 219 } 220 221 /* 222 * Now tear through the trace buffer. 223 */ 224 if (!iflag) 225 i = (index - 1) % entries; 226 tlast = -1; 227 for (;;) { 228 if (buf[i].ktr_desc == NULL) 229 break; 230 if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc, 231 sizeof(desc)) == -1) 232 errx(1, "%s", kvm_geterr(kd)); 233 desc[sizeof(desc) - 1] = '\0'; 234 parm = 0; 235 for (p = desc; (c = *p++) != '\0';) { 236 if (c != '%') 237 continue; 238next: if ((c = *p++) == '\0') 239 break; 240 if (parm == KTR_PARMS) 241 errx(1, "too many parameters"); 242 switch (c) { 243 case '0': case '1': case '2': case '3': case '4': 244 case '5': case '6': case '7': case '8': case '9': 245 case '#': case '-': case ' ': case '+': case '\'': 246 case 'h': case 'l': case 'j': case 't': case 'z': 247 case 'q': case 'L': case '.': 248 goto next; 249 case 's': 250 if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm], 251 sbuf[parm], sizeof(sbuf[parm])) == -1) 252 strcpy(sbuf[parm], "(null)"); 253 sbuf[parm][sizeof(sbuf[0]) - 1] = '\0'; 254 parms[parm] = (u_long)sbuf[parm]; 255 parm++; 256 break; 257 default: 258 parms[parm] = buf[i].ktr_parms[parm]; 259 parm++; 260 break; 261 } 262 } 263 fprintf(out, "%6d ", i); 264 if (cflag) 265 fprintf(out, "%3d ", buf[i].ktr_cpu); 266 if (tflag) { 267 tnow = (uintmax_t)buf[i].ktr_timestamp; 268 if (rflag) { 269 if (tlast == -1) 270 tlast = tnow; 271 fprintf(out, "%16ju ", !iflag ? tlast - tnow : 272 tnow - tlast); 273 tlast = tnow; 274 } else 275 fprintf(out, "%16ju ", tnow); 276 } 277 if (fflag) { 278 if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf, 279 sizeof(fbuf)) == -1) 280 strcpy(fbuf, "(null)"); 281 snprintf(obuf, sizeof(obuf), "%s:%d", fbuf, 282 buf[i].ktr_line); 283 fprintf(out, "%-40s ", obuf); 284 } 285 if (hflag) 286 fprintf(out, "%p ", buf[i].ktr_thread); 287 fprintf(out, desc, parms[0], parms[1], parms[2], parms[3], 288 parms[4], parms[5]); 289 fprintf(out, "\n"); 290 if (!iflag) { 291 if (i == index) 292 break; 293 i = (i - 1) % entries; 294 } else { 295 if (++i == entries) 296 break; 297 } 298 } 299 300 return (0); 301} 302 303static void 304usage(void) 305{ 306 307 fprintf(stderr, USAGE); 308 exit(1); 309} 310