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 55 static 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 69 static int 70 kread(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 82 static int 83 kread_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 94 static void 95 ddb_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 121 static void 122 ddb_capture_print_sysctl(void) 123 { 124 size_t buflen, len; 125 char *buffer; 126 int ret; 127 128 repeat: 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 150 static void 151 ddb_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 172 static void 173 ddb_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 197 void 198 ddb_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