diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libtnfctl/kernel_int.c | |
| download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libtnfctl/kernel_int.c')
| -rw-r--r-- | usr/src/lib/libtnfctl/kernel_int.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/usr/src/lib/libtnfctl/kernel_int.c b/usr/src/lib/libtnfctl/kernel_int.c new file mode 100644 index 0000000000..b9260f3212 --- /dev/null +++ b/usr/src/lib/libtnfctl/kernel_int.c @@ -0,0 +1,536 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Interfaces to control kernel tracing and kernel probes + */ + +#ifndef DEBUG +#define NDEBUG 1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> /* for strerror() */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/tnf.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <assert.h> + +#include "tnfctl_int.h" +#include "kernel_int.h" + +/* The TNF pseudo-device */ +#define TNFDRIVER "/dev/tnfctl" + +/* Dummy "test" function -- just used to flag enabled probes */ +#define PRBK_DUMMY_TEST ((tnf_probe_test_func_t) 4) + +/* Dummy "commit" function -- just used to flag trace enabled */ +#define PRBK_DUMMY_COMMIT ((tnf_probe_func_t) 8) + +/* Dummy "rollback" function -- just used to flag trace disabled */ +#define PRBK_DUMMY_ROLLBACK ((tnf_probe_func_t) 12) + +/* Dummy "end" function */ +#define PRBK_DUMMY_END ((uintptr_t) 16) + +/* Dummy "alloc" function */ +#define PRBK_DUMMY_ALLOC ((uintptr_t) 20) + +/* Minimum and maximum allowed buffer sizes. */ +/* XXX -- maximum should be some function of physmem. */ +#define KERNEL_MINBUF_SIZE (128 * 1024) +#define KERNEL_MAXBUF_SIZE (128 * 1024 * 1024) + +static tnfctl_errcode_t prbk_get_buf_attrs(tnfctl_handle_t *hdl); +static tnfctl_errcode_t alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe); + +/* + * Initialize the kernel interface: Open the TNF control device, + * and determine the current kernel probes state, including the + * current pidfilter list. + */ +tnfctl_errcode_t +_tnfctl_prbk_init(tnfctl_handle_t *hdl) +{ + tnfctl_errcode_t prexstat; + tifiocstate_t kstate; + int kfd; + + kfd = open(TNFDRIVER, O_RDWR); + if (kfd < 0) { + return (tnfctl_status_map(errno)); + } + if (ioctl(kfd, TIFIOCGSTATE, &kstate) < 0) + return (tnfctl_status_map(errno)); + + hdl->kfd = kfd; + hdl->kpidfilter_state = kstate.pidfilter_mode; + hdl->trace_state = !kstate.trace_stopped; + hdl->trace_min_size = KERNEL_MINBUF_SIZE; + prexstat = prbk_get_buf_attrs(hdl); + if (prexstat) + return (prexstat); + + return (TNFCTL_ERR_NONE); +} + +/* + * Close the TNF control device. + */ +tnfctl_errcode_t +_tnfctl_prbk_close(tnfctl_handle_t *hdl) +{ + if (hdl == NULL) + return (TNFCTL_ERR_NONE); + + if (close(hdl->kfd) == -1) { + return (tnfctl_status_map(errno)); + } + return (TNFCTL_ERR_NONE); +} + +/* + * Returns function addresses that can be plugged into function pointers + * in kernel probes. These are actually dummy values that get + * interpreted by a routine in this file when a probe is flushed. + */ +void +_tnfctl_prbk_get_other_funcs(uintptr_t *allocp, uintptr_t *commitp, + uintptr_t *rollbackp, uintptr_t *endp) +{ + *allocp = PRBK_DUMMY_ALLOC; + *commitp = (uintptr_t) PRBK_DUMMY_COMMIT; + *rollbackp = (uintptr_t) PRBK_DUMMY_ROLLBACK; + *endp = PRBK_DUMMY_END; +} + + +/* + * Returns test function address + */ +void +_tnfctl_prbk_test_func(uintptr_t *outp) +{ + *outp = (uintptr_t) PRBK_DUMMY_TEST; +} + +/* + * Allocate a trace buffer. Check for reasonable size; reject if there's + * already a buffer. + */ +tnfctl_errcode_t +_tnfctl_prbk_buffer_alloc(tnfctl_handle_t *hdl, int size) +{ + tifiocstate_t bufstat; + tnfctl_errcode_t prexstat; + int saved_val; + + if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) { + return (tnfctl_status_map(errno)); + } + if (bufstat.buffer_state != TIFIOCBUF_NONE) { + return (TNFCTL_ERR_BUFEXISTS); + } + if (size < KERNEL_MINBUF_SIZE) { + return (TNFCTL_ERR_SIZETOOSMALL); + } else if (size > KERNEL_MAXBUF_SIZE) { + /* REMIND: make this an error ? */ + size = KERNEL_MAXBUF_SIZE; + } + if (ioctl(hdl->kfd, TIFIOCALLOCBUF, size) < 0) { + saved_val = errno; + (void) prbk_get_buf_attrs(hdl); + return (tnfctl_status_map(saved_val)); + } + + prexstat = prbk_get_buf_attrs(hdl); + if (prexstat) + return (prexstat); + + return (TNFCTL_ERR_NONE); +} + +/* + * Deallocate the kernel's trace buffer. + */ +tnfctl_errcode_t +_tnfctl_prbk_buffer_dealloc(tnfctl_handle_t *hdl) +{ + tifiocstate_t bufstat; + tnfctl_errcode_t prexstat; + int saved_val; + + if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) { + return (tnfctl_status_map(errno)); + } + if (bufstat.buffer_state == TIFIOCBUF_NONE) { + return (TNFCTL_ERR_NOBUF); + } + + if (bufstat.buffer_state == TIFIOCBUF_OK && !bufstat.trace_stopped) { + return (TNFCTL_ERR_BADDEALLOC); + } + if (ioctl(hdl->kfd, TIFIOCDEALLOCBUF) < 0) { + saved_val = errno; + (void) prbk_get_buf_attrs(hdl); + return (tnfctl_status_map(saved_val)); + } + + prexstat = prbk_get_buf_attrs(hdl); + if (prexstat) + return (prexstat); + + return (TNFCTL_ERR_NONE); +} + +/* + * Turns kernel global tracing on or off. + */ +tnfctl_errcode_t +_tnfctl_prbk_set_tracing(tnfctl_handle_t *hdl, boolean_t onoff) +{ + if (hdl->trace_state != onoff && + ioctl(hdl->kfd, TIFIOCSTRACING, onoff) < 0) { + if (errno == ENOMEM && onoff) + return (TNFCTL_ERR_NOBUF); + else + return (tnfctl_status_map(errno)); + } + hdl->trace_state = onoff; + return (TNFCTL_ERR_NONE); +} + +/* + * Turn process filter mode on or off. The process filter is maintained + * even when process filtering is off, but has no effect: all processes + * are traced. + */ +tnfctl_errcode_t +_tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t *hdl, boolean_t onoff) +{ + if (hdl->kpidfilter_state != onoff && + ioctl(hdl->kfd, TIFIOCSPIDFILTER, onoff) < 0) { + return (tnfctl_status_map(errno)); + } + hdl->kpidfilter_state = onoff; + return (TNFCTL_ERR_NONE); +} + +/* + * Return the process filter list. + */ +tnfctl_errcode_t +_tnfctl_prbk_get_pfilter_list(tnfctl_handle_t *hdl, pid_t **ret_list_p, + int *ret_count) +{ + tifiocstate_t kstate; + int *filterset; + int i; + pid_t *ret_list; + + if (ioctl(hdl->kfd, TIFIOCGSTATE, &kstate) < 0) + return (tnfctl_status_map(errno)); + + if (kstate.pidfilter_size == 0) { + *ret_count = 0; + *ret_list_p = NULL; + return (TNFCTL_ERR_NONE); + } + + filterset = (int *) malloc((kstate.pidfilter_size + 1) * + sizeof (pid_t)); + if (filterset == NULL) + return (TNFCTL_ERR_ALLOCFAIL); + if (ioctl(hdl->kfd, TIFIOCPIDFILTERGET, filterset) < 0) + return (tnfctl_status_map(errno)); + + /* filterset[0] contains size of array */ + ret_list = malloc(filterset[0] * sizeof (pid_t)); + if (ret_list == NULL) + return (TNFCTL_ERR_ALLOCFAIL); + + for (i = 1; i <= filterset[0]; ++i) + ret_list[i - 1] = filterset[i]; + + *ret_count = filterset[0]; + (void) free(filterset); + *ret_list_p = ret_list; + return (TNFCTL_ERR_NONE); +} + +/* + * Add the pid to the process filter list. + * check whether it's already in the filter list, + * and whether the process exists. + */ +tnfctl_errcode_t +_tnfctl_prbk_pfilter_add(tnfctl_handle_t *hdl, pid_t pid_to_add) +{ + if (ioctl(hdl->kfd, TIFIOCSPIDON, pid_to_add) < 0) { + return (tnfctl_status_map(errno)); + } + return (TNFCTL_ERR_NONE); +} + +/* + * Drop the pid from the process filter list. + */ +tnfctl_errcode_t +_tnfctl_prbk_pfilter_delete(tnfctl_handle_t *hdl, pid_t pid_to_del) +{ + if (ioctl(hdl->kfd, TIFIOCSPIDOFF, pid_to_del) < 0) { + if (errno == ESRCH) { + return (TNFCTL_ERR_NOPROCESS); + } else { + return (tnfctl_status_map(errno)); + } + } + return (TNFCTL_ERR_NONE); +} + +/* + * get the buffer attributes - side effect tnfctl handle + */ +static tnfctl_errcode_t +prbk_get_buf_attrs(tnfctl_handle_t *hdl) +{ + tifiocstate_t bufstat; + + if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) { + return (tnfctl_status_map(errno)); + } + + hdl->trace_file_name = NULL; + hdl->trace_buf_size = bufstat.buffer_size; + if (bufstat.buffer_state == TIFIOCBUF_NONE) + hdl->trace_buf_state = TNFCTL_BUF_NONE; + else if (bufstat.buffer_state == TIFIOCBUF_BROKEN) + hdl->trace_buf_state = TNFCTL_BUF_BROKEN; + else + hdl->trace_buf_state = TNFCTL_BUF_OK; + return (TNFCTL_ERR_NONE); +} + +/* + * "Flush" a probe: i.e., sync up the kernel state with the + * (desired) state stored in our data structure. + */ +tnfctl_errcode_t +_tnfctl_prbk_flush(tnfctl_handle_t *hndl, prbctlref_t *p) +{ + tnf_probevals_t probebuf; + + probebuf.probenum = p->probe_id; + probebuf.enabled = (p->wrkprbctl.test_func != NULL); + probebuf.traced = (p->wrkprbctl.commit_func == PRBK_DUMMY_COMMIT); + if (ioctl(hndl->kfd, TIFIOCSPROBEVALS, &probebuf) < 0) + return (tnfctl_status_map(errno)); + return (TNFCTL_ERR_NONE); +} + +/* + * Refresh our understanding of the existing probes in the kernel. + */ +tnfctl_errcode_t +_tnfctl_refresh_kernel(tnfctl_handle_t *hndl) +{ + int maxprobe, i; + int pos; + tnfctl_errcode_t prexstat; + tnf_probevals_t probebuf; + objlist_t *obj_p; + prbctlref_t *p = NULL; + + prexstat = prbk_get_buf_attrs(hndl); + if (prexstat) + return (prexstat); + /* + * Here is where you'd set obj_p->new to B_FALSE and obj_p->old to + * B_TRUE for all existing objects. We currently don't need + * it until we get modload/unload working correctly with probes + */ + if (ioctl(hndl->kfd, TIFIOCGMAXPROBE, &maxprobe) < 0) + return (tnfctl_status_map(errno)); + if (maxprobe == hndl->num_probes) { + /* XXX Inadequate in the presence of module unloading */ + return (TNFCTL_ERR_NONE); + } + + prexstat = alloc_probe_space(hndl, maxprobe); + if (prexstat) + return (prexstat); + + NOTE(NO_COMPETING_THREADS_NOW) + obj_p = hndl->objlist; + NOTE(COMPETING_THREADS_NOW) + assert((obj_p != NULL) && (obj_p->probes != NULL)); + + for (i = 1; i <= maxprobe; ++i) { + + if (i >= (obj_p->min_probe_num + obj_p->probecnt)) { + obj_p = obj_p->next; + } + + /* make sure we are in the correct object */ + assert(obj_p != NULL); + assert((i >= obj_p->min_probe_num) && + (i < (obj_p->min_probe_num + obj_p->probecnt))); + + /* get a pointer to correct probe */ + pos = i - obj_p->min_probe_num; + p = &(obj_p->probes[pos]); + assert((p != NULL) && (p->probe_id == i) && (p->probe_handle)); + + probebuf.probenum = i; + if (ioctl(hndl->kfd, TIFIOCGPROBEVALS, &probebuf) < 0) { + if (errno == ENOENT) { + /* + * This probe has vanished due to a module + * unload. + */ + p->probe_handle->valid = B_FALSE; + } else { + return (tnfctl_status_map(errno)); + } + } else { + if (p->probe_handle->valid == B_FALSE) { + /* + * seeing this probe for the first time + * (alloc_probe_space() initialized this + * "valid" field to B_FALSE) + */ + /* Update our info about this probe */ + p->wrkprbctl.test_func = (probebuf.enabled) ? + PRBK_DUMMY_TEST : NULL; + p->wrkprbctl.commit_func = (probebuf.traced) ? + PRBK_DUMMY_COMMIT : PRBK_DUMMY_ROLLBACK; + p->probe_handle->valid = B_TRUE; + if (probebuf.attrsize < sizeof (probebuf)) + probebuf.attrsize = sizeof (probebuf); + p->attr_string = malloc(probebuf.attrsize); + if (p->attr_string == NULL) + return (TNFCTL_ERR_ALLOCFAIL); + /* + * NOTE: the next statement is a structure + * copy and *not* a pointer assignment + */ +/* LINTED pointer cast may result in improper alignment */ + *(tnf_probevals_t *) p->attr_string = probebuf; + if (ioctl(hndl->kfd, TIFIOCGPROBESTRING, + p->attr_string) < 0) + return (tnfctl_status_map(errno)); + if (hndl->create_func) { + p->probe_handle->client_registered_data = + hndl->create_func(hndl, + p->probe_handle); + } + } + } + } + hndl->num_probes = maxprobe; + return (TNFCTL_ERR_NONE); +} + +/* + * check if there are any new probes in the kernel that we aren't aware of. + * If so, allocate space for those probes in our data structure. + */ +static tnfctl_errcode_t +alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe) +{ + objlist_t **o_pp; + objlist_t *obj_p, *nobj_p; + int min_probe_num, i; + prbctlref_t *probe_p; + + /* we know that: hndl->maxprobe != maxprobe */ + NOTE(NO_COMPETING_THREADS_NOW) + obj_p = hndl->objlist; + NOTE(COMPETING_THREADS_NOW) + if (obj_p == NULL) { + /* no objects allocated */ + o_pp = &(hndl->objlist); + min_probe_num = 1; + } else { + /* find last object */ + while (obj_p->next != NULL) { + /* reset new_probe field on modload/unload */ + obj_p->new_probe = B_FALSE; + obj_p = obj_p->next; + } + o_pp = &(obj_p->next); + min_probe_num = obj_p->min_probe_num + obj_p->probecnt; + } + + nobj_p = calloc(1, sizeof (objlist_t)); + if (nobj_p == NULL) + return (TNFCTL_ERR_ALLOCFAIL); + /* add to the linked list */ + *o_pp = nobj_p; + /* NULL, B_FALSE, or 0's not explicitly initialized */ + nobj_p->new_probe = B_TRUE; + nobj_p->new = B_TRUE; + nobj_p->objfd = -1; + nobj_p->min_probe_num = min_probe_num; + nobj_p->probecnt = maxprobe - min_probe_num + 1; + nobj_p->probes = calloc(nobj_p->probecnt, sizeof (prbctlref_t)); + if (nobj_p->probes == NULL) { + free(nobj_p); + return (TNFCTL_ERR_ALLOCFAIL); + } + + probe_p = &(nobj_p->probes[0]); + for (i = min_probe_num; i <= maxprobe; i++) { + NOTE(NO_COMPETING_THREADS_NOW) + probe_p->obj = nobj_p; + NOTE(COMPETING_THREADS_NOW) + probe_p->probe_id = i; + probe_p->probe_handle = calloc(1, sizeof (tnfctl_probe_t)); + if (probe_p->probe_handle == NULL) { + if (nobj_p->probes) + free(nobj_p->probes); + free(nobj_p); + return (TNFCTL_ERR_ALLOCFAIL); + } + probe_p->probe_handle->valid = B_FALSE; + probe_p->probe_handle->probe_p = probe_p; + /* link in probe handle into chain off tnfctl_handle_t */ + probe_p->probe_handle->next = hndl->probe_handle_list_head; + hndl->probe_handle_list_head = probe_p->probe_handle; + + probe_p++; + } + + hndl->num_probes = maxprobe; + return (TNFCTL_ERR_NONE); +} |
