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 #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 *
tnf_trace_alloc(tnf_ops_t * ops,tnf_probe_control_t * probe_p,tnf_probe_setup_t * set_p)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
tnf_trace_commit(tnf_probe_setup_t * set_p)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
tnf_trace_rollback(tnf_probe_setup_t * set_p)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 *
tnf_allocate(tnf_ops_t * ops,size_t size)339 tnf_allocate(tnf_ops_t *ops, size_t size)
340 {
341 return (tnfw_b_alloc(&ops->wcb, size, ops->mode));
342 }
343