1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1994, by Sun Microsytems, Inc. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Interfaces to control kernel tracing and kernel probes 30 */ 31 32 #ifndef DEBUG 33 #define NDEBUG 1 34 #endif 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <string.h> /* for strerror() */ 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <sys/tnf.h> 43 #include <fcntl.h> 44 #include <errno.h> 45 #include <signal.h> 46 #include <assert.h> 47 48 #include "tnfctl_int.h" 49 #include "kernel_int.h" 50 51 /* The TNF pseudo-device */ 52 #define TNFDRIVER "/dev/tnfctl" 53 54 /* Dummy "test" function -- just used to flag enabled probes */ 55 #define PRBK_DUMMY_TEST ((tnf_probe_test_func_t) 4) 56 57 /* Dummy "commit" function -- just used to flag trace enabled */ 58 #define PRBK_DUMMY_COMMIT ((tnf_probe_func_t) 8) 59 60 /* Dummy "rollback" function -- just used to flag trace disabled */ 61 #define PRBK_DUMMY_ROLLBACK ((tnf_probe_func_t) 12) 62 63 /* Dummy "end" function */ 64 #define PRBK_DUMMY_END ((uintptr_t) 16) 65 66 /* Dummy "alloc" function */ 67 #define PRBK_DUMMY_ALLOC ((uintptr_t) 20) 68 69 /* Minimum and maximum allowed buffer sizes. */ 70 /* XXX -- maximum should be some function of physmem. */ 71 #define KERNEL_MINBUF_SIZE (128 * 1024) 72 #define KERNEL_MAXBUF_SIZE (128 * 1024 * 1024) 73 74 static tnfctl_errcode_t prbk_get_buf_attrs(tnfctl_handle_t *hdl); 75 static tnfctl_errcode_t alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe); 76 77 /* 78 * Initialize the kernel interface: Open the TNF control device, 79 * and determine the current kernel probes state, including the 80 * current pidfilter list. 81 */ 82 tnfctl_errcode_t 83 _tnfctl_prbk_init(tnfctl_handle_t *hdl) 84 { 85 tnfctl_errcode_t prexstat; 86 tifiocstate_t kstate; 87 int kfd; 88 89 kfd = open(TNFDRIVER, O_RDWR); 90 if (kfd < 0) { 91 return (tnfctl_status_map(errno)); 92 } 93 if (ioctl(kfd, TIFIOCGSTATE, &kstate) < 0) 94 return (tnfctl_status_map(errno)); 95 96 hdl->kfd = kfd; 97 hdl->kpidfilter_state = kstate.pidfilter_mode; 98 hdl->trace_state = !kstate.trace_stopped; 99 hdl->trace_min_size = KERNEL_MINBUF_SIZE; 100 prexstat = prbk_get_buf_attrs(hdl); 101 if (prexstat) 102 return (prexstat); 103 104 return (TNFCTL_ERR_NONE); 105 } 106 107 /* 108 * Close the TNF control device. 109 */ 110 tnfctl_errcode_t 111 _tnfctl_prbk_close(tnfctl_handle_t *hdl) 112 { 113 if (hdl == NULL) 114 return (TNFCTL_ERR_NONE); 115 116 if (close(hdl->kfd) == -1) { 117 return (tnfctl_status_map(errno)); 118 } 119 return (TNFCTL_ERR_NONE); 120 } 121 122 /* 123 * Returns function addresses that can be plugged into function pointers 124 * in kernel probes. These are actually dummy values that get 125 * interpreted by a routine in this file when a probe is flushed. 126 */ 127 void 128 _tnfctl_prbk_get_other_funcs(uintptr_t *allocp, uintptr_t *commitp, 129 uintptr_t *rollbackp, uintptr_t *endp) 130 { 131 *allocp = PRBK_DUMMY_ALLOC; 132 *commitp = (uintptr_t) PRBK_DUMMY_COMMIT; 133 *rollbackp = (uintptr_t) PRBK_DUMMY_ROLLBACK; 134 *endp = PRBK_DUMMY_END; 135 } 136 137 138 /* 139 * Returns test function address 140 */ 141 void 142 _tnfctl_prbk_test_func(uintptr_t *outp) 143 { 144 *outp = (uintptr_t) PRBK_DUMMY_TEST; 145 } 146 147 /* 148 * Allocate a trace buffer. Check for reasonable size; reject if there's 149 * already a buffer. 150 */ 151 tnfctl_errcode_t 152 _tnfctl_prbk_buffer_alloc(tnfctl_handle_t *hdl, int size) 153 { 154 tifiocstate_t bufstat; 155 tnfctl_errcode_t prexstat; 156 int saved_val; 157 158 if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) { 159 return (tnfctl_status_map(errno)); 160 } 161 if (bufstat.buffer_state != TIFIOCBUF_NONE) { 162 return (TNFCTL_ERR_BUFEXISTS); 163 } 164 if (size < KERNEL_MINBUF_SIZE) { 165 return (TNFCTL_ERR_SIZETOOSMALL); 166 } else if (size > KERNEL_MAXBUF_SIZE) { 167 /* REMIND: make this an error ? */ 168 size = KERNEL_MAXBUF_SIZE; 169 } 170 if (ioctl(hdl->kfd, TIFIOCALLOCBUF, size) < 0) { 171 saved_val = errno; 172 (void) prbk_get_buf_attrs(hdl); 173 return (tnfctl_status_map(saved_val)); 174 } 175 176 prexstat = prbk_get_buf_attrs(hdl); 177 if (prexstat) 178 return (prexstat); 179 180 return (TNFCTL_ERR_NONE); 181 } 182 183 /* 184 * Deallocate the kernel's trace buffer. 185 */ 186 tnfctl_errcode_t 187 _tnfctl_prbk_buffer_dealloc(tnfctl_handle_t *hdl) 188 { 189 tifiocstate_t bufstat; 190 tnfctl_errcode_t prexstat; 191 int saved_val; 192 193 if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) { 194 return (tnfctl_status_map(errno)); 195 } 196 if (bufstat.buffer_state == TIFIOCBUF_NONE) { 197 return (TNFCTL_ERR_NOBUF); 198 } 199 200 if (bufstat.buffer_state == TIFIOCBUF_OK && !bufstat.trace_stopped) { 201 return (TNFCTL_ERR_BADDEALLOC); 202 } 203 if (ioctl(hdl->kfd, TIFIOCDEALLOCBUF) < 0) { 204 saved_val = errno; 205 (void) prbk_get_buf_attrs(hdl); 206 return (tnfctl_status_map(saved_val)); 207 } 208 209 prexstat = prbk_get_buf_attrs(hdl); 210 if (prexstat) 211 return (prexstat); 212 213 return (TNFCTL_ERR_NONE); 214 } 215 216 /* 217 * Turns kernel global tracing on or off. 218 */ 219 tnfctl_errcode_t 220 _tnfctl_prbk_set_tracing(tnfctl_handle_t *hdl, boolean_t onoff) 221 { 222 if (hdl->trace_state != onoff && 223 ioctl(hdl->kfd, TIFIOCSTRACING, onoff) < 0) { 224 if (errno == ENOMEM && onoff) 225 return (TNFCTL_ERR_NOBUF); 226 else 227 return (tnfctl_status_map(errno)); 228 } 229 hdl->trace_state = onoff; 230 return (TNFCTL_ERR_NONE); 231 } 232 233 /* 234 * Turn process filter mode on or off. The process filter is maintained 235 * even when process filtering is off, but has no effect: all processes 236 * are traced. 237 */ 238 tnfctl_errcode_t 239 _tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t *hdl, boolean_t onoff) 240 { 241 if (hdl->kpidfilter_state != onoff && 242 ioctl(hdl->kfd, TIFIOCSPIDFILTER, onoff) < 0) { 243 return (tnfctl_status_map(errno)); 244 } 245 hdl->kpidfilter_state = onoff; 246 return (TNFCTL_ERR_NONE); 247 } 248 249 /* 250 * Return the process filter list. 251 */ 252 tnfctl_errcode_t 253 _tnfctl_prbk_get_pfilter_list(tnfctl_handle_t *hdl, pid_t **ret_list_p, 254 int *ret_count) 255 { 256 tifiocstate_t kstate; 257 int *filterset; 258 int i; 259 pid_t *ret_list; 260 261 if (ioctl(hdl->kfd, TIFIOCGSTATE, &kstate) < 0) 262 return (tnfctl_status_map(errno)); 263 264 if (kstate.pidfilter_size == 0) { 265 *ret_count = 0; 266 *ret_list_p = NULL; 267 return (TNFCTL_ERR_NONE); 268 } 269 270 filterset = (int *) malloc((kstate.pidfilter_size + 1) * 271 sizeof (pid_t)); 272 if (filterset == NULL) 273 return (TNFCTL_ERR_ALLOCFAIL); 274 if (ioctl(hdl->kfd, TIFIOCPIDFILTERGET, filterset) < 0) 275 return (tnfctl_status_map(errno)); 276 277 /* filterset[0] contains size of array */ 278 ret_list = malloc(filterset[0] * sizeof (pid_t)); 279 if (ret_list == NULL) 280 return (TNFCTL_ERR_ALLOCFAIL); 281 282 for (i = 1; i <= filterset[0]; ++i) 283 ret_list[i - 1] = filterset[i]; 284 285 *ret_count = filterset[0]; 286 (void) free(filterset); 287 *ret_list_p = ret_list; 288 return (TNFCTL_ERR_NONE); 289 } 290 291 /* 292 * Add the pid to the process filter list. 293 * check whether it's already in the filter list, 294 * and whether the process exists. 295 */ 296 tnfctl_errcode_t 297 _tnfctl_prbk_pfilter_add(tnfctl_handle_t *hdl, pid_t pid_to_add) 298 { 299 if (ioctl(hdl->kfd, TIFIOCSPIDON, pid_to_add) < 0) { 300 return (tnfctl_status_map(errno)); 301 } 302 return (TNFCTL_ERR_NONE); 303 } 304 305 /* 306 * Drop the pid from the process filter list. 307 */ 308 tnfctl_errcode_t 309 _tnfctl_prbk_pfilter_delete(tnfctl_handle_t *hdl, pid_t pid_to_del) 310 { 311 if (ioctl(hdl->kfd, TIFIOCSPIDOFF, pid_to_del) < 0) { 312 if (errno == ESRCH) { 313 return (TNFCTL_ERR_NOPROCESS); 314 } else { 315 return (tnfctl_status_map(errno)); 316 } 317 } 318 return (TNFCTL_ERR_NONE); 319 } 320 321 /* 322 * get the buffer attributes - side effect tnfctl handle 323 */ 324 static tnfctl_errcode_t 325 prbk_get_buf_attrs(tnfctl_handle_t *hdl) 326 { 327 tifiocstate_t bufstat; 328 329 if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) { 330 return (tnfctl_status_map(errno)); 331 } 332 333 hdl->trace_file_name = NULL; 334 hdl->trace_buf_size = bufstat.buffer_size; 335 if (bufstat.buffer_state == TIFIOCBUF_NONE) 336 hdl->trace_buf_state = TNFCTL_BUF_NONE; 337 else if (bufstat.buffer_state == TIFIOCBUF_BROKEN) 338 hdl->trace_buf_state = TNFCTL_BUF_BROKEN; 339 else 340 hdl->trace_buf_state = TNFCTL_BUF_OK; 341 return (TNFCTL_ERR_NONE); 342 } 343 344 /* 345 * "Flush" a probe: i.e., sync up the kernel state with the 346 * (desired) state stored in our data structure. 347 */ 348 tnfctl_errcode_t 349 _tnfctl_prbk_flush(tnfctl_handle_t *hndl, prbctlref_t *p) 350 { 351 tnf_probevals_t probebuf; 352 353 probebuf.probenum = p->probe_id; 354 probebuf.enabled = (p->wrkprbctl.test_func != NULL); 355 probebuf.traced = (p->wrkprbctl.commit_func == PRBK_DUMMY_COMMIT); 356 if (ioctl(hndl->kfd, TIFIOCSPROBEVALS, &probebuf) < 0) 357 return (tnfctl_status_map(errno)); 358 return (TNFCTL_ERR_NONE); 359 } 360 361 /* 362 * Refresh our understanding of the existing probes in the kernel. 363 */ 364 tnfctl_errcode_t 365 _tnfctl_refresh_kernel(tnfctl_handle_t *hndl) 366 { 367 int maxprobe, i; 368 int pos; 369 tnfctl_errcode_t prexstat; 370 tnf_probevals_t probebuf; 371 objlist_t *obj_p; 372 prbctlref_t *p = NULL; 373 374 prexstat = prbk_get_buf_attrs(hndl); 375 if (prexstat) 376 return (prexstat); 377 /* 378 * Here is where you'd set obj_p->new to B_FALSE and obj_p->old to 379 * B_TRUE for all existing objects. We currently don't need 380 * it until we get modload/unload working correctly with probes 381 */ 382 if (ioctl(hndl->kfd, TIFIOCGMAXPROBE, &maxprobe) < 0) 383 return (tnfctl_status_map(errno)); 384 if (maxprobe == hndl->num_probes) { 385 /* XXX Inadequate in the presence of module unloading */ 386 return (TNFCTL_ERR_NONE); 387 } 388 389 prexstat = alloc_probe_space(hndl, maxprobe); 390 if (prexstat) 391 return (prexstat); 392 393 NOTE(NO_COMPETING_THREADS_NOW) 394 obj_p = hndl->objlist; 395 NOTE(COMPETING_THREADS_NOW) 396 assert((obj_p != NULL) && (obj_p->probes != NULL)); 397 398 for (i = 1; i <= maxprobe; ++i) { 399 400 if (i >= (obj_p->min_probe_num + obj_p->probecnt)) { 401 obj_p = obj_p->next; 402 } 403 404 /* make sure we are in the correct object */ 405 assert(obj_p != NULL); 406 assert((i >= obj_p->min_probe_num) && 407 (i < (obj_p->min_probe_num + obj_p->probecnt))); 408 409 /* get a pointer to correct probe */ 410 pos = i - obj_p->min_probe_num; 411 p = &(obj_p->probes[pos]); 412 assert((p != NULL) && (p->probe_id == i) && (p->probe_handle)); 413 414 probebuf.probenum = i; 415 if (ioctl(hndl->kfd, TIFIOCGPROBEVALS, &probebuf) < 0) { 416 if (errno == ENOENT) { 417 /* 418 * This probe has vanished due to a module 419 * unload. 420 */ 421 p->probe_handle->valid = B_FALSE; 422 } else { 423 return (tnfctl_status_map(errno)); 424 } 425 } else { 426 if (p->probe_handle->valid == B_FALSE) { 427 /* 428 * seeing this probe for the first time 429 * (alloc_probe_space() initialized this 430 * "valid" field to B_FALSE) 431 */ 432 /* Update our info about this probe */ 433 p->wrkprbctl.test_func = (probebuf.enabled) ? 434 PRBK_DUMMY_TEST : NULL; 435 p->wrkprbctl.commit_func = (probebuf.traced) ? 436 PRBK_DUMMY_COMMIT : PRBK_DUMMY_ROLLBACK; 437 p->probe_handle->valid = B_TRUE; 438 if (probebuf.attrsize < sizeof (probebuf)) 439 probebuf.attrsize = sizeof (probebuf); 440 p->attr_string = malloc(probebuf.attrsize); 441 if (p->attr_string == NULL) 442 return (TNFCTL_ERR_ALLOCFAIL); 443 /* 444 * NOTE: the next statement is a structure 445 * copy and *not* a pointer assignment 446 */ 447 /* LINTED pointer cast may result in improper alignment */ 448 *(tnf_probevals_t *) p->attr_string = probebuf; 449 if (ioctl(hndl->kfd, TIFIOCGPROBESTRING, 450 p->attr_string) < 0) 451 return (tnfctl_status_map(errno)); 452 if (hndl->create_func) { 453 p->probe_handle->client_registered_data = 454 hndl->create_func(hndl, 455 p->probe_handle); 456 } 457 } 458 } 459 } 460 hndl->num_probes = maxprobe; 461 return (TNFCTL_ERR_NONE); 462 } 463 464 /* 465 * check if there are any new probes in the kernel that we aren't aware of. 466 * If so, allocate space for those probes in our data structure. 467 */ 468 static tnfctl_errcode_t 469 alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe) 470 { 471 objlist_t **o_pp; 472 objlist_t *obj_p, *nobj_p; 473 int min_probe_num, i; 474 prbctlref_t *probe_p; 475 476 /* we know that: hndl->maxprobe != maxprobe */ 477 NOTE(NO_COMPETING_THREADS_NOW) 478 obj_p = hndl->objlist; 479 NOTE(COMPETING_THREADS_NOW) 480 if (obj_p == NULL) { 481 /* no objects allocated */ 482 o_pp = &(hndl->objlist); 483 min_probe_num = 1; 484 } else { 485 /* find last object */ 486 while (obj_p->next != NULL) { 487 /* reset new_probe field on modload/unload */ 488 obj_p->new_probe = B_FALSE; 489 obj_p = obj_p->next; 490 } 491 o_pp = &(obj_p->next); 492 min_probe_num = obj_p->min_probe_num + obj_p->probecnt; 493 } 494 495 nobj_p = calloc(1, sizeof (objlist_t)); 496 if (nobj_p == NULL) 497 return (TNFCTL_ERR_ALLOCFAIL); 498 /* add to the linked list */ 499 *o_pp = nobj_p; 500 /* NULL, B_FALSE, or 0's not explicitly initialized */ 501 nobj_p->new_probe = B_TRUE; 502 nobj_p->new = B_TRUE; 503 nobj_p->objfd = -1; 504 nobj_p->min_probe_num = min_probe_num; 505 nobj_p->probecnt = maxprobe - min_probe_num + 1; 506 nobj_p->probes = calloc(nobj_p->probecnt, sizeof (prbctlref_t)); 507 if (nobj_p->probes == NULL) { 508 free(nobj_p); 509 return (TNFCTL_ERR_ALLOCFAIL); 510 } 511 512 probe_p = &(nobj_p->probes[0]); 513 for (i = min_probe_num; i <= maxprobe; i++) { 514 NOTE(NO_COMPETING_THREADS_NOW) 515 probe_p->obj = nobj_p; 516 NOTE(COMPETING_THREADS_NOW) 517 probe_p->probe_id = i; 518 probe_p->probe_handle = calloc(1, sizeof (tnfctl_probe_t)); 519 if (probe_p->probe_handle == NULL) { 520 if (nobj_p->probes) 521 free(nobj_p->probes); 522 free(nobj_p); 523 return (TNFCTL_ERR_ALLOCFAIL); 524 } 525 probe_p->probe_handle->valid = B_FALSE; 526 probe_p->probe_handle->probe_p = probe_p; 527 /* link in probe handle into chain off tnfctl_handle_t */ 528 probe_p->probe_handle->next = hndl->probe_handle_list_head; 529 hndl->probe_handle_list_head = probe_p->probe_handle; 530 531 probe_p++; 532 } 533 534 hndl->num_probes = maxprobe; 535 return (TNFCTL_ERR_NONE); 536 } 537