/* * 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" /* * Published interfaces for probe control. */ #ifndef DEBUG #define NDEBUG 1 #endif #include #include #include #include #include #include #include "tnfctl_int.h" #include "kernel_int.h" #include "dbg.h" struct pr_func_args { tnfctl_probe_op_t func_p; void *calldata; }; tnfctl_errcode_t _tnfctl_destructor_wrapper(tnfctl_handle_t *, prbctlref_t *, void *); tnfctl_errcode_t _tnfctl_creator_wrapper(tnfctl_handle_t *, prbctlref_t *, void *); static tnfctl_errcode_t apply_func(tnfctl_handle_t *, prbctlref_t *, void *); static tnfctl_errcode_t check_operation(tnfctl_handle_t *, tnfctl_probe_t *); tnfctl_errcode_t tnfctl_register_funcs(tnfctl_handle_t *hndl, void *(*create_func)(tnfctl_handle_t *, tnfctl_probe_t *), void (*destroy_func)(void *)) { tnfctl_errcode_t prexstat; if (hndl->destroy_func) { /* * not the first time the register_funcs() is being called * First call currently registered destroy_func on all * probes */ prexstat = _tnfctl_probes_traverse(hndl, _tnfctl_destructor_wrapper, NULL); if (prexstat) return (prexstat); } /* set up new creator and destructor functions */ hndl->create_func = create_func; hndl->destroy_func = destroy_func; /* call new creator function for all current probes */ if (create_func) { prexstat = _tnfctl_probes_traverse(hndl, _tnfctl_creator_wrapper, NULL); if (prexstat) return (prexstat); } return (TNFCTL_ERR_NONE); } tnfctl_errcode_t _tnfctl_destructor_wrapper(tnfctl_handle_t *hndl, prbctlref_t *probe, void *cd) { assert(hndl->destroy_func); hndl->destroy_func(probe->probe_handle->client_registered_data); return (TNFCTL_ERR_NONE); } tnfctl_errcode_t _tnfctl_creator_wrapper(tnfctl_handle_t *hndl, prbctlref_t *probe, void *cd) { tnfctl_probe_t *p_handle; assert(hndl->create_func); p_handle = probe->probe_handle; p_handle->client_registered_data = hndl->create_func(hndl, p_handle); return (TNFCTL_ERR_NONE); } tnfctl_errcode_t tnfctl_probe_apply(tnfctl_handle_t *hndl, tnfctl_probe_op_t func_p, void *calldata) { struct pr_func_args pr_args; tnfctl_errcode_t prexstat; pr_args.func_p = func_p; pr_args.calldata = calldata; prexstat = _tnfctl_probes_traverse(hndl, apply_func, &pr_args); return (prexstat); } tnfctl_errcode_t tnfctl_probe_apply_ids(tnfctl_handle_t *hndl, ulong_t probe_count, ulong_t *probe_ids, tnfctl_probe_op_t func_p, void *calldata) { ulong_t *id_p; ulong_t i, pos; objlist_t *obj_p; prbctlref_t *probe; tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE; boolean_t release_lock; /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); /* select probes based on numbers */ id_p = probe_ids; for (i = 0; i < probe_count; i++, id_p++) { obj_p = hndl->objlist; while (obj_p) { if ((*id_p >= obj_p->min_probe_num) && (*id_p < (obj_p->min_probe_num + obj_p->probecnt))) { break; } obj_p = obj_p->next; } if (obj_p == NULL) { prexstat = TNFCTL_ERR_INVALIDPROBE; goto end_of_func; } pos = *id_p - obj_p->min_probe_num; probe = &(obj_p->probes[pos]); prexstat = func_p(hndl, probe->probe_handle, calldata); if (prexstat) goto end_of_func; } end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } tnfctl_errcode_t tnfctl_probe_state_get(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl, tnfctl_probe_state_t *state_p) { tnf_probe_control_t *prbctl_p; boolean_t release_lock; tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE; char **func_names; uintptr_t *func_addrs; if (hndl->mode == KERNEL_MODE) { prexstat = _tnfctl_refresh_kernel(hndl); if (prexstat) return (prexstat); } /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); if (probe_hndl->valid == B_FALSE) { prexstat = TNFCTL_ERR_INVALIDPROBE; goto end_of_func; } state_p->id = probe_hndl->probe_p->probe_id; state_p->attr_string = probe_hndl->probe_p->attr_string; prbctl_p = &probe_hndl->probe_p->wrkprbctl; state_p->enabled = (prbctl_p->test_func) ? B_TRUE : B_FALSE; state_p->traced = (prbctl_p->commit_func == (tnf_probe_func_t) hndl->commitfunc) ? B_TRUE : B_FALSE; state_p->new_probe = probe_hndl->probe_p->obj->new_probe; state_p->obj_name = probe_hndl->probe_p->obj->objname; state_p->client_registered_data = probe_hndl->client_registered_data; if (hndl->mode == KERNEL_MODE) { state_p->func_names = NULL; state_p->func_addrs = NULL; /* skip code upto label */ goto end_of_func; } /* process mode - get the probe functions */ prexstat = _tnfctl_comb_decode(hndl, (uintptr_t) prbctl_p->probe_func, &func_names, &func_addrs); if (prexstat) goto end_of_func; /* if there are any probe functions */ if (func_names[0] != NULL) { state_p->func_names = (const char * const *) func_names; state_p->func_addrs = func_addrs; } else { state_p->func_names = NULL; state_p->func_addrs = NULL; } end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } static tnfctl_errcode_t check_operation(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl) { tnfctl_errcode_t prexstat; if (hndl->mode == KERNEL_MODE) { prexstat = _tnfctl_refresh_kernel(hndl); if (prexstat) return (prexstat); } else if (hndl->trace_buf_state == TNFCTL_BUF_NONE) { /* process tracing */ return (TNFCTL_ERR_NOBUF); } if (hndl->trace_buf_state == TNFCTL_BUF_BROKEN) return (TNFCTL_ERR_BUFBROKEN); if (probe_hndl->valid == B_FALSE) { return (TNFCTL_ERR_INVALIDPROBE); } return (TNFCTL_ERR_NONE); } tnfctl_errcode_t tnfctl_probe_enable(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl, void *cd) { tnf_probe_control_t *prbctl_p; boolean_t release_lock; tnfctl_errcode_t prexstat; /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); prexstat = check_operation(hndl, probe_hndl); if (prexstat) goto end_of_func; prbctl_p = &probe_hndl->probe_p->wrkprbctl; prbctl_p->test_func = (tnf_probe_test_func_t) hndl->testfunc; prexstat = _tnfctl_flush_a_probe(hndl, probe_hndl->probe_p, offsetof(struct tnf_probe_control, test_func), sizeof (tnf_probe_test_func_t)); end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } tnfctl_errcode_t tnfctl_probe_disable(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl, void *cd) { tnf_probe_control_t *prbctl_p; boolean_t release_lock; tnfctl_errcode_t prexstat; /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); prexstat = check_operation(hndl, probe_hndl); if (prexstat) goto end_of_func; prbctl_p = &probe_hndl->probe_p->wrkprbctl; prbctl_p->test_func = (tnf_probe_test_func_t) NULL; prexstat = _tnfctl_flush_a_probe(hndl, probe_hndl->probe_p, offsetof(struct tnf_probe_control, test_func), sizeof (tnf_probe_test_func_t)); end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } tnfctl_errcode_t tnfctl_probe_trace(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl, void *cd) { tnf_probe_control_t *prbctl_p; boolean_t release_lock; tnfctl_errcode_t prexstat; /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); prexstat = check_operation(hndl, probe_hndl); if (prexstat) goto end_of_func; prbctl_p = &probe_hndl->probe_p->wrkprbctl; prbctl_p->commit_func = (tnf_probe_func_t) hndl->commitfunc; prexstat = _tnfctl_flush_a_probe(hndl, probe_hndl->probe_p, offsetof(struct tnf_probe_control, commit_func), sizeof (tnf_probe_func_t)); end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } tnfctl_errcode_t tnfctl_probe_untrace(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl, void *cd) { tnf_probe_control_t *prbctl_p; boolean_t release_lock; tnfctl_errcode_t prexstat; /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); prexstat = check_operation(hndl, probe_hndl); if (prexstat) goto end_of_func; prbctl_p = &probe_hndl->probe_p->wrkprbctl; prbctl_p->commit_func = (tnf_probe_func_t) hndl->rollbackfunc; prexstat = _tnfctl_flush_a_probe(hndl, probe_hndl->probe_p, offsetof(struct tnf_probe_control, commit_func), sizeof (tnf_probe_func_t)); end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } tnfctl_errcode_t tnfctl_probe_connect(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl, const char *lib_base_name, const char *func) { tnf_probe_control_t *prbctl_p; boolean_t release_lock; tnfctl_errcode_t prexstat; uintptr_t func_addr; uintptr_t comb; if (hndl->mode == KERNEL_MODE) return (TNFCTL_ERR_BADARG); /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); prexstat = check_operation(hndl, probe_hndl); if (prexstat) goto end_of_func; if (func == NULL) { prexstat = TNFCTL_ERR_NONE; goto end_of_func; } if (lib_base_name) { prexstat = _tnfctl_sym_obj_find(hndl, lib_base_name, func, &func_addr); } else { prexstat = _tnfctl_sym_find(hndl, func, &func_addr); } /* check if function address was found */ if (prexstat) goto end_of_func; prbctl_p = &probe_hndl->probe_p->wrkprbctl; prexstat = _tnfctl_comb_build(hndl, PRB_COMB_CHAIN, func_addr, (uintptr_t) prbctl_p->probe_func, &comb); if (prexstat) goto end_of_func; prbctl_p->probe_func = (tnf_probe_func_t) comb; prexstat = _tnfctl_flush_a_probe(hndl, probe_hndl->probe_p, offsetof(struct tnf_probe_control, probe_func), sizeof (tnf_probe_func_t)); end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } tnfctl_errcode_t tnfctl_probe_disconnect_all(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_hndl, void *cd) { tnf_probe_control_t *prbctl_p; boolean_t release_lock; tnfctl_errcode_t prexstat; if (hndl->mode == KERNEL_MODE) return (TNFCTL_ERR_BADARG); /*LINTED statement has no consequent: else*/ LOCK_SYNC(hndl, prexstat, release_lock); prexstat = check_operation(hndl, probe_hndl); if (prexstat) goto end_of_func; prbctl_p = &probe_hndl->probe_p->wrkprbctl; prbctl_p->probe_func = (tnf_probe_func_t) hndl->endfunc; prexstat = _tnfctl_flush_a_probe(hndl, probe_hndl->probe_p, offsetof(struct tnf_probe_control, probe_func), sizeof (tnf_probe_func_t)); end_of_func: /*LINTED statement has no consequent: else*/ UNLOCK(hndl, release_lock); return (prexstat); } /* * Important that this function be tail recursive to minimize depth * of call chain that is called for every probe */ static tnfctl_errcode_t apply_func(tnfctl_handle_t *hndl, prbctlref_t *probe, void *cd) { struct pr_func_args *args = cd; tnfctl_errcode_t prexstat; /* Call function only if match_func returns true */ prexstat = (*(args->func_p))(hndl, probe->probe_handle, args->calldata); return (prexstat); }