OpenSolaris_b135/lib/libtnfctl/probes_ext.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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);
}