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	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Includes
30  */
31 
32 #ifndef DEBUG
33 #define	NDEBUG	1
34 #endif
35 
36 #include <assert.h>
37 #include <limits.h>
38 #include <values.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "tnf_trace.h"
44 
45 /*
46  * Defines
47  */
48 
49 #define	ASSERT(expr)	assert(expr)
50 
51 #define	ENCODED_TAG(tag, tagarg) 		\
52 	(((tag) | ((tagarg) & 0xfffc)) | TNF_REF32_T_PAIR)
53 
54 /*
55  * CAUTION: halfword_accessible assumes that the pointer is to a reclaimable
56  *		block - i.e. negative offsets have a 0 in high bit
57  */
58 #define	HALFWORD_ACCESSIBLE(x) 			\
59 	((((x) & 0xffff8000) == 0) || (((x) & 0xffff8000) == 0x7fff8000))
60 
61 /*
62  * Check that x can be encoded in tagarg slot
63  * Same as above, but operates on ints (no space bit)
64  */
65 #define	TAGARG_CHECK(x)				\
66 	(((x) < 32767) && ((x) > -32768))
67 
68 /*
69  * Check that hi 32 bits of hrtime are zero
70  */
71 #define	TIME_CHECK(x)				\
72 	(((x) >> 32) == 0)
73 
74 /*
75  * CAUTION: Use the following macro only when doing a self relative pointer
76  *		to a target in the same block
77  */
78 #define	PTR_DIFF(item, ref)			\
79 	((tnf_ref32_t)((tnf_record_p)(item) - (tnf_record_p)(ref)))
80 
81 /*
82  * Typedefs
83  */
84 
85 typedef struct {
86 	tnf_probe_event_t		probe_event;
87 	tnf_time_delta_t		time_delta;
88 } probe_event_prototype_t;
89 
90 /*
91  * tnf_trace_alloc
92  * 	the probe allocation function
93  */
94 
95 #define	IS_NEWBLOCK(blockArray, dataBuffer) \
96 	    (((caddr_t)&blockArray[1]) == (caddr_t)dataBuffer)
97 
98 void *
tnf_trace_alloc(tnf_ops_t * ops,tnf_probe_control_t * probe_p,tnf_probe_setup_t * set_p)99 tnf_trace_alloc(tnf_ops_t *ops, tnf_probe_control_t *probe_p,
100     tnf_probe_setup_t *set_p)
101 {
102 	TNFW_B_WCB		*wcb;
103 	volatile char 		*file_start;
104 	uintptr_t		probe_index;
105 	tnf_record_p		sched_record_p;
106 	tnf_reference_t		sched_offset, tag_disp;
107 	tnf_block_header_t	*block;
108 	tnf_uint32_t		shift;
109 	probe_event_prototype_t *buffer;
110 	hrtime_t 		curr_time, time_diff;
111 	tnf_schedule_t		*sched;
112 	tnf_ref32_t		*fwd_p;
113 	ulong_t			size, asize;
114 #if defined(DEBUG) || defined(VERYVERBOSE)
115 	char tmp_buf[512];
116 #endif
117 
118 	ASSERT(ops != NULL);
119 
120 	/* check if already in a probe */
121 	if (ops->busy)
122 		return (NULL);
123 	ops->busy = 1;
124 
125 
126 #ifdef VERYVERBOSE
127 	sprintf(tmp_buf, "tnf_trace_alloc: begin\n");
128 	(void) write(2, tmp_buf, strlen(tmp_buf));
129 #endif
130 
131 	/*
132 	 * CAUTION: Ordering of function calls in this file is critical because
133 	 * we call TNFW_B_GIVEBACK. Between the time we allocate space for the
134 	 * event and call TNFW_B_GIVEBACK there can be no other allocations!!
135 	 */
136 
137 	/*
138 	 * Write probe tag if needed
139 	 */
140 	probe_index = probe_p->index;
141 #ifdef VERYVERBOSE
142 	sprintf(tmp_buf, "tnf_trace_alloc: (1) probe_index=%p\n", probe_index);
143 	(void) write(2, tmp_buf, strlen(tmp_buf));
144 #endif
145 	if (probe_index == 0) {
146 	    if ((probe_index = tnf_probe_tag(ops, probe_p)) == 0) {
147 #ifdef VERYVERBOSE
148 	    sprintf(tmp_buf, "tnf_trace_alloc: (2) probe_index=%p\n",
149 		    probe_index);
150 	    (void) write(2, tmp_buf, strlen(tmp_buf));
151 	    sprintf(tmp_buf, "tnf_trace_alloc: goto null_ret\n");
152 	    (void) write(2, tmp_buf, strlen(tmp_buf));
153 	    fflush(stderr);
154 	    sleep(2);
155 #endif
156 	    goto null_ret;
157 	    }
158 #ifdef VERYVERBOSE
159 	    sprintf(tmp_buf, "tnf_trace_alloc: (3) probe_index=%p\n",
160 		probe_index);
161 	    (void) write(2, tmp_buf, strlen(tmp_buf));
162 	    fflush(stderr);
163 #endif
164 	}
165 	/*
166 	 * Determine how much memory is required
167 	 */
168 	size = probe_p->tnf_event_size;
169 	asize = size + sizeof (tnf_ref32_t);	/* one fwd ptr */
170 
171 	if (PROBE_IS_FILE_PTR(probe_index)) {
172 		/* common case - probe_index is a file ptr */
173 		tag_disp = probe_index & PROBE_INDEX_LOW_MASK;
174 	} else {
175 		/* rare case -- get an extra fwd ptr */
176 		asize += sizeof (tnf_ref32_t);
177 	}
178 
179 	/*
180 	 * Allocate memory
181 	 */
182 	wcb = &(ops->wcb);
183 
184 #ifdef _TNF_VERBOSE
185 	sprintf(tmp_buf, "tnf_trace_alloc, wcb=%p\n", wcb);
186 	(void) write(2, tmp_buf, strlen(tmp_buf));
187 #endif
188 
189 	buffer = ops->alloc(wcb, asize, ops->mode);
190 
191 #ifdef _TNF_VERBOSE
192 	sprintf(tmp_buf, "tnf_trace_alloc, buffer=%p\n", buffer);
193 	(void) write(2, tmp_buf, strlen(tmp_buf));
194 #endif
195 	if (buffer == NULL)
196 		goto null_ret;
197 
198 	/* LINTED pointer cast may result in improper alignment */
199 	fwd_p = (tnf_ref32_t *) ((char *)(buffer) + size);
200 
201 #ifdef _TNF_VERBOSE
202 	sprintf(tmp_buf, "tnf_trace_alloc, fwd_pr=%p\n", fwd_p);
203 	(void) write(2, tmp_buf, strlen(tmp_buf));
204 #endif
205 
206 	/* set file_start after calling alloc because it allocs the file */
207 	file_start = _tnfw_b_control->tnf_buffer;
208 
209 	/* Check if the probe tag needs more work */
210 	if (!PROBE_IS_FILE_PTR(probe_index)) {
211 		/* LINTED use up first fwd ptr */
212 		*fwd_p = TNF_REF32_MAKE_PERMANENT(
213 		/* LINTED ptr subtraction */
214 			(tnf_record_p)probe_index - (tnf_record_p) file_start);
215 		/* LINTED ptr subtraction */
216 		tag_disp = PTR_DIFF(fwd_p, buffer);
217 		ASSERT(TAGARG_CHECK(tag_disp));
218 		tag_disp |= TNF_TAG16_T_REL;
219 		tag_disp = tag_disp << TNF_REF32_TAG16_SHIFT;
220 		fwd_p++;
221 	}
222 
223 	/*
224 	 * Get timestamp
225 	 */
226 	curr_time = gethrtime();
227 
228 	/*
229 	 * initialize and write schedule record if needed
230 	 * watch out for sched->record_p - it has to be checked after alloc is
231 	 * called for the event, because it could be side effected by alloc
232 	 * if a fork happened.  Pre-requisite to our algorithm - if a fork
233 	 * happens all other threads have to be quiescent i.e. not in a probe.
234 	 */
235 	sched = &(ops->schedule);
236 
237 #ifdef _TNF_VERBOSE
238 	sprintf(tmp_buf, "tnf_trace_alloc, sched=%p\n", sched);
239 	(void) write(2, tmp_buf, strlen(tmp_buf));
240 #endif
241 
242 	/* LINTED pointer cast */
243 	shift = ((tnf_buf_file_header_t *)file_start)->com.file_log_size;
244 	block = (tnf_block_header_t *)((ulong_t)buffer & TNF_BLOCK_MASK);
245 
246 	if ((sched_record_p = sched->record_p) == NULL ||
247 	    IS_NEWBLOCK(block, buffer)) {
248 		/* No record written yet */
249 		goto new_schedule;
250 	}
251 
252 	/*
253 	 * Note: Don't bother about space bit here, because we'll
254 	 * only use bits 15:2 anyway
255 	 */
256 	sched_offset = ((sched->record_gen - block->generation) << shift) +
257 		/* LINTED - ptr subtraction */
258 		(uint_t) (sched_record_p - (caddr_t)buffer);
259 
260 	if (!TAGARG_CHECK(sched_offset))
261 		/* Record too far away to reference */
262 		goto new_schedule;
263 
264 	time_diff = curr_time - sched->time_base;
265 	if (!TIME_CHECK(time_diff))
266 		/* Time delta can't fit in 32 bits */
267 		goto new_schedule;
268 
269 	/*
270 	 * Can reuse existing schedule record
271 	 * Since we did not allocate any more space, can giveback
272 	 */
273 	/* LINTED - GIVEBACK returns a pointer subtraction */
274 	TNFW_B_GIVEBACK(wcb, fwd_p);
275 
276 good_ret:
277 	/*
278 	 * Store return params and two common event members, return buffer
279 	 */
280 	set_p->tpd_p = ops;
281 	set_p->buffer_p = buffer;
282 	set_p->probe_p = probe_p;
283 	buffer->probe_event = ENCODED_TAG(tag_disp, sched_offset);
284 	/* LINTED - TIME_CHECK already passed, see above */
285 	buffer->time_delta = tnf_time_delta(ops, (unsigned long)time_diff,
286 	    &buffer->probe_time_delta);
287 	return (buffer);
288 
289 new_schedule:
290 	/*
291 	 * Write a new schedule record for this thread
292 	 */
293 #ifdef VERYVERBOSE
294 	sprintf(tmp_buf, "	tnf_trace_alloc: initializing "
295 				"new schedule record\n");
296 	(void) write(2, tmp_buf, strlen(tmp_buf));
297 #endif
298 	_tnf_sched_init(sched, curr_time);
299 	time_diff = 0;
300 	if ((sched_record_p = tnf_schedule_write(ops, sched)) != NULL) {
301 		/* use one of the extra alloced words for the forwarding ptr */
302 		/* LINTED - ptr subtraction */
303 		*fwd_p = TNF_REF32_MAKE_RECLAIMABLE(
304 			((sched->record_gen - block->generation) << shift) +
305 			(sched_record_p - (tnf_record_p)fwd_p));
306 		/* LINTED - ptr subtraction */
307 		sched_offset = PTR_DIFF(fwd_p, buffer);
308 		ASSERT(TAGARG_CHECK(sched_offset));
309 	} else {
310 		/* Allocation failed (tracing may have been stopped) */
311 		sched_offset = 0;
312 		*fwd_p = TNF_NULL;
313 	}
314 	goto good_ret;
315 
316 null_ret:
317 	/*
318 	 * reset re-entrancy protector, because tnf_trace_end() will
319 	 * not be called
320 	 */
321 #ifdef VERYVERBOSE
322 	sprintf(tmp_buf, "tnf_trace_alloc: null return\n");
323 	(void) write(2, tmp_buf, strlen(tmp_buf));
324 #endif
325 	ops->busy = 0;
326 	return (NULL);
327 }
328 
329 /*
330  * tnf_trace_end
331  *	the last (usually only) function in the list of probe functions
332  */
333 void
tnf_trace_end(tnf_probe_setup_t * set_p)334 tnf_trace_end(tnf_probe_setup_t *set_p)
335 {
336 #ifdef VERYVERBOSE
337 	char tmp_buf[512];
338 
339 	sprintf(tmp_buf, "tnf_trace_end: \n");
340 	(void) write(2, tmp_buf, strlen(tmp_buf));
341 #endif
342 
343 	(set_p->probe_p->commit_func)(set_p);
344 	set_p->tpd_p->busy = 0;
345 }
346 
347 /*
348  * tnf_trace_commit
349  *	a probe commit function that really commits trace data
350  */
351 void
tnf_trace_commit(tnf_probe_setup_t * set_p)352 tnf_trace_commit(tnf_probe_setup_t *set_p)
353 {
354 #ifdef VERYVERBOSE
355 	char tmp_buf[512];
356 
357 	sprintf(tmp_buf, "tnf_trace_commit: \n\n");
358 	(void) write(2, tmp_buf, strlen(tmp_buf));
359 #endif
360 	(void) set_p->tpd_p->commit(&(set_p->tpd_p->wcb));
361 
362 	return;
363 
364 }
365 
366 /*
367  * tnf_trace_rollback
368  *	a probe commit function that unrolls trace data
369  */
370 void
tnf_trace_rollback(tnf_probe_setup_t * set_p)371 tnf_trace_rollback(tnf_probe_setup_t *set_p)
372 {
373 #ifdef VERYVERBOSE
374 	char tmp_buf[512];
375 
376 	sprintf(tmp_buf, "tnf_trace_rollback: \n\n");
377 	(void) write(2, tmp_buf, strlen(tmp_buf));
378 #endif
379 	(void) set_p->tpd_p->rollback(&(set_p->tpd_p->wcb));
380 
381 	return;
382 
383 }
384 
385 /*
386  * tnf_allocate
387  *	exported interface for allocating trace memory
388  */
389 
390 void *
tnf_allocate(tnf_ops_t * ops,size_t size)391 tnf_allocate(tnf_ops_t *ops, size_t size)
392 {
393 	void *retval;
394 	char tmp_buf[512];
395 
396 #ifdef _TNF_VERBOSE
397 	sprintf(tmp_buf, "tnf_allocate\n");
398 	(void) write(2, tmp_buf, strlen(tmp_buf));
399 #endif
400 
401 	retval = ops->alloc(&(ops->wcb), size, ops->mode);
402 
403 #ifdef _TNF_VERBOSE
404 	sprintf(tmp_buf, "tnf_allocate, retval=%p\n", retval);
405 	(void) write(2, tmp_buf, strlen(tmp_buf));
406 #endif
407 
408 	return (retval);
409 }
410