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 * 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