probes_ext.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <stddef.h>
#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);
}