1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008 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#include <sys/types.h> 30#include <sys/sysctl.h> 31 32#include <err.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <kvm.h> 36#include <limits.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <sysexits.h> 41#include <unistd.h> 42 43#include "ddb.h" 44 45/* 46 * Interface with the ddb(4) capture buffer of a live kernel using sysctl, or 47 * for a crash dump using libkvm. 48 */ 49#define SYSCTL_DDB_CAPTURE_BUFOFF "debug.ddb.capture.bufoff" 50#define SYSCTL_DDB_CAPTURE_BUFSIZE "debug.ddb.capture.bufsize" 51#define SYSCTL_DDB_CAPTURE_MAXBUFSIZE "debug.ddb.capture.maxbufsize" 52#define SYSCTL_DDB_CAPTURE_DATA "debug.ddb.capture.data" 53#define SYSCTL_DDB_CAPTURE_INPROGRESS "debug.ddb.capture.inprogress" 54 55static struct nlist namelist[] = { 56#define X_DB_CAPTURE_BUF 0 57 { .n_name = "_db_capture_buf" }, 58#define X_DB_CAPTURE_BUFSIZE 1 59 { .n_name = "_db_capture_bufsize" }, 60#define X_DB_CAPTURE_MAXBUFSIZE 2 61 { .n_name = "_db_capture_maxbufsize" }, 62#define X_DB_CAPTURE_BUFOFF 3 63 { .n_name = "_db_capture_bufoff" }, 64#define X_DB_CAPTURE_INPROGRESS 4 65 { .n_name = "_db_capture_inprogress" }, 66 { .n_name = "" }, 67}; 68 69static int 70kread(kvm_t *kvm, void *kvm_pointer, void *address, size_t size, 71 size_t offset) 72{ 73 ssize_t ret; 74 75 ret = kvm_read(kvm, (unsigned long)kvm_pointer + offset, address, 76 size); 77 if (ret < 0 || (size_t)ret != size) 78 return (-1); 79 return (0); 80} 81 82static int 83kread_symbol(kvm_t *kvm, int read_index, void *address, size_t size, 84 size_t offset) 85{ 86 ssize_t ret; 87 88 ret = kvm_read(kvm, namelist[read_index].n_value + offset, address, size); 89 if (ret < 0 || (size_t)ret != size) 90 return (-1); 91 return (0); 92} 93 94static void 95ddb_capture_print_kvm(kvm_t *kvm) 96{ 97 u_int db_capture_bufoff; 98 char *buffer, *db_capture_buf; 99 100 if (kread_symbol(kvm, X_DB_CAPTURE_BUF, &db_capture_buf, 101 sizeof(db_capture_buf), 0) < 0) 102 errx(-1, "kvm: unable to read db_capture_buf"); 103 104 if (kread_symbol(kvm, X_DB_CAPTURE_BUFOFF, &db_capture_bufoff, 105 sizeof(db_capture_bufoff), 0) < 0) 106 errx(-1, "kvm: unable to read db_capture_bufoff"); 107 108 buffer = malloc(db_capture_bufoff + 1); 109 if (buffer == NULL) 110 err(-1, "malloc: db_capture_bufoff (%u)", 111 db_capture_bufoff); 112 bzero(buffer, db_capture_bufoff + 1); 113 114 if (kread(kvm, db_capture_buf, buffer, db_capture_bufoff, 0) < 0) 115 errx(-1, "kvm: unable to read buffer"); 116 117 printf("%s\n", buffer); 118 free(buffer); 119} 120 121static void 122ddb_capture_print_sysctl(void) 123{ 124 size_t buflen, len; 125 char *buffer; 126 int ret; 127 128repeat: 129 if (sysctlbyname(SYSCTL_DDB_CAPTURE_DATA, NULL, &buflen, NULL, 0) < 0) 130 err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_DATA); 131 if (buflen == 0) 132 return; 133 buffer = malloc(buflen); 134 if (buffer == NULL) 135 err(EX_OSERR, "malloc"); 136 bzero(buffer, buflen); 137 len = buflen; 138 ret = sysctlbyname(SYSCTL_DDB_CAPTURE_DATA, buffer, &len, NULL, 0); 139 if (ret < 0 && errno != ENOMEM) 140 err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_DATA); 141 if (ret < 0) { 142 free(buffer); 143 goto repeat; 144 } 145 146 printf("%s\n", buffer); 147 free(buffer); 148} 149 150static void 151ddb_capture_status_kvm(kvm_t *kvm) 152{ 153 u_int db_capture_bufoff, db_capture_bufsize, db_capture_inprogress; 154 155 if (kread_symbol(kvm, X_DB_CAPTURE_BUFOFF, &db_capture_bufoff, 156 sizeof(db_capture_bufoff), 0) < 0) 157 errx(-1, "kvm: unable to read db_capture_bufoff"); 158 if (kread_symbol(kvm, X_DB_CAPTURE_BUFSIZE, &db_capture_bufsize, 159 sizeof(db_capture_bufsize), 0) < 0) 160 errx(-1, "kvm: unable to read db_capture_bufsize"); 161 if (kread_symbol(kvm, X_DB_CAPTURE_INPROGRESS, 162 &db_capture_inprogress, sizeof(db_capture_inprogress), 0) < 0) 163 err(-1, "kvm: unable to read db_capture_inprogress"); 164 printf("%u/%u bytes used\n", db_capture_bufoff, db_capture_bufsize); 165 if (db_capture_inprogress) 166 printf("capture is on\n"); 167 else 168 printf("capture is off\n"); 169 170} 171 172static void 173ddb_capture_status_sysctl(void) 174{ 175 u_int db_capture_bufoff, db_capture_bufsize, db_capture_inprogress; 176 size_t len; 177 178 len = sizeof(db_capture_bufoff); 179 if (sysctlbyname(SYSCTL_DDB_CAPTURE_BUFOFF, &db_capture_bufoff, &len, 180 NULL, 0) < 0) 181 err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_BUFOFF); 182 len = sizeof(db_capture_bufoff); 183 if (sysctlbyname(SYSCTL_DDB_CAPTURE_BUFSIZE, &db_capture_bufsize, 184 &len, NULL, 0) < 0) 185 err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_BUFSIZE); 186 len = sizeof(db_capture_inprogress); 187 if (sysctlbyname(SYSCTL_DDB_CAPTURE_INPROGRESS, 188 &db_capture_inprogress, &len, NULL, 0) < 0) 189 err(EX_OSERR, "sysctl: %s", SYSCTL_DDB_CAPTURE_INPROGRESS); 190 printf("%u/%u bytes used\n", db_capture_bufoff, db_capture_bufsize); 191 if (db_capture_inprogress) 192 printf("capture is on\n"); 193 else 194 printf("capture is off\n"); 195} 196 197void 198ddb_capture(int argc, char *argv[]) 199{ 200 char *mflag, *nflag, errbuf[_POSIX2_LINE_MAX]; 201 kvm_t *kvm; 202 int ch; 203 204 mflag = NULL; 205 nflag = NULL; 206 kvm = NULL; 207 while ((ch = getopt(argc, argv, "M:N:")) != -1) { 208 switch (ch) { 209 case 'M': 210 mflag = optarg; 211 break; 212 213 case 'N': 214 nflag = optarg; 215 break; 216 217 default: 218 usage(); 219 } 220 } 221 argc -= optind; 222 argv += optind; 223 224 if (argc != 1) 225 usage(); 226 227 if (mflag != NULL) { 228 kvm = kvm_openfiles(nflag, mflag, NULL, O_RDONLY, errbuf); 229 if (kvm == NULL) 230 errx(-1, "ddb_capture: kvm_openfiles: %s", errbuf); 231 if (kvm_nlist(kvm, namelist) != 0) 232 errx(-1, "ddb_capture: kvm_nlist"); 233 } else if (nflag != NULL) 234 usage(); 235 if (strcmp(argv[0], "print") == 0) { 236 if (kvm != NULL) 237 ddb_capture_print_kvm(kvm); 238 else 239 ddb_capture_print_sysctl(); 240 } else if (strcmp(argv[0], "status") == 0) { 241 if (kvm != NULL) 242 ddb_capture_status_kvm(kvm); 243 else 244 ddb_capture_status_sysctl(); 245 } else 246 usage(); 247} 248