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 51 static void usage(void); 52 53 static struct nlist nl[] = { 54 { "_ktr_version" }, 55 { "_ktr_entries" }, 56 { "_ktr_idx" }, 57 { "_ktr_buf" }, 58 { NULL } 59 }; 60 61 static int cflag; 62 static int fflag; 63 static int Mflag; 64 static int Nflag; 65 static int qflag; 66 static int rflag; 67 static int tflag; 68 static int iflag; 69 static int hflag; 70 71 static char corefile[PATH_MAX]; 72 static char execfile[PATH_MAX]; 73 74 static char desc[SBUFLEN]; 75 static char errbuf[_POSIX2_LINE_MAX]; 76 static char fbuf[PATH_MAX]; 77 static char obuf[PATH_MAX]; 78 static char sbuf[KTR_PARMS][SBUFLEN]; 79 80 /* 81 * Reads the ktr trace buffer from kernel memory and prints the trace entries. 82 */ 83 int 84 main(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, index2; 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 kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1) 189 errx(1, "%s", kvm_geterr(kd)); 190 } 191 192 /* 193 * Print a nice header. 194 */ 195 if (!qflag) { 196 fprintf(out, "%-6s ", "index"); 197 if (cflag) 198 fprintf(out, "%-3s ", "cpu"); 199 if (tflag) 200 fprintf(out, "%-16s ", "timestamp"); 201 if (fflag) 202 fprintf(out, "%-40s ", "file and line"); 203 if (hflag) 204 fprintf(out, "%-18s ", "tid"); 205 fprintf(out, "%s", "trace"); 206 fprintf(out, "\n"); 207 208 fprintf(out, "------ "); 209 if (cflag) 210 fprintf(out, "--- "); 211 if (tflag) 212 fprintf(out, "---------------- "); 213 if (fflag) 214 fprintf(out, 215 "---------------------------------------- "); 216 if (hflag) 217 fprintf(out, "------------------ "); 218 fprintf(out, "----- "); 219 fprintf(out, "\n"); 220 } 221 222 /* 223 * Now tear through the trace buffer. 224 */ 225 if (!iflag) { 226 i = index - 1; 227 if (i < 0) 228 i = entries - 1; 229 } 230 tlast = -1; 231 for (;;) { 232 if (buf[i].ktr_desc == NULL) 233 break; 234 if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc, 235 sizeof(desc)) == -1) 236 errx(1, "%s", kvm_geterr(kd)); 237 desc[sizeof(desc) - 1] = '\0'; 238 parm = 0; 239 for (p = desc; (c = *p++) != '\0';) { 240 if (c != '%') 241 continue; 242 next: if ((c = *p++) == '\0') 243 break; 244 if (parm == KTR_PARMS) 245 errx(1, "too many parameters in \"%s\"", desc); 246 switch (c) { 247 case '0': case '1': case '2': case '3': case '4': 248 case '5': case '6': case '7': case '8': case '9': 249 case '#': case '-': case ' ': case '+': case '\'': 250 case 'h': case 'l': case 'j': case 't': case 'z': 251 case 'q': case 'L': case '.': 252 goto next; 253 case 's': 254 if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm], 255 sbuf[parm], sizeof(sbuf[parm])) == -1) 256 strcpy(sbuf[parm], "(null)"); 257 sbuf[parm][sizeof(sbuf[0]) - 1] = '\0'; 258 parms[parm] = (u_long)sbuf[parm]; 259 parm++; 260 break; 261 default: 262 parms[parm] = buf[i].ktr_parms[parm]; 263 parm++; 264 break; 265 } 266 } 267 fprintf(out, "%6d ", i); 268 if (cflag) 269 fprintf(out, "%3d ", buf[i].ktr_cpu); 270 if (tflag) { 271 tnow = (uintmax_t)buf[i].ktr_timestamp; 272 if (rflag) { 273 if (tlast == -1) 274 tlast = tnow; 275 fprintf(out, "%16ju ", !iflag ? tlast - tnow : 276 tnow - tlast); 277 tlast = tnow; 278 } else 279 fprintf(out, "%16ju ", tnow); 280 } 281 if (fflag) { 282 if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf, 283 sizeof(fbuf)) == -1) 284 strcpy(fbuf, "(null)"); 285 snprintf(obuf, sizeof(obuf), "%s:%d", fbuf, 286 buf[i].ktr_line); 287 fprintf(out, "%-40s ", obuf); 288 } 289 if (hflag) 290 fprintf(out, "%p ", buf[i].ktr_thread); 291 fprintf(out, desc, parms[0], parms[1], parms[2], parms[3], 292 parms[4], parms[5]); 293 fprintf(out, "\n"); 294 if (!iflag) { 295 /* 296 * 'index' and 'index2' are the values of 'ktr_idx' 297 * before and after the KTR buffer was copied into 298 * 'buf'. Since the KTR entries between 'index' and 299 * 'index2' were in flux while the KTR buffer was 300 * being copied to userspace we don't dump them. 301 */ 302 if (i == index2) 303 break; 304 if (--i < 0) 305 i = entries - 1; 306 } else { 307 if (++i == entries) 308 break; 309 } 310 } 311 312 return (0); 313 } 314 315 static void 316 usage(void) 317 { 318 319 fprintf(stderr, USAGE); 320 exit(1); 321 } 322