ktrdump.c revision 278327
193504Sjake/*- 293504Sjake * Copyright (c) 2002 Jake Burkholder 3129574Srwatson * Copyright (c) 2004 Robert Watson 493504Sjake * All rights reserved. 593504Sjake * 693504Sjake * Redistribution and use in source and binary forms, with or without 793504Sjake * modification, are permitted provided that the following conditions 893504Sjake * are met: 993504Sjake * 1. Redistributions of source code must retain the above copyright 1093504Sjake * notice, this list of conditions and the following disclaimer. 1193504Sjake * 2. Redistributions in binary form must reproduce the above copyright 1293504Sjake * notice, this list of conditions and the following disclaimer in the 1393504Sjake * documentation and/or other materials provided with the distribution. 1493504Sjake * 1593504Sjake * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1693504Sjake * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1793504Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1893504Sjake * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1993504Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2093504Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2193504Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2293504Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2393504Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2493504Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2593504Sjake * SUCH DAMAGE. 2693504Sjake */ 2793504Sjake 2893504Sjake#include <sys/cdefs.h> 2993504Sjake__FBSDID("$FreeBSD: head/usr.bin/ktrdump/ktrdump.c 278327 2015-02-06 19:41:23Z jhb $"); 3093504Sjake 3193504Sjake#include <sys/types.h> 3293504Sjake#include <sys/ktr.h> 33103791Sjeff#include <sys/mman.h> 34103791Sjeff#include <sys/stat.h> 3593504Sjake 3693504Sjake#include <err.h> 3793504Sjake#include <fcntl.h> 3893504Sjake#include <kvm.h> 3993504Sjake#include <limits.h> 4093504Sjake#include <nlist.h> 4193617Sjake#include <stdint.h> 4293504Sjake#include <stdio.h> 4393504Sjake#include <stdlib.h> 4493504Sjake#include <string.h> 4593504Sjake#include <unistd.h> 4693504Sjake 4793504Sjake#define SBUFLEN 128 4893504Sjake#define USAGE \ 49278327Sjhb "usage: ktrdump [-cfqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n" 5093504Sjake 5193504Sjakestatic void usage(void); 5293504Sjake 5393504Sjakestatic struct nlist nl[] = { 5493504Sjake { "_ktr_version" }, 5593504Sjake { "_ktr_entries" }, 5693504Sjake { "_ktr_idx" }, 5793504Sjake { "_ktr_buf" }, 5893504Sjake { NULL } 5993504Sjake}; 6093504Sjake 6193504Sjakestatic int cflag; 6293504Sjakestatic int fflag; 63278327Sjhbstatic int Mflag; 64278327Sjhbstatic int Nflag; 65129559Srwatsonstatic int qflag; 66129574Srwatsonstatic int rflag; 6793504Sjakestatic int tflag; 68103791Sjeffstatic int iflag; 69217873Sdchaginstatic int hflag; 7093504Sjake 7193504Sjakestatic char corefile[PATH_MAX]; 7293504Sjakestatic char execfile[PATH_MAX]; 7393504Sjake 7493504Sjakestatic char desc[SBUFLEN]; 7593504Sjakestatic char errbuf[_POSIX2_LINE_MAX]; 7693504Sjakestatic char fbuf[PATH_MAX]; 7793504Sjakestatic char obuf[PATH_MAX]; 7893504Sjakestatic char sbuf[KTR_PARMS][SBUFLEN]; 7993504Sjake 8093504Sjake/* 8193504Sjake * Reads the ktr trace buffer from kernel memory and prints the trace entries. 8293504Sjake */ 8393504Sjakeint 8493504Sjakemain(int ac, char **av) 8593504Sjake{ 8693504Sjake u_long parms[KTR_PARMS]; 8793504Sjake struct ktr_entry *buf; 88129574Srwatson uintmax_t tlast, tnow; 89243046Sjeff unsigned long bufptr; 90103791Sjeff struct stat sb; 9193504Sjake kvm_t *kd; 9293504Sjake FILE *out; 9393504Sjake char *p; 9493504Sjake int version; 9593504Sjake int entries; 96263196Sneel int index, index2; 9793504Sjake int parm; 98103791Sjeff int in; 9993504Sjake int c; 100135842Sjulian int i = 0; 10193504Sjake 10293504Sjake /* 10393504Sjake * Parse commandline arguments. 10493504Sjake */ 10593504Sjake out = stdout; 106278327Sjhb while ((c = getopt(ac, av, "cfqrtHe:i:m:M:N:o:")) != -1) 10793504Sjake switch (c) { 10893504Sjake case 'c': 10993504Sjake cflag = 1; 11093504Sjake break; 111278327Sjhb case 'N': 11293504Sjake case 'e': 113104586Skris if (strlcpy(execfile, optarg, sizeof(execfile)) 114104586Skris >= sizeof(execfile)) 115104586Skris errx(1, "%s: File name too long", optarg); 116278327Sjhb Nflag = 1; 11793504Sjake break; 11893504Sjake case 'f': 11993504Sjake fflag = 1; 12093504Sjake break; 121103791Sjeff case 'i': 122103791Sjeff iflag = 1; 123103791Sjeff if ((in = open(optarg, O_RDONLY)) == -1) 124103791Sjeff err(1, "%s", optarg); 125103791Sjeff break; 126278327Sjhb case 'M': 12793504Sjake case 'm': 128104586Skris if (strlcpy(corefile, optarg, sizeof(corefile)) 129104586Skris >= sizeof(corefile)) 130104586Skris errx(1, "%s: File name too long", optarg); 131278327Sjhb Mflag = 1; 13293504Sjake break; 13393504Sjake case 'o': 13493504Sjake if ((out = fopen(optarg, "w")) == NULL) 13593504Sjake err(1, "%s", optarg); 13693504Sjake break; 137129559Srwatson case 'q': 138129559Srwatson qflag++; 139129559Srwatson break; 140129574Srwatson case 'r': 141129574Srwatson rflag = 1; 142129574Srwatson break; 14393504Sjake case 't': 14493504Sjake tflag = 1; 14593504Sjake break; 146217873Sdchagin case 'H': 147217873Sdchagin hflag = 1; 148217873Sdchagin break; 14993504Sjake case '?': 15093504Sjake default: 15193504Sjake usage(); 15293504Sjake } 15393504Sjake ac -= optind; 15493504Sjake av += optind; 15593504Sjake if (ac != 0) 15693504Sjake usage(); 15793504Sjake 15893504Sjake /* 15993504Sjake * Open our execfile and corefile, resolve needed symbols and read in 16093504Sjake * the trace buffer. 16193504Sjake */ 162278327Sjhb if ((kd = kvm_openfiles(Nflag ? execfile : NULL, 163278327Sjhb Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL) 16493504Sjake errx(1, "%s", errbuf); 16593504Sjake if (kvm_nlist(kd, nl) != 0 || 16693504Sjake kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1) 16793504Sjake errx(1, "%s", kvm_geterr(kd)); 16893504Sjake if (version != KTR_VERSION) 16993504Sjake errx(1, "ktr version mismatch"); 170103791Sjeff if (iflag) { 171103791Sjeff if (fstat(in, &sb) == -1) 172103791Sjeff errx(1, "stat"); 173103791Sjeff entries = sb.st_size / sizeof(*buf); 174103791Sjeff index = 0; 175103791Sjeff buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0); 176103791Sjeff if (buf == MAP_FAILED) 177103791Sjeff errx(1, "mmap"); 178103791Sjeff } else { 179103791Sjeff if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries)) 180103791Sjeff == -1) 181103791Sjeff errx(1, "%s", kvm_geterr(kd)); 182103791Sjeff if ((buf = malloc(sizeof(*buf) * entries)) == NULL) 183103791Sjeff err(1, NULL); 184103791Sjeff if (kvm_read(kd, nl[2].n_value, &index, sizeof(index)) == -1 || 185243046Sjeff kvm_read(kd, nl[3].n_value, &bufptr, 186243046Sjeff sizeof(bufptr)) == -1 || 187263196Sneel kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 || 188263196Sneel kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1) 189103791Sjeff errx(1, "%s", kvm_geterr(kd)); 190103791Sjeff } 19193504Sjake 19293504Sjake /* 19393504Sjake * Print a nice header. 19493504Sjake */ 195129559Srwatson if (!qflag) { 196129559Srwatson fprintf(out, "%-6s ", "index"); 197129559Srwatson if (cflag) 198129559Srwatson fprintf(out, "%-3s ", "cpu"); 199129559Srwatson if (tflag) 200129559Srwatson fprintf(out, "%-16s ", "timestamp"); 201129559Srwatson if (fflag) 202129559Srwatson fprintf(out, "%-40s ", "file and line"); 203217873Sdchagin if (hflag) 204217873Sdchagin fprintf(out, "%-18s ", "tid"); 205129559Srwatson fprintf(out, "%s", "trace"); 206129559Srwatson fprintf(out, "\n"); 20793504Sjake 208129559Srwatson fprintf(out, "------ "); 209129559Srwatson if (cflag) 210129559Srwatson fprintf(out, "--- "); 211129559Srwatson if (tflag) 212129559Srwatson fprintf(out, "---------------- "); 213129559Srwatson if (fflag) 214129559Srwatson fprintf(out, 215129559Srwatson "---------------------------------------- "); 216217873Sdchagin if (hflag) 217217873Sdchagin fprintf(out, "------------------ "); 218129559Srwatson fprintf(out, "----- "); 219129559Srwatson fprintf(out, "\n"); 220129559Srwatson } 22193504Sjake 22293504Sjake /* 22393504Sjake * Now tear through the trace buffer. 22493504Sjake */ 225263194Sneel if (!iflag) { 226263194Sneel i = index - 1; 227263194Sneel if (i < 0) 228263194Sneel i = entries - 1; 229263194Sneel } 230129574Srwatson tlast = -1; 23193504Sjake for (;;) { 23293504Sjake if (buf[i].ktr_desc == NULL) 23393504Sjake break; 23493504Sjake if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc, 23593504Sjake sizeof(desc)) == -1) 23693504Sjake errx(1, "%s", kvm_geterr(kd)); 23793504Sjake desc[sizeof(desc) - 1] = '\0'; 23893504Sjake parm = 0; 23993504Sjake for (p = desc; (c = *p++) != '\0';) { 24093504Sjake if (c != '%') 24193504Sjake continue; 242154274Sglebiusnext: if ((c = *p++) == '\0') 24393504Sjake break; 24493504Sjake if (parm == KTR_PARMS) 245260810Sglebius errx(1, "too many parameters in \"%s\"", desc); 24693504Sjake switch (c) { 247154274Sglebius case '0': case '1': case '2': case '3': case '4': 248154274Sglebius case '5': case '6': case '7': case '8': case '9': 249154274Sglebius case '#': case '-': case ' ': case '+': case '\'': 250154274Sglebius case 'h': case 'l': case 'j': case 't': case 'z': 251154274Sglebius case 'q': case 'L': case '.': 252154274Sglebius goto next; 25393504Sjake case 's': 25493504Sjake if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm], 25593504Sjake sbuf[parm], sizeof(sbuf[parm])) == -1) 25693504Sjake strcpy(sbuf[parm], "(null)"); 25793504Sjake sbuf[parm][sizeof(sbuf[0]) - 1] = '\0'; 25893504Sjake parms[parm] = (u_long)sbuf[parm]; 25993504Sjake parm++; 26093504Sjake break; 26193504Sjake default: 26293504Sjake parms[parm] = buf[i].ktr_parms[parm]; 26393504Sjake parm++; 26493504Sjake break; 26593504Sjake } 26693504Sjake } 26793504Sjake fprintf(out, "%6d ", i); 26893504Sjake if (cflag) 26993504Sjake fprintf(out, "%3d ", buf[i].ktr_cpu); 270129574Srwatson if (tflag) { 271129574Srwatson tnow = (uintmax_t)buf[i].ktr_timestamp; 272129574Srwatson if (rflag) { 273129574Srwatson if (tlast == -1) 274129574Srwatson tlast = tnow; 275153270Snjl fprintf(out, "%16ju ", !iflag ? tlast - tnow : 276153270Snjl tnow - tlast); 277129574Srwatson tlast = tnow; 278129574Srwatson } else 279129574Srwatson fprintf(out, "%16ju ", tnow); 280129574Srwatson } 28193504Sjake if (fflag) { 28293504Sjake if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf, 28393504Sjake sizeof(fbuf)) == -1) 28493504Sjake strcpy(fbuf, "(null)"); 28593504Sjake snprintf(obuf, sizeof(obuf), "%s:%d", fbuf, 28693504Sjake buf[i].ktr_line); 28793504Sjake fprintf(out, "%-40s ", obuf); 28893504Sjake } 289217873Sdchagin if (hflag) 290217873Sdchagin fprintf(out, "%p ", buf[i].ktr_thread); 29193504Sjake fprintf(out, desc, parms[0], parms[1], parms[2], parms[3], 29293504Sjake parms[4], parms[5]); 29393504Sjake fprintf(out, "\n"); 294103791Sjeff if (!iflag) { 295263196Sneel /* 296263196Sneel * 'index' and 'index2' are the values of 'ktr_idx' 297263196Sneel * before and after the KTR buffer was copied into 298263196Sneel * 'buf'. Since the KTR entries between 'index' and 299263196Sneel * 'index2' were in flux while the KTR buffer was 300263196Sneel * being copied to userspace we don't dump them. 301263196Sneel */ 302263196Sneel if (i == index2) 303103791Sjeff break; 304263194Sneel if (--i < 0) 305263194Sneel i = entries - 1; 306103791Sjeff } else { 307103791Sjeff if (++i == entries) 308103791Sjeff break; 309103791Sjeff } 31093504Sjake } 31193504Sjake 31293504Sjake return (0); 31393504Sjake} 31493504Sjake 31593504Sjakestatic void 31693504Sjakeusage(void) 31793504Sjake{ 318146466Sru 319146466Sru fprintf(stderr, USAGE); 320146466Sru exit(1); 32193504Sjake} 322