The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_capture.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2007 Robert N. M. Watson
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 /*
   28  * DDB capture support: capture kernel debugger output into a fixed-size
   29  * buffer for later dumping to disk or extraction from user space.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD: releng/9.1/sys/ddb/db_capture.c 217326 2011-01-12 19:54:19Z mdf $");
   34 
   35 #include "opt_ddb.h"
   36 
   37 #include <sys/param.h>
   38 #include <sys/conf.h>
   39 #include <sys/kernel.h>
   40 #include <sys/kerneldump.h>
   41 #include <sys/malloc.h>
   42 #include <sys/msgbuf.h>
   43 #include <sys/priv.h>
   44 #include <sys/sx.h>
   45 #include <sys/sysctl.h>
   46 #include <sys/systm.h>
   47 
   48 #include <ddb/ddb.h>
   49 #include <ddb/db_lex.h>
   50 
   51 /*
   52  * While it would be desirable to use a small block-sized buffer and dump
   53  * incrementally to disk in fixed-size blocks, it's not possible to enter
   54  * kernel dumper routines without restarting the kernel, which is undesirable
   55  * in the midst of debugging.  Instead, we maintain a large static global
   56  * buffer that we fill from DDB's output routines.
   57  *
   58  * We enforce an invariant at runtime that buffer sizes are even multiples of
   59  * the textdump block size, which is a design choice that we might want to
   60  * reconsider.
   61  */
   62 static MALLOC_DEFINE(M_DDB_CAPTURE, "ddb_capture", "DDB capture buffer");
   63 
   64 #ifndef DDB_CAPTURE_DEFAULTBUFSIZE
   65 #define DDB_CAPTURE_DEFAULTBUFSIZE      48*1024
   66 #endif
   67 #ifndef DDB_CAPTURE_MAXBUFSIZE
   68 #define DDB_CAPTURE_MAXBUFSIZE  5*1024*1024
   69 #endif
   70 #define DDB_CAPTURE_FILENAME    "ddb.txt"       /* Captured DDB output. */
   71 
   72 static char *db_capture_buf;
   73 static u_int db_capture_bufsize = DDB_CAPTURE_DEFAULTBUFSIZE;
   74 static u_int db_capture_maxbufsize = DDB_CAPTURE_MAXBUFSIZE; /* Read-only. */
   75 static u_int db_capture_bufoff;         /* Next location to write in buffer. */
   76 static u_int db_capture_bufpadding;     /* Amount of zero padding. */
   77 static int db_capture_inpager;          /* Suspend capture in pager. */
   78 static int db_capture_inprogress;       /* DDB capture currently in progress. */
   79 
   80 struct sx db_capture_sx;                /* Lock against user thread races. */
   81 SX_SYSINIT(db_capture_sx, &db_capture_sx, "db_capture_sx");
   82 
   83 static SYSCTL_NODE(_debug_ddb, OID_AUTO, capture, CTLFLAG_RW, 0,
   84     "DDB capture options");
   85 
   86 SYSCTL_UINT(_debug_ddb_capture, OID_AUTO, bufoff, CTLFLAG_RD,
   87     &db_capture_bufoff, 0, "Bytes of data in DDB capture buffer");
   88 
   89 SYSCTL_UINT(_debug_ddb_capture, OID_AUTO, maxbufsize, CTLFLAG_RD,
   90     &db_capture_maxbufsize, 0,
   91     "Maximum value for debug.ddb.capture.bufsize");
   92 
   93 SYSCTL_INT(_debug_ddb_capture, OID_AUTO, inprogress, CTLFLAG_RD,
   94     &db_capture_inprogress, 0, "DDB output capture in progress");
   95 
   96 /*
   97  * Boot-time allocation of the DDB capture buffer, if any.  Force all buffer
   98  * sizes, including the maximum size, to be rounded to block sizes.
   99  */
  100 static void
  101 db_capture_sysinit(__unused void *dummy)
  102 {
  103 
  104         TUNABLE_INT_FETCH("debug.ddb.capture.bufsize", &db_capture_bufsize);
  105         db_capture_maxbufsize = roundup(db_capture_maxbufsize,
  106             TEXTDUMP_BLOCKSIZE);
  107         db_capture_bufsize = roundup(db_capture_bufsize, TEXTDUMP_BLOCKSIZE);
  108         if (db_capture_bufsize > db_capture_maxbufsize)
  109                 db_capture_bufsize = db_capture_maxbufsize;
  110         if (db_capture_bufsize != 0)
  111                 db_capture_buf = malloc(db_capture_bufsize, M_DDB_CAPTURE,
  112                     M_WAITOK);
  113 }
  114 SYSINIT(db_capture, SI_SUB_DDB_SERVICES, SI_ORDER_ANY, db_capture_sysinit,
  115     NULL);
  116 
  117 /*
  118  * Run-time adjustment of the capture buffer.
  119  */
  120 static int
  121 sysctl_debug_ddb_capture_bufsize(SYSCTL_HANDLER_ARGS)
  122 {
  123         u_int len, size;
  124         char *buf;
  125         int error;
  126 
  127         size = db_capture_bufsize;
  128         error = sysctl_handle_int(oidp, &size, 0, req);
  129         if (error || req->newptr == NULL)
  130                 return (error);
  131         size = roundup(size, TEXTDUMP_BLOCKSIZE);
  132         if (size > db_capture_maxbufsize)
  133                 return (EINVAL);
  134         sx_xlock(&db_capture_sx);
  135         if (size != 0) {
  136                 /*
  137                  * Potentially the buffer is quite large, so if we can't
  138                  * allocate it, fail rather than waiting.
  139                  */
  140                 buf = malloc(size, M_DDB_CAPTURE, M_NOWAIT);
  141                 if (buf == NULL) {
  142                         sx_xunlock(&db_capture_sx);
  143                         return (ENOMEM);
  144                 }
  145                 len = min(db_capture_bufoff, size);
  146         } else {
  147                 buf = NULL;
  148                 len = 0;
  149         }
  150         if (db_capture_buf != NULL && buf != NULL)
  151                 bcopy(db_capture_buf, buf, len);
  152         if (db_capture_buf != NULL)
  153                 free(db_capture_buf, M_DDB_CAPTURE);
  154         db_capture_bufoff = len;
  155         db_capture_buf = buf;
  156         db_capture_bufsize = size;
  157         sx_xunlock(&db_capture_sx);
  158 
  159         KASSERT(db_capture_bufoff <= db_capture_bufsize,
  160             ("sysctl_debug_ddb_capture_bufsize: bufoff > bufsize"));
  161         KASSERT(db_capture_bufsize <= db_capture_maxbufsize,
  162             ("sysctl_debug_ddb_capture_maxbufsize: bufsize > maxbufsize"));
  163 
  164         return (0);
  165 }
  166 SYSCTL_PROC(_debug_ddb_capture, OID_AUTO, bufsize, CTLTYPE_UINT|CTLFLAG_RW,
  167     0, 0, sysctl_debug_ddb_capture_bufsize, "IU",
  168     "Size of DDB capture buffer");
  169 
  170 /*
  171  * Sysctl to read out the capture buffer from userspace.  We require
  172  * privilege as sensitive process/memory information may be accessed.
  173  */
  174 static int
  175 sysctl_debug_ddb_capture_data(SYSCTL_HANDLER_ARGS)
  176 {
  177         int error;
  178         char ch;
  179 
  180         error = priv_check(req->td, PRIV_DDB_CAPTURE);
  181         if (error)
  182                 return (error);
  183 
  184         sx_slock(&db_capture_sx);
  185         error = SYSCTL_OUT(req, db_capture_buf, db_capture_bufoff);
  186         sx_sunlock(&db_capture_sx);
  187         if (error)
  188                 return (error);
  189         ch = '\0';
  190         return (SYSCTL_OUT(req, &ch, sizeof(ch)));
  191 }
  192 SYSCTL_PROC(_debug_ddb_capture, OID_AUTO, data, CTLTYPE_STRING | CTLFLAG_RD,
  193     NULL, 0, sysctl_debug_ddb_capture_data, "A", "DDB capture data");
  194 
  195 /*
  196  * Routines for capturing DDB output into a fixed-size buffer.  These are
  197  * invoked from DDB's input and output routines.  If we hit the limit on the
  198  * buffer, we simply drop further data.
  199  */
  200 void
  201 db_capture_write(char *buffer, u_int buflen)
  202 {
  203         u_int len;
  204 
  205         if (db_capture_inprogress == 0 || db_capture_inpager)
  206                 return;
  207         len = min(buflen, db_capture_bufsize - db_capture_bufoff);
  208         bcopy(buffer, db_capture_buf + db_capture_bufoff, len);
  209         db_capture_bufoff += len;
  210 
  211         KASSERT(db_capture_bufoff <= db_capture_bufsize,
  212             ("db_capture_write: bufoff > bufsize"));
  213 }
  214 
  215 void
  216 db_capture_writech(char ch)
  217 {
  218 
  219         return (db_capture_write(&ch, sizeof(ch)));
  220 }
  221 
  222 void
  223 db_capture_enterpager(void)
  224 {
  225 
  226         db_capture_inpager = 1;
  227 }
  228 
  229 void
  230 db_capture_exitpager(void)
  231 {
  232 
  233         db_capture_inpager = 0;
  234 }
  235 
  236 /*
  237  * Zero out any bytes left in the last block of the DDB capture buffer.  This
  238  * is run shortly before writing the blocks to disk, rather than when output
  239  * capture is stopped, in order to avoid injecting nul's into the middle of
  240  * output.
  241  */
  242 static void
  243 db_capture_zeropad(void)
  244 {
  245         u_int len;
  246 
  247         len = min(TEXTDUMP_BLOCKSIZE, (db_capture_bufsize -
  248             db_capture_bufoff) % TEXTDUMP_BLOCKSIZE);
  249         bzero(db_capture_buf + db_capture_bufoff, len);
  250         db_capture_bufpadding = len;
  251 }
  252 
  253 /*
  254  * Reset capture state, which flushes buffers.
  255  */
  256 static void
  257 db_capture_reset(void)
  258 {
  259 
  260         db_capture_inprogress = 0;
  261         db_capture_bufoff = 0;
  262         db_capture_bufpadding = 0;
  263 }
  264 
  265 /*
  266  * Start capture.  Only one session is allowed at any time, but we may
  267  * continue a previous session, so the buffer isn't reset.
  268  */
  269 static void
  270 db_capture_start(void)
  271 {
  272 
  273         if (db_capture_inprogress) {
  274                 db_printf("Capture already started\n");
  275                 return;
  276         }
  277         db_capture_inprogress = 1;
  278 }
  279 
  280 /*
  281  * Terminate DDB output capture--real work is deferred to db_capture_dump,
  282  * which executes outside of the DDB context.  We don't zero pad here because
  283  * capture may be started again before the dump takes place.
  284  */
  285 static void
  286 db_capture_stop(void)
  287 {
  288 
  289         if (db_capture_inprogress == 0) {
  290                 db_printf("Capture not started\n");
  291                 return;
  292         }
  293         db_capture_inprogress = 0;
  294 }
  295 
  296 /*
  297  * Dump DDB(4) captured output (and resets capture buffers).
  298  */
  299 void
  300 db_capture_dump(struct dumperinfo *di)
  301 {
  302         u_int offset;
  303 
  304         if (db_capture_bufoff == 0)
  305                 return;
  306 
  307         db_capture_zeropad();
  308         textdump_mkustar(textdump_block_buffer, DDB_CAPTURE_FILENAME,
  309             db_capture_bufoff);
  310         (void)textdump_writenextblock(di, textdump_block_buffer);
  311         for (offset = 0; offset < db_capture_bufoff + db_capture_bufpadding;
  312             offset += TEXTDUMP_BLOCKSIZE)
  313                 (void)textdump_writenextblock(di, db_capture_buf + offset);
  314         db_capture_bufoff = 0;
  315         db_capture_bufpadding = 0;
  316 }
  317 
  318 /*-
  319  * DDB(4) command to manage capture:
  320  *
  321  * capture on          - start DDB output capture
  322  * capture off         - stop DDB output capture
  323  * capture reset       - reset DDB capture buffer (also stops capture)
  324  * capture status      - print DDB output capture status
  325  */
  326 static void
  327 db_capture_usage(void)
  328 {
  329 
  330         db_error("capture [on|off|reset|status]\n");
  331 }
  332 
  333 void
  334 db_capture_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
  335     char *modif)
  336 {
  337         int t;
  338 
  339         t = db_read_token();
  340         if (t != tIDENT) {
  341                 db_capture_usage();
  342                 return;
  343         }
  344         if (db_read_token() != tEOL)
  345                 db_error("?\n");
  346         if (strcmp(db_tok_string, "on") == 0)
  347                 db_capture_start();
  348         else if (strcmp(db_tok_string, "off") == 0)
  349                 db_capture_stop();
  350         else if (strcmp(db_tok_string, "reset") == 0)
  351                 db_capture_reset();
  352         else if (strcmp(db_tok_string, "status") == 0) {
  353                 db_printf("%u/%u bytes used\n", db_capture_bufoff,
  354                     db_capture_bufsize);
  355                 if (db_capture_inprogress)
  356                         db_printf("capture is on\n");
  357                 else
  358                         db_printf("capture is off\n");
  359         } else
  360                 db_capture_usage();
  361 }

Cache object: d798248688bc622325064c26e3c6d7d8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.