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

Cache object: 74e737a6dadaa496896deb07c0402a6b


[ 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.