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