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