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