193504Sjake/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 493504Sjake * Copyright (c) 2002 Jake Burkholder 5129574Srwatson * Copyright (c) 2004 Robert Watson 693504Sjake * All rights reserved. 793504Sjake * 893504Sjake * Redistribution and use in source and binary forms, with or without 993504Sjake * modification, are permitted provided that the following conditions 1093504Sjake * are met: 1193504Sjake * 1. Redistributions of source code must retain the above copyright 1293504Sjake * notice, this list of conditions and the following disclaimer. 1393504Sjake * 2. Redistributions in binary form must reproduce the above copyright 1493504Sjake * notice, this list of conditions and the following disclaimer in the 1593504Sjake * documentation and/or other materials provided with the distribution. 1693504Sjake * 1793504Sjake * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1893504Sjake * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1993504Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2093504Sjake * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2193504Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2293504Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2393504Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2493504Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2593504Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2693504Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2793504Sjake * SUCH DAMAGE. 2893504Sjake */ 2993504Sjake 3093504Sjake#include <sys/cdefs.h> 3193504Sjake__FBSDID("$FreeBSD: stable/11/usr.bin/ktrdump/ktrdump.c 342706 2019-01-02 19:48:17Z jhb $"); 3293504Sjake 3393504Sjake#include <sys/types.h> 3493504Sjake#include <sys/ktr.h> 35103791Sjeff#include <sys/mman.h> 36103791Sjeff#include <sys/stat.h> 3793504Sjake 3893504Sjake#include <err.h> 3993504Sjake#include <fcntl.h> 4093504Sjake#include <kvm.h> 4193504Sjake#include <limits.h> 4293504Sjake#include <nlist.h> 4393617Sjake#include <stdint.h> 4493504Sjake#include <stdio.h> 4593504Sjake#include <stdlib.h> 4693504Sjake#include <string.h> 4793504Sjake#include <unistd.h> 4893504Sjake 4993504Sjake#define SBUFLEN 128 5093504Sjake#define USAGE \ 51342706Sjhb "usage: ktrdump [-cflqrtH] [-i ktrfile] [-M core] [-N system] [-o outfile]\n" 5293504Sjake 5393504Sjakestatic void usage(void); 5493504Sjake 5593504Sjakestatic struct nlist nl[] = { 5693504Sjake { "_ktr_version" }, 5793504Sjake { "_ktr_entries" }, 5893504Sjake { "_ktr_idx" }, 5993504Sjake { "_ktr_buf" }, 6093504Sjake { NULL } 6193504Sjake}; 6293504Sjake 6393504Sjakestatic int cflag; 6493504Sjakestatic int fflag; 65342706Sjhbstatic int lflag; 66278327Sjhbstatic int Mflag; 67278327Sjhbstatic int Nflag; 68129559Srwatsonstatic int qflag; 69129574Srwatsonstatic int rflag; 7093504Sjakestatic int tflag; 71103791Sjeffstatic int iflag; 72217873Sdchaginstatic int hflag; 7393504Sjake 7493504Sjakestatic char corefile[PATH_MAX]; 7593504Sjakestatic char execfile[PATH_MAX]; 7693504Sjake 7793504Sjakestatic char desc[SBUFLEN]; 7893504Sjakestatic char errbuf[_POSIX2_LINE_MAX]; 7993504Sjakestatic char fbuf[PATH_MAX]; 8093504Sjakestatic char obuf[PATH_MAX]; 8193504Sjakestatic char sbuf[KTR_PARMS][SBUFLEN]; 8293504Sjake 8393504Sjake/* 8493504Sjake * Reads the ktr trace buffer from kernel memory and prints the trace entries. 8593504Sjake */ 8693504Sjakeint 8793504Sjakemain(int ac, char **av) 8893504Sjake{ 8993504Sjake u_long parms[KTR_PARMS]; 9093504Sjake struct ktr_entry *buf; 91129574Srwatson uintmax_t tlast, tnow; 92243046Sjeff unsigned long bufptr; 93103791Sjeff struct stat sb; 9493504Sjake kvm_t *kd; 9593504Sjake FILE *out; 9693504Sjake char *p; 9793504Sjake int version; 9893504Sjake int entries; 99323863Smarkj int count; 100263196Sneel int index, index2; 10193504Sjake int parm; 102103791Sjeff int in; 10393504Sjake int c; 104135842Sjulian int i = 0; 10593504Sjake 10693504Sjake /* 10793504Sjake * Parse commandline arguments. 10893504Sjake */ 10993504Sjake out = stdout; 110342706Sjhb while ((c = getopt(ac, av, "cflqrtHe:i:m:M:N:o:")) != -1) 11193504Sjake switch (c) { 11293504Sjake case 'c': 11393504Sjake cflag = 1; 11493504Sjake break; 115278327Sjhb case 'N': 11693504Sjake case 'e': 117104586Skris if (strlcpy(execfile, optarg, sizeof(execfile)) 118104586Skris >= sizeof(execfile)) 119104586Skris errx(1, "%s: File name too long", optarg); 120278327Sjhb Nflag = 1; 12193504Sjake break; 12293504Sjake case 'f': 12393504Sjake fflag = 1; 12493504Sjake break; 125103791Sjeff case 'i': 126103791Sjeff iflag = 1; 127103791Sjeff if ((in = open(optarg, O_RDONLY)) == -1) 128103791Sjeff err(1, "%s", optarg); 129103791Sjeff break; 130342706Sjhb case 'l': 131342706Sjhb lflag = 1; 132342706Sjhb break; 133278327Sjhb case 'M': 13493504Sjake case 'm': 135104586Skris if (strlcpy(corefile, optarg, sizeof(corefile)) 136104586Skris >= sizeof(corefile)) 137104586Skris errx(1, "%s: File name too long", optarg); 138278327Sjhb Mflag = 1; 13993504Sjake break; 14093504Sjake case 'o': 14193504Sjake if ((out = fopen(optarg, "w")) == NULL) 14293504Sjake err(1, "%s", optarg); 14393504Sjake break; 144129559Srwatson case 'q': 145129559Srwatson qflag++; 146129559Srwatson break; 147129574Srwatson case 'r': 148129574Srwatson rflag = 1; 149129574Srwatson break; 15093504Sjake case 't': 15193504Sjake tflag = 1; 15293504Sjake break; 153217873Sdchagin case 'H': 154217873Sdchagin hflag = 1; 155217873Sdchagin break; 15693504Sjake case '?': 15793504Sjake default: 15893504Sjake usage(); 15993504Sjake } 16093504Sjake ac -= optind; 16193504Sjake av += optind; 16293504Sjake if (ac != 0) 16393504Sjake usage(); 16493504Sjake 16593504Sjake /* 16693504Sjake * Open our execfile and corefile, resolve needed symbols and read in 16793504Sjake * the trace buffer. 16893504Sjake */ 169278327Sjhb if ((kd = kvm_openfiles(Nflag ? execfile : NULL, 170278327Sjhb Mflag ? corefile : NULL, NULL, O_RDONLY, errbuf)) == NULL) 17193504Sjake errx(1, "%s", errbuf); 172323863Smarkj count = kvm_nlist(kd, nl); 173323863Smarkj if (count == -1) 17493504Sjake errx(1, "%s", kvm_geterr(kd)); 175323863Smarkj if (count > 0) 176323863Smarkj errx(1, "failed to resolve ktr symbols"); 177323863Smarkj if (kvm_read(kd, nl[0].n_value, &version, sizeof(version)) == -1) 178323863Smarkj errx(1, "%s", kvm_geterr(kd)); 17993504Sjake if (version != KTR_VERSION) 18093504Sjake errx(1, "ktr version mismatch"); 181103791Sjeff if (iflag) { 182103791Sjeff if (fstat(in, &sb) == -1) 183103791Sjeff errx(1, "stat"); 184103791Sjeff entries = sb.st_size / sizeof(*buf); 185103791Sjeff index = 0; 186103791Sjeff buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, in, 0); 187103791Sjeff if (buf == MAP_FAILED) 188103791Sjeff errx(1, "mmap"); 189103791Sjeff } else { 190103791Sjeff if (kvm_read(kd, nl[1].n_value, &entries, sizeof(entries)) 191103791Sjeff == -1) 192103791Sjeff errx(1, "%s", kvm_geterr(kd)); 193103791Sjeff if ((buf = malloc(sizeof(*buf) * entries)) == NULL) 194103791Sjeff err(1, NULL); 195103791Sjeff if (kvm_read(kd, nl[2].n_value, &index, sizeof(index)) == -1 || 196243046Sjeff kvm_read(kd, nl[3].n_value, &bufptr, 197243046Sjeff sizeof(bufptr)) == -1 || 198263196Sneel kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 || 199263196Sneel kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1) 200103791Sjeff errx(1, "%s", kvm_geterr(kd)); 201103791Sjeff } 20293504Sjake 20393504Sjake /* 20493504Sjake * Print a nice header. 20593504Sjake */ 206129559Srwatson if (!qflag) { 207129559Srwatson fprintf(out, "%-6s ", "index"); 208129559Srwatson if (cflag) 209129559Srwatson fprintf(out, "%-3s ", "cpu"); 210129559Srwatson if (tflag) 211129559Srwatson fprintf(out, "%-16s ", "timestamp"); 212129559Srwatson if (fflag) 213129559Srwatson fprintf(out, "%-40s ", "file and line"); 214217873Sdchagin if (hflag) 215217873Sdchagin fprintf(out, "%-18s ", "tid"); 216129559Srwatson fprintf(out, "%s", "trace"); 217129559Srwatson fprintf(out, "\n"); 21893504Sjake 219129559Srwatson fprintf(out, "------ "); 220129559Srwatson if (cflag) 221129559Srwatson fprintf(out, "--- "); 222129559Srwatson if (tflag) 223129559Srwatson fprintf(out, "---------------- "); 224129559Srwatson if (fflag) 225129559Srwatson fprintf(out, 226129559Srwatson "---------------------------------------- "); 227217873Sdchagin if (hflag) 228217873Sdchagin fprintf(out, "------------------ "); 229129559Srwatson fprintf(out, "----- "); 230129559Srwatson fprintf(out, "\n"); 231129559Srwatson } 23293504Sjake 233342706Sjhb tlast = -1; 23493504Sjake /* 23593504Sjake * Now tear through the trace buffer. 236342706Sjhb * 237342706Sjhb * In "live" mode, find the oldest entry (first non-NULL entry 238342706Sjhb * after index2) and walk forward. Otherwise, start with the 239342706Sjhb * most recent entry and walk backwards. 24093504Sjake */ 241263194Sneel if (!iflag) { 242342706Sjhb if (lflag) { 243342706Sjhb i = index2 + 1 % entries; 244342706Sjhb while (buf[i].ktr_desc == NULL && i != index) { 245342706Sjhb i++; 246342706Sjhb if (i == entries) 247342706Sjhb i = 0; 248342706Sjhb } 249342706Sjhb } else { 250342706Sjhb i = index - 1; 251342706Sjhb if (i < 0) 252342706Sjhb i = entries - 1; 253342706Sjhb } 254263194Sneel } 255342706Sjhbdump_entries: 25693504Sjake for (;;) { 25793504Sjake if (buf[i].ktr_desc == NULL) 25893504Sjake break; 25993504Sjake if (kvm_read(kd, (u_long)buf[i].ktr_desc, desc, 26093504Sjake sizeof(desc)) == -1) 26193504Sjake errx(1, "%s", kvm_geterr(kd)); 26293504Sjake desc[sizeof(desc) - 1] = '\0'; 26393504Sjake parm = 0; 26493504Sjake for (p = desc; (c = *p++) != '\0';) { 26593504Sjake if (c != '%') 26693504Sjake continue; 267154274Sglebiusnext: if ((c = *p++) == '\0') 26893504Sjake break; 26993504Sjake if (parm == KTR_PARMS) 270260810Sglebius errx(1, "too many parameters in \"%s\"", desc); 27193504Sjake switch (c) { 272154274Sglebius case '0': case '1': case '2': case '3': case '4': 273154274Sglebius case '5': case '6': case '7': case '8': case '9': 274154274Sglebius case '#': case '-': case ' ': case '+': case '\'': 275154274Sglebius case 'h': case 'l': case 'j': case 't': case 'z': 276154274Sglebius case 'q': case 'L': case '.': 277154274Sglebius goto next; 27893504Sjake case 's': 27993504Sjake if (kvm_read(kd, (u_long)buf[i].ktr_parms[parm], 28093504Sjake sbuf[parm], sizeof(sbuf[parm])) == -1) 28193504Sjake strcpy(sbuf[parm], "(null)"); 28293504Sjake sbuf[parm][sizeof(sbuf[0]) - 1] = '\0'; 28393504Sjake parms[parm] = (u_long)sbuf[parm]; 28493504Sjake parm++; 28593504Sjake break; 28693504Sjake default: 28793504Sjake parms[parm] = buf[i].ktr_parms[parm]; 28893504Sjake parm++; 28993504Sjake break; 29093504Sjake } 29193504Sjake } 29293504Sjake fprintf(out, "%6d ", i); 29393504Sjake if (cflag) 29493504Sjake fprintf(out, "%3d ", buf[i].ktr_cpu); 295129574Srwatson if (tflag) { 296129574Srwatson tnow = (uintmax_t)buf[i].ktr_timestamp; 297129574Srwatson if (rflag) { 298129574Srwatson if (tlast == -1) 299129574Srwatson tlast = tnow; 300153270Snjl fprintf(out, "%16ju ", !iflag ? tlast - tnow : 301153270Snjl tnow - tlast); 302129574Srwatson tlast = tnow; 303129574Srwatson } else 304129574Srwatson fprintf(out, "%16ju ", tnow); 305129574Srwatson } 30693504Sjake if (fflag) { 30793504Sjake if (kvm_read(kd, (u_long)buf[i].ktr_file, fbuf, 30893504Sjake sizeof(fbuf)) == -1) 30993504Sjake strcpy(fbuf, "(null)"); 31093504Sjake snprintf(obuf, sizeof(obuf), "%s:%d", fbuf, 31193504Sjake buf[i].ktr_line); 31293504Sjake fprintf(out, "%-40s ", obuf); 31393504Sjake } 314217873Sdchagin if (hflag) 315217873Sdchagin fprintf(out, "%p ", buf[i].ktr_thread); 31693504Sjake fprintf(out, desc, parms[0], parms[1], parms[2], parms[3], 31793504Sjake parms[4], parms[5]); 31893504Sjake fprintf(out, "\n"); 319103791Sjeff if (!iflag) { 320263196Sneel /* 321263196Sneel * 'index' and 'index2' are the values of 'ktr_idx' 322263196Sneel * before and after the KTR buffer was copied into 323263196Sneel * 'buf'. Since the KTR entries between 'index' and 324263196Sneel * 'index2' were in flux while the KTR buffer was 325263196Sneel * being copied to userspace we don't dump them. 326263196Sneel */ 327342706Sjhb if (lflag) { 328342706Sjhb if (++i == entries) 329342706Sjhb i = 0; 330342706Sjhb if (i == index) 331342706Sjhb break; 332342706Sjhb } else { 333342706Sjhb if (i == index2) 334342706Sjhb break; 335342706Sjhb if (--i < 0) 336342706Sjhb i = entries - 1; 337342706Sjhb } 338103791Sjeff } else { 339103791Sjeff if (++i == entries) 340103791Sjeff break; 341103791Sjeff } 34293504Sjake } 34393504Sjake 344342706Sjhb /* 345342706Sjhb * In "live" mode, poll 'ktr_idx' periodically and dump any 346342706Sjhb * new entries since our last pass through the ring. 347342706Sjhb */ 348342706Sjhb if (lflag && !iflag) { 349342706Sjhb while (index == index2) { 350342706Sjhb usleep(50 * 1000); 351342706Sjhb if (kvm_read(kd, nl[2].n_value, &index2, 352342706Sjhb sizeof(index2)) == -1) 353342706Sjhb errx(1, "%s", kvm_geterr(kd)); 354342706Sjhb } 355342706Sjhb i = index; 356342706Sjhb index = index2; 357342706Sjhb if (kvm_read(kd, bufptr, buf, sizeof(*buf) * entries) == -1 || 358342706Sjhb kvm_read(kd, nl[2].n_value, &index2, sizeof(index2)) == -1) 359342706Sjhb errx(1, "%s", kvm_geterr(kd)); 360342706Sjhb goto dump_entries; 361342706Sjhb } 362342706Sjhb 36393504Sjake return (0); 36493504Sjake} 36593504Sjake 36693504Sjakestatic void 36793504Sjakeusage(void) 36893504Sjake{ 369146466Sru 370146466Sru fprintf(stderr, USAGE); 371146466Sru exit(1); 37293504Sjake} 373