db_capture.c revision 283248
11541Srgrimes/*- 21541Srgrimes * Copyright (c) 2007 Robert N. M. Watson 31541Srgrimes * All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 71541Srgrimes * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 91541Srgrimes * notice, this list of conditions and the following disclaimer. 101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111541Srgrimes * notice, this list of conditions and the following disclaimer in the 121541Srgrimes * documentation and/or other materials provided with the distribution. 131541Srgrimes * 141541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241541Srgrimes * SUCH DAMAGE. 251541Srgrimes */ 261541Srgrimes 271541Srgrimes/* 281541Srgrimes * DDB capture support: capture kernel debugger output into a fixed-size 291541Srgrimes * buffer for later dumping to disk or extraction from user space. 301541Srgrimes */ 311541Srgrimes 321541Srgrimes#include <sys/cdefs.h> 331541Srgrimes__FBSDID("$FreeBSD: head/sys/ddb/db_capture.c 283248 2015-05-21 15:16:18Z pfg $"); 341541Srgrimes 351541Srgrimes#include "opt_ddb.h" 3622521Sdyson 3736511Speter#include <sys/param.h> 381541Srgrimes#include <sys/conf.h> 391541Srgrimes#include <sys/kernel.h> 4022521Sdyson#include <sys/kerneldump.h> 412175Spaul#include <sys/malloc.h> 422175Spaul#include <sys/msgbuf.h> 432175Spaul#include <sys/priv.h> 4433054Sbde#include <sys/sx.h> 4533054Sbde#include <sys/sysctl.h> 469336Sdfr#include <sys/systm.h> 471541Srgrimes 481541Srgrimes#include <ddb/ddb.h> 491541Srgrimes#include <ddb/db_lex.h> 501541Srgrimes 511541Srgrimes/* 521541Srgrimes * While it would be desirable to use a small block-sized buffer and dump 531541Srgrimes * incrementally to disk in fixed-size blocks, it's not possible to enter 541541Srgrimes * kernel dumper routines without restarting the kernel, which is undesirable 551541Srgrimes * in the midst of debugging. Instead, we maintain a large static global 5610222Sdfr * buffer that we fill from DDB's output routines. 5710222Sdfr * 5810222Sdfr * We enforce an invariant at runtime that buffer sizes are even multiples of 5910222Sdfr * the textdump block size, which is a design choice that we might want to 6010222Sdfr * reconsider. 6110222Sdfr */ 6210222Sdfrstatic MALLOC_DEFINE(M_DDB_CAPTURE, "ddb_capture", "DDB capture buffer"); 631541Srgrimes 641541Srgrimes#ifndef DDB_CAPTURE_DEFAULTBUFSIZE 651541Srgrimes#define DDB_CAPTURE_DEFAULTBUFSIZE 48*1024 661541Srgrimes#endif 671541Srgrimes#ifndef DDB_CAPTURE_MAXBUFSIZE 681541Srgrimes#define DDB_CAPTURE_MAXBUFSIZE 5*1024*1024 691541Srgrimes#endif 701541Srgrimes#define DDB_CAPTURE_FILENAME "ddb.txt" /* Captured DDB output. */ 711541Srgrimes 721541Srgrimesstatic char *db_capture_buf; 731541Srgrimesstatic u_int db_capture_bufsize = DDB_CAPTURE_DEFAULTBUFSIZE; 741541Srgrimesstatic u_int db_capture_maxbufsize = DDB_CAPTURE_MAXBUFSIZE; /* Read-only. */ 751541Srgrimesstatic u_int db_capture_bufoff; /* Next location to write in buffer. */ 761541Srgrimesstatic u_int db_capture_bufpadding; /* Amount of zero padding. */ 771541Srgrimesstatic int db_capture_inpager; /* Suspend capture in pager. */ 781541Srgrimesstatic int db_capture_inprogress; /* DDB capture currently in progress. */ 791541Srgrimes 801541Srgrimesstruct sx db_capture_sx; /* Lock against user thread races. */ 811541SrgrimesSX_SYSINIT(db_capture_sx, &db_capture_sx, "db_capture_sx"); 821541Srgrimes 831541Srgrimesstatic SYSCTL_NODE(_debug_ddb, OID_AUTO, capture, CTLFLAG_RW, 0, 841541Srgrimes "DDB capture options"); 851541Srgrimes 861541SrgrimesSYSCTL_UINT(_debug_ddb_capture, OID_AUTO, bufoff, CTLFLAG_RD, 871541Srgrimes &db_capture_bufoff, 0, "Bytes of data in DDB capture buffer"); 881541Srgrimes 891541SrgrimesSYSCTL_UINT(_debug_ddb_capture, OID_AUTO, maxbufsize, CTLFLAG_RD, 901541Srgrimes &db_capture_maxbufsize, 0, 911541Srgrimes "Maximum value for debug.ddb.capture.bufsize"); 921541Srgrimes 931541SrgrimesSYSCTL_INT(_debug_ddb_capture, OID_AUTO, inprogress, CTLFLAG_RD, 941541Srgrimes &db_capture_inprogress, 0, "DDB output capture in progress"); 951541Srgrimes 961541Srgrimes/* 971541Srgrimes * Boot-time allocation of the DDB capture buffer, if any. Force all buffer 981541Srgrimes * sizes, including the maximum size, to be rounded to block sizes. 991541Srgrimes */ 1001541Srgrimesstatic void 1011541Srgrimesdb_capture_sysinit(__unused void *dummy) 1021541Srgrimes{ 1039336Sdfr 1041541Srgrimes TUNABLE_INT_FETCH("debug.ddb.capture.bufsize", &db_capture_bufsize); 1051541Srgrimes db_capture_maxbufsize = roundup(db_capture_maxbufsize, 1061541Srgrimes TEXTDUMP_BLOCKSIZE); 1071541Srgrimes db_capture_bufsize = roundup(db_capture_bufsize, TEXTDUMP_BLOCKSIZE); 10836503Speter if (db_capture_bufsize > db_capture_maxbufsize) 1099336Sdfr db_capture_bufsize = db_capture_maxbufsize; 1109336Sdfr if (db_capture_bufsize != 0) 1119336Sdfr db_capture_buf = malloc(db_capture_bufsize, M_DDB_CAPTURE, 1121541Srgrimes M_WAITOK); 1139336Sdfr} 1149336SdfrSYSINIT(db_capture, SI_SUB_DDB_SERVICES, SI_ORDER_ANY, db_capture_sysinit, 1159336Sdfr NULL); 1169336Sdfr 1179336Sdfr/* 1189336Sdfr * Run-time adjustment of the capture buffer. 1199336Sdfr */ 1209336Sdfrstatic int 1219336Sdfrsysctl_debug_ddb_capture_bufsize(SYSCTL_HANDLER_ARGS) 1229336Sdfr{ 1239336Sdfr u_int len, size; 1249336Sdfr char *buf; 12536503Speter int error; 12636503Speter 12736503Speter size = db_capture_bufsize; 1289336Sdfr error = sysctl_handle_int(oidp, &size, 0, req); 1299336Sdfr if (error || req->newptr == NULL) 1303305Sphk return (error); 1313305Sphk size = roundup(size, TEXTDUMP_BLOCKSIZE); 1329336Sdfr if (size > db_capture_maxbufsize) 1339336Sdfr return (EINVAL); 1349336Sdfr sx_xlock(&db_capture_sx); 1351541Srgrimes if (size != 0) { 1361541Srgrimes /* 1379336Sdfr * Potentially the buffer is quite large, so if we can't 1389336Sdfr * allocate it, fail rather than waiting. 1399336Sdfr */ 1409336Sdfr buf = malloc(size, M_DDB_CAPTURE, M_NOWAIT); 1419336Sdfr if (buf == NULL) { 1429336Sdfr sx_xunlock(&db_capture_sx); 1439336Sdfr return (ENOMEM); 1449336Sdfr } 1459336Sdfr len = min(db_capture_bufoff, size); 1461541Srgrimes } else { 1479336Sdfr buf = NULL; 1489336Sdfr len = 0; 1499336Sdfr } 1509336Sdfr if (db_capture_buf != NULL && buf != NULL) 1519336Sdfr bcopy(db_capture_buf, buf, len); 1529336Sdfr if (db_capture_buf != NULL) 1531541Srgrimes free(db_capture_buf, M_DDB_CAPTURE); 1549336Sdfr db_capture_bufoff = len; 1559336Sdfr db_capture_buf = buf; 1569336Sdfr db_capture_bufsize = size; 1579336Sdfr sx_xunlock(&db_capture_sx); 1589336Sdfr 1599336Sdfr KASSERT(db_capture_bufoff <= db_capture_bufsize, 1609336Sdfr ("sysctl_debug_ddb_capture_bufsize: bufoff > bufsize")); 1619336Sdfr KASSERT(db_capture_bufsize <= db_capture_maxbufsize, 1629336Sdfr ("sysctl_debug_ddb_capture_maxbufsize: bufsize > maxbufsize")); 16336503Speter 16436503Speter return (0); 1659336Sdfr} 1669336SdfrSYSCTL_PROC(_debug_ddb_capture, OID_AUTO, bufsize, CTLTYPE_UINT|CTLFLAG_RW, 1679336Sdfr 0, 0, sysctl_debug_ddb_capture_bufsize, "IU", 1689336Sdfr "Size of DDB capture buffer"); 1699336Sdfr 1701541Srgrimes/* 1719336Sdfr * Sysctl to read out the capture buffer from userspace. We require 1729336Sdfr * privilege as sensitive process/memory information may be accessed. 1739336Sdfr */ 1749336Sdfrstatic int 1759336Sdfrsysctl_debug_ddb_capture_data(SYSCTL_HANDLER_ARGS) 1769336Sdfr{ 1779336Sdfr int error; 1789336Sdfr char ch; 1799336Sdfr 1801541Srgrimes error = priv_check(req->td, PRIV_DDB_CAPTURE); 1811541Srgrimes if (error) 1829336Sdfr return (error); 1839336Sdfr 1849336Sdfr sx_slock(&db_capture_sx); 1859336Sdfr error = SYSCTL_OUT(req, db_capture_buf, db_capture_bufoff); 1869336Sdfr sx_sunlock(&db_capture_sx); 1879336Sdfr if (error) 1889336Sdfr return (error); 1899336Sdfr ch = '\0'; 1909336Sdfr return (SYSCTL_OUT(req, &ch, sizeof(ch))); 1919336Sdfr} 1929336SdfrSYSCTL_PROC(_debug_ddb_capture, OID_AUTO, data, CTLTYPE_STRING | CTLFLAG_RD, 1939336Sdfr NULL, 0, sysctl_debug_ddb_capture_data, "A", "DDB capture data"); 1949336Sdfr 1959336Sdfr/* 1969336Sdfr * Routines for capturing DDB output into a fixed-size buffer. These are 19736503Speter * invoked from DDB's input and output routines. If we hit the limit on the 1989336Sdfr * buffer, we simply drop further data. 1991541Srgrimes */ 2001541Srgrimesvoid 2011541Srgrimesdb_capture_write(char *buffer, u_int buflen) 2029336Sdfr{ 2031541Srgrimes u_int len; 2049336Sdfr 2059336Sdfr if (db_capture_inprogress == 0 || db_capture_inpager) 2069336Sdfr return; 2079336Sdfr len = min(buflen, db_capture_bufsize - db_capture_bufoff); 20836503Speter bcopy(buffer, db_capture_buf + db_capture_bufoff, len); 20936503Speter db_capture_bufoff += len; 2109336Sdfr 2119336Sdfr KASSERT(db_capture_bufoff <= db_capture_bufsize, 2129336Sdfr ("db_capture_write: bufoff > bufsize")); 2139336Sdfr} 2149336Sdfr 2159336Sdfrvoid 2169336Sdfrdb_capture_writech(char ch) 2179336Sdfr{ 2189336Sdfr 2199336Sdfr return (db_capture_write(&ch, sizeof(ch))); 2209336Sdfr} 2219336Sdfr 2229336Sdfrvoid 2239336Sdfrdb_capture_enterpager(void) 2249336Sdfr{ 2259336Sdfr 2269336Sdfr db_capture_inpager = 1; 2279336Sdfr} 2289336Sdfr 2299336Sdfrvoid 2309336Sdfrdb_capture_exitpager(void) 2319336Sdfr{ 2329336Sdfr 2339336Sdfr db_capture_inpager = 0; 2349336Sdfr} 2359336Sdfr 2369336Sdfr/* 2379336Sdfr * Zero out any bytes left in the last block of the DDB capture buffer. This 2389336Sdfr * is run shortly before writing the blocks to disk, rather than when output 2399336Sdfr * capture is stopped, in order to avoid injecting nul's into the middle of 2409336Sdfr * output. 2419336Sdfr */ 2429336Sdfrstatic void 2439336Sdfrdb_capture_zeropad(void) 2449336Sdfr{ 2459336Sdfr u_int len; 2469336Sdfr 2479336Sdfr len = min(TEXTDUMP_BLOCKSIZE, (db_capture_bufsize - 2489336Sdfr db_capture_bufoff) % TEXTDUMP_BLOCKSIZE); 2499336Sdfr bzero(db_capture_buf + db_capture_bufoff, len); 2509336Sdfr db_capture_bufpadding = len; 2519336Sdfr} 2521541Srgrimes 2531541Srgrimes/* 2541541Srgrimes * Reset capture state, which flushes buffers. 2551541Srgrimes */ 2561541Srgrimesstatic void 2571541Srgrimesdb_capture_reset(void) 2581541Srgrimes{ 2591541Srgrimes 2601541Srgrimes db_capture_inprogress = 0; 2611541Srgrimes db_capture_bufoff = 0; 2621541Srgrimes db_capture_bufpadding = 0; 2631541Srgrimes} 2641541Srgrimes 2651541Srgrimes/* 2661541Srgrimes * Start capture. Only one session is allowed at any time, but we may 2679336Sdfr * continue a previous session, so the buffer isn't reset. 2689336Sdfr */ 2699336Sdfrstatic void 2709336Sdfrdb_capture_start(void) 2719336Sdfr{ 2729336Sdfr 2739336Sdfr if (db_capture_inprogress) { 2749336Sdfr db_printf("Capture already started\n"); 2759336Sdfr return; 2769336Sdfr } 2771541Srgrimes db_capture_inprogress = 1; 2781541Srgrimes} 27936503Speter 2809336Sdfr/* 2811541Srgrimes * Terminate DDB output capture--real work is deferred to db_capture_dump, 2821541Srgrimes * which executes outside of the DDB context. We don't zero pad here because 2831541Srgrimes * capture may be started again before the dump takes place. 2841541Srgrimes */ 2851541Srgrimesstatic void 28636503Speterdb_capture_stop(void) 2879336Sdfr{ 2881541Srgrimes 2891541Srgrimes if (db_capture_inprogress == 0) { 2901541Srgrimes db_printf("Capture not started\n"); 2911541Srgrimes return; 2921541Srgrimes } 2931541Srgrimes db_capture_inprogress = 0; 2941541Srgrimes} 2951541Srgrimes 2968876Srgrimes/* 2971541Srgrimes * Dump DDB(4) captured output (and resets capture buffers). 2981541Srgrimes */ 2991541Srgrimesvoid 3001541Srgrimesdb_capture_dump(struct dumperinfo *di) 30136503Speter{ 30236503Speter u_int offset; 3039336Sdfr 3049336Sdfr if (db_capture_bufoff == 0) 3059336Sdfr return; 3069336Sdfr 3079336Sdfr db_capture_zeropad(); 3081541Srgrimes textdump_mkustar(textdump_block_buffer, DDB_CAPTURE_FILENAME, 3091541Srgrimes db_capture_bufoff); 3101541Srgrimes (void)textdump_writenextblock(di, textdump_block_buffer); 3111541Srgrimes for (offset = 0; offset < db_capture_bufoff + db_capture_bufpadding; 3121541Srgrimes offset += TEXTDUMP_BLOCKSIZE) 3131541Srgrimes (void)textdump_writenextblock(di, db_capture_buf + offset); 3141541Srgrimes db_capture_bufoff = 0; 3151541Srgrimes db_capture_bufpadding = 0; 3161541Srgrimes} 3171541Srgrimes 3181541Srgrimes/*- 3191541Srgrimes * DDB(4) command to manage capture: 32036511Speter * 32136503Speter * capture on - start DDB output capture 3229336Sdfr * capture off - stop DDB output capture 3239336Sdfr * capture reset - reset DDB capture buffer (also stops capture) 3249336Sdfr * capture status - print DDB output capture status 3251541Srgrimes */ 3261541Srgrimesstatic void 3271541Srgrimesdb_capture_usage(void) 3281541Srgrimes{ 3291541Srgrimes 3301541Srgrimes db_error("capture [on|off|reset|status]\n"); 3311541Srgrimes} 3321541Srgrimes 3331541Srgrimesvoid 3349336Sdfrdb_capture_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 3359336Sdfr{ 3361541Srgrimes int t; 3371541Srgrimes 3389336Sdfr t = db_read_token(); 3391541Srgrimes if (t != tIDENT) { 3401541Srgrimes db_capture_usage(); 3411541Srgrimes return; 3429336Sdfr } 3439336Sdfr if (db_read_token() != tEOL) 3441541Srgrimes db_error("?\n"); 3451541Srgrimes if (strcmp(db_tok_string, "on") == 0) 3461541Srgrimes db_capture_start(); 3479336Sdfr else if (strcmp(db_tok_string, "off") == 0) 3489336Sdfr db_capture_stop(); 3499336Sdfr else if (strcmp(db_tok_string, "reset") == 0) 3509336Sdfr db_capture_reset(); 3519336Sdfr else if (strcmp(db_tok_string, "status") == 0) { 3529336Sdfr db_printf("%u/%u bytes used\n", db_capture_bufoff, 3539336Sdfr db_capture_bufsize); 3549336Sdfr if (db_capture_inprogress) 3559336Sdfr db_printf("capture is on\n"); 3569336Sdfr else 3579336Sdfr db_printf("capture is off\n"); 3581541Srgrimes } else 3599336Sdfr db_capture_usage(); 3601541Srgrimes} 3611541Srgrimes