Home | History | Annotate | Download | only in tnf
      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	"@(#)trace_funcs.c	1.24	05/06/08 SMI"
     27 
     28 /*
     29  * Includes
     30  */
     31 #include <sys/param.h>
     32 #include <sys/time.h>
     33 #include <sys/debug.h>
     34 #include <sys/cmn_err.h>
     35 #include <sys/tnf.h>
     36 
     37 #include "tnf_buf.h"
     38 #include "tnf_types.h"
     39 #include "tnf_trace.h"
     40 
     41 /*
     42  * Defines
     43  */
     44 
     45 #define	ENCODED_TAG(tag, tagarg) 		\
     46 	((tag) | ((tagarg) & 0xfffc) | TNF_REF32_T_PAIR)
     47 
     48 /*
     49  * CAUTION: halfword_accessible assumes that the pointer is to a reclaimable
     50  *		block - i.e. negative offsets have a 0 in high bit
     51  */
     52 #define	HALFWORD_ACCESSIBLE(x) 			\
     53 	((((x) & 0xffff8000) == 0) || (((x) & 0xffff8000) == 0x7fff8000))
     54 
     55 /*
     56  * Check that x can be encoded in tagarg slot
     57  * Same as above, but operates on ints (no space bit)
     58  */
     59 #define	TAGARG_CHECK(x)				\
     60 	(((x) < 32767) && ((x) > -32768))
     61 
     62 /*
     63  * Check that hit 32 bits of hrtime are zero
     64  */
     65 #define	TIME_CHECK(x) 			\
     66 	(((x) >> 32) == 0)
     67 
     68 /*
     69  * CAUTION: Use the following macro only when doing a self relative pointer
     70  *		to a target in the same block
     71  */
     72 #define	PTR_DIFF(item, ref)			\
     73 	((tnf_ref32_t)((tnf_record_p)(item) - (tnf_record_p)(ref)))
     74 
     75 /*
     76  * Typedefs
     77  */
     78 
     79 typedef struct {
     80 	tnf_probe_event_t		probe_event;
     81 	tnf_time_delta_t		time_delta;
     82 } probe_event_prototype_t;
     83 
     84 /*
     85  * Declarations
     86  */
     87 
     88 /*
     89  * tnf_trace_alloc
     90  * 	the probe allocation function
     91  */
     92 
     93 void *
     94 tnf_trace_alloc(tnf_ops_t *ops, tnf_probe_control_t *probe_p,
     95     tnf_probe_setup_t *set_p)
     96 {
     97 	TNFW_B_WCB		*wcb;
     98 	uintptr_t 		probe_index;
     99 	tnf_record_p		sched_record_p;
    100 	tnf_reference_t 	sched_offset, tag_disp;
    101 	tnf_block_header_t	*block;
    102 	tnf_uint32_t		shift;
    103 	probe_event_prototype_t *buffer;
    104 	hrtime_t 		curr_time, time_diff;
    105 	tnf_schedule_t		*sched;
    106 	tnf_ref32_t		*fwd_p;
    107 	size_t			size, asize;
    108 
    109 	/*
    110 	 * Check the "tracing active" flag after setting the busy bit;
    111 	 * this avoids a race in which we check the "tracing active"
    112 	 * flag, then it gets turned off, and the buffer gets
    113 	 * deallocated, before we've set the busy bit.
    114 	 */
    115 	if (!lock_try(&ops->busy)) /* atomic op flushes WB */
    116 		return (NULL);
    117 	if (!tnf_tracing_active)
    118 		goto null_ret;
    119 
    120 	/*
    121 	 * Write probe tag if needed
    122 	 */
    123 	probe_index = probe_p->index;
    124 	if (probe_index == 0) {
    125 		if ((probe_index = tnf_probe_tag(ops, probe_p)) == 0)
    126 			goto null_ret;
    127 	}
    128 
    129 	/*
    130 	 * Determine how much memory is required
    131 	 */
    132 	size = probe_p->tnf_event_size;
    133 	asize = size + sizeof (tnf_ref32_t); /* one fwd ptr */
    134 
    135 	if (PROBE_IS_FILE_PTR(probe_index))
    136 		/* common case - probe_index is a file ptr */
    137 		/* LINTED assignment of 64-bit integer to 32-bit integer */
    138 		tag_disp = probe_index & PROBE_INDEX_LOW_MASK;
    139 	else
    140 		/* rare case -- get an extra fwd ptr */
    141 		asize += sizeof (tnf_ref32_t);
    142 
    143 	/*
    144 	 * Allocate memory
    145 	 */
    146 	wcb = &ops->wcb;
    147 	/* LINTED assignment of 64-bit integer to 16-bit integer */
    148 	TNFW_B_ALLOC(wcb, asize, buffer, probe_event_prototype_t *);
    149 	if (buffer == NULL)
    150 		goto null_ret;
    151 
    152 	/* LINTED pointer cast may result in improper alignment */
    153 	fwd_p = (tnf_ref32_t *)((char *)buffer + size);
    154 
    155 	/*
    156 	 * Check if the probe tag needs more work
    157 	 */
    158 	if (!PROBE_IS_FILE_PTR(probe_index)) {
    159 		/* use up first fwd ptr */
    160 		/* LINTED assignment of 64-bit integer to 32-bit integer */
    161 		*fwd_p = TNF_REF32_MAKE_PERMANENT(
    162 			(tnf_record_p)probe_index - tnf_buf);
    163 		/* LINTED cast from 64-bit integer to 32-bit integer */
    164 		tag_disp = PTR_DIFF(fwd_p, buffer);
    165 		tag_disp |= TNF_TAG16_T_REL;
    166 		tag_disp = tag_disp << TNF_REF32_TAG16_SHIFT;
    167 		fwd_p++;
    168 	}
    169 
    170 	/*
    171 	 * Get timestamp
    172 	 */
    173 	curr_time = gethrtime();
    174 
    175 	/*
    176 	 * Write schedule record if needed
    177 	 */
    178 	sched = &ops->schedule;
    179 
    180 	/* LINTED pointer cast */
    181 	shift = ((tnf_buf_file_header_t *)tnf_buf)->com.file_log_size;
    182 	block = (tnf_block_header_t *)((uintptr_t)buffer & TNF_BLOCK_MASK);
    183 
    184 	if ((sched_record_p = sched->record_p) == NULL)
    185 		/* No record written yet */
    186 		goto new_schedule;
    187 
    188 	/*
    189 	 * Note: Don't bother about space bit here, because we'll
    190 	 * only use bits 15:2 anyway
    191 	 */
    192 #if defined(_LP64)
    193 	/* LINTED  assignment of 64-bit integer to 32-bit integer */
    194 	sched_offset = ((sched->record_gen - block->generation) << shift) +
    195 		(sched_record_p - (caddr_t)buffer);
    196 #else
    197 	sched_offset = ((sched->record_gen - block->generation) << shift) +
    198 		(sched_record_p - (caddr_t)buffer);
    199 #endif
    200 	if (!TAGARG_CHECK(sched_offset))
    201 		/* Record too far away to reference */
    202 		goto new_schedule;
    203 
    204 	time_diff = curr_time - sched->time_base;
    205 	if (!TIME_CHECK(time_diff))
    206 		/* Time delta can't fit in 32 bits */
    207 		goto new_schedule;
    208 
    209 	if (sched->cpuid != CPU->cpu_id)
    210 		/* CPU information is invalid */
    211 		goto new_schedule;
    212 
    213 	/*
    214 	 * Can reuse existing schedule record
    215 	 * Since we did not allocate any more space, can giveback
    216 	 */
    217 #if defined(_LP64)
    218 	/* LINTED warning: assignment of 64-bit integer to 16-bit integer */
    219 	TNFW_B_GIVEBACK(wcb, fwd_p);
    220 #else
    221 	TNFW_B_GIVEBACK(wcb, fwd_p);
    222 #endif
    223 
    224 good_ret:
    225 	/*
    226 	 * Store return params and two common event members, return buffer
    227 	 */
    228 	set_p->tpd_p = ops;
    229 	set_p->buffer_p = buffer;
    230 	set_p->probe_p = probe_p;
    231 	buffer->probe_event = ENCODED_TAG(tag_disp, sched_offset);
    232 #if defined(_LP64)
    233 	/* LINTED assignment of 64-bit integer to 32-bit integer */
    234 	buffer->time_delta = tnf_time_delta(ops, (unsigned long)time_diff,
    235 	    &buffer->probe_time_delta);
    236 #else
    237 	buffer->time_delta = tnf_time_delta(ops, (unsigned long)time_diff,
    238 		&buffer->probe_time_delta);
    239 #endif
    240 	return (buffer);
    241 
    242 new_schedule:
    243 	/*
    244 	 * Write a new schedule record for this thread
    245 	 */
    246 	sched->cpuid = CPU->cpu_id;
    247 	sched->time_base = curr_time;
    248 	time_diff = 0;
    249 	if ((sched_record_p = tnf_kernel_schedule(ops, sched)) != NULL) {
    250 		/* use one of the extra alloced words for the forwarding ptr */
    251 #if defined(_LP64)
    252 		/* LINTED assignment of 64-bit integer to 32-bit integer */
    253 		*fwd_p = TNF_REF32_MAKE_RECLAIMABLE(
    254 			((sched->record_gen - block->generation) << shift) +
    255 			    /* LINTED */
    256 			    (sched_record_p - (tnf_record_p)fwd_p));
    257 		/* LINTED cast from 64-bit integer to 32-bit integer */
    258 		sched_offset = PTR_DIFF(fwd_p, buffer);
    259 #else
    260 		*fwd_p = TNF_REF32_MAKE_RECLAIMABLE(
    261 			((sched->record_gen - block->generation) << shift) +
    262 			(sched_record_p - (tnf_record_p)fwd_p));
    263 		sched_offset = PTR_DIFF(fwd_p, buffer);
    264 #endif
    265 	} else {
    266 		/* Allocation failed (tracing may have been stopped) */
    267 		sched_offset = 0;
    268 		*fwd_p = TNF_NULL;
    269 	}
    270 	goto good_ret;
    271 
    272 null_ret:
    273 	/*
    274 	 * Clear busy flag and return null
    275 	 */
    276 	LOCK_INIT_CLEAR(&ops->busy);	/* XXX save a call */
    277 	return (NULL);
    278 }
    279 
    280 /*
    281  * tnf_trace_commit
    282  */
    283 void
    284 tnf_trace_commit(tnf_probe_setup_t *set_p)
    285 {
    286 	tnf_ops_t	*ops;
    287 	TNFW_B_WCB	*wcb;
    288 	TNFW_B_POS 	*pos;
    289 
    290 	ops = set_p->tpd_p;
    291 	wcb = &ops->wcb;
    292 
    293 	/* commit reusable bytes */
    294 	pos = &wcb->tnfw_w_pos;
    295 	TNFW_B_COMMIT(pos);
    296 
    297 	/* commit tag bytes */
    298 	pos = &wcb->tnfw_w_tag_pos;
    299 	TNFW_B_COMMIT(pos);
    300 
    301 	/* clear busy flag */
    302 	LOCK_INIT_CLEAR(&ops->busy);	/* XXX save a call */
    303 }
    304 
    305 /*
    306  * tnf_trace_rollback
    307  */
    308 void
    309 tnf_trace_rollback(tnf_probe_setup_t *set_p)
    310 {
    311 	tnf_ops_t	*ops;
    312 	TNFW_B_WCB	*wcb;
    313 	TNFW_B_POS 	*pos;
    314 
    315 	ops = set_p->tpd_p;
    316 	wcb = &ops->wcb;
    317 
    318 	/* rollback data bytes */
    319 	pos = &wcb->tnfw_w_pos;
    320 	TNFW_B_ROLLBACK(pos);
    321 
    322 	/* commit tag bytes */
    323 	pos = &wcb->tnfw_w_tag_pos;
    324 	TNFW_B_COMMIT(pos);
    325 
    326 	/* zap schedule record, since it is in uncommitted store */
    327 	ops->schedule.record_p = NULL;
    328 
    329 	/* clear busy flag */
    330 	LOCK_INIT_CLEAR(&ops->busy);	/* XXX save a call */
    331 }
    332 
    333 /*
    334  * tnf_allocate
    335  *	exported interface for allocating trace memory
    336  */
    337 
    338 void *
    339 tnf_allocate(tnf_ops_t *ops, size_t size)
    340 {
    341 	return (tnfw_b_alloc(&ops->wcb, size, ops->mode));
    342 }
    343