/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1994, by Sun Microsytems, Inc. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Includes */ #ifndef DEBUG #define NDEBUG 1 #endif #include #include #include #include #include #include #include "tnf_trace.h" /* * Defines */ #define ASSERT(expr) assert(expr) #define ENCODED_TAG(tag, tagarg) \ (((tag) | ((tagarg) & 0xfffc)) | TNF_REF32_T_PAIR) /* * CAUTION: halfword_accessible assumes that the pointer is to a reclaimable * block - i.e. negative offsets have a 0 in high bit */ #define HALFWORD_ACCESSIBLE(x) \ ((((x) & 0xffff8000) == 0) || (((x) & 0xffff8000) == 0x7fff8000)) /* * Check that x can be encoded in tagarg slot * Same as above, but operates on ints (no space bit) */ #define TAGARG_CHECK(x) \ (((x) < 32767) && ((x) > -32768)) /* * Check that hi 32 bits of hrtime are zero */ #define TIME_CHECK(x) \ (((x) >> 32) == 0) /* * CAUTION: Use the following macro only when doing a self relative pointer * to a target in the same block */ #define PTR_DIFF(item, ref) \ ((tnf_ref32_t)((tnf_record_p)(item) - (tnf_record_p)(ref))) /* * Typedefs */ typedef struct { tnf_probe_event_t probe_event; tnf_time_delta_t time_delta; } probe_event_prototype_t; /* * tnf_trace_alloc * the probe allocation function */ #define IS_NEWBLOCK(blockArray, dataBuffer) \ (((caddr_t)&blockArray[1]) == (caddr_t)dataBuffer) void * tnf_trace_alloc(tnf_ops_t *ops, tnf_probe_control_t *probe_p, tnf_probe_setup_t *set_p) { TNFW_B_WCB *wcb; volatile char *file_start; uintptr_t probe_index; tnf_record_p sched_record_p; tnf_reference_t sched_offset, tag_disp; tnf_block_header_t *block; tnf_uint32_t shift; probe_event_prototype_t *buffer; hrtime_t curr_time, time_diff; tnf_schedule_t *sched; tnf_ref32_t *fwd_p; ulong_t size, asize; #if defined(DEBUG) || defined(VERYVERBOSE) char tmp_buf[512]; #endif ASSERT(ops != NULL); /* check if already in a probe */ if (ops->busy) return (NULL); ops->busy = 1; #ifdef VERYVERBOSE sprintf(tmp_buf, "tnf_trace_alloc: begin\n"); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif /* * CAUTION: Ordering of function calls in this file is critical because * we call TNFW_B_GIVEBACK. Between the time we allocate space for the * event and call TNFW_B_GIVEBACK there can be no other allocations!! */ /* * Write probe tag if needed */ probe_index = probe_p->index; #ifdef VERYVERBOSE sprintf(tmp_buf, "tnf_trace_alloc: (1) probe_index=%p\n", probe_index); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif if (probe_index == 0) { if ((probe_index = tnf_probe_tag(ops, probe_p)) == 0) { #ifdef VERYVERBOSE sprintf(tmp_buf, "tnf_trace_alloc: (2) probe_index=%p\n", probe_index); (void) write(2, tmp_buf, strlen(tmp_buf)); sprintf(tmp_buf, "tnf_trace_alloc: goto null_ret\n"); (void) write(2, tmp_buf, strlen(tmp_buf)); fflush(stderr); sleep(2); #endif goto null_ret; } #ifdef VERYVERBOSE sprintf(tmp_buf, "tnf_trace_alloc: (3) probe_index=%p\n", probe_index); (void) write(2, tmp_buf, strlen(tmp_buf)); fflush(stderr); #endif } /* * Determine how much memory is required */ size = probe_p->tnf_event_size; asize = size + sizeof (tnf_ref32_t); /* one fwd ptr */ if (PROBE_IS_FILE_PTR(probe_index)) { /* common case - probe_index is a file ptr */ tag_disp = probe_index & PROBE_INDEX_LOW_MASK; } else { /* rare case -- get an extra fwd ptr */ asize += sizeof (tnf_ref32_t); } /* * Allocate memory */ wcb = &(ops->wcb); #ifdef _TNF_VERBOSE sprintf(tmp_buf, "tnf_trace_alloc, wcb=%p\n", wcb); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif buffer = ops->alloc(wcb, asize, ops->mode); #ifdef _TNF_VERBOSE sprintf(tmp_buf, "tnf_trace_alloc, buffer=%p\n", buffer); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif if (buffer == NULL) goto null_ret; /* LINTED pointer cast may result in improper alignment */ fwd_p = (tnf_ref32_t *) ((char *)(buffer) + size); #ifdef _TNF_VERBOSE sprintf(tmp_buf, "tnf_trace_alloc, fwd_pr=%p\n", fwd_p); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif /* set file_start after calling alloc because it allocs the file */ file_start = _tnfw_b_control->tnf_buffer; /* Check if the probe tag needs more work */ if (!PROBE_IS_FILE_PTR(probe_index)) { /* LINTED use up first fwd ptr */ *fwd_p = TNF_REF32_MAKE_PERMANENT( /* LINTED ptr subtraction */ (tnf_record_p)probe_index - (tnf_record_p) file_start); /* LINTED ptr subtraction */ tag_disp = PTR_DIFF(fwd_p, buffer); ASSERT(TAGARG_CHECK(tag_disp)); tag_disp |= TNF_TAG16_T_REL; tag_disp = tag_disp << TNF_REF32_TAG16_SHIFT; fwd_p++; } /* * Get timestamp */ curr_time = gethrtime(); /* * initialize and write schedule record if needed * watch out for sched->record_p - it has to be checked after alloc is * called for the event, because it could be side effected by alloc * if a fork happened. Pre-requisite to our algorithm - if a fork * happens all other threads have to be quiescent i.e. not in a probe. */ sched = &(ops->schedule); #ifdef _TNF_VERBOSE sprintf(tmp_buf, "tnf_trace_alloc, sched=%p\n", sched); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif /* LINTED pointer cast */ shift = ((tnf_buf_file_header_t *)file_start)->com.file_log_size; block = (tnf_block_header_t *)((ulong_t)buffer & TNF_BLOCK_MASK); if ((sched_record_p = sched->record_p) == NULL || IS_NEWBLOCK(block, buffer)) { /* No record written yet */ goto new_schedule; } /* * Note: Don't bother about space bit here, because we'll * only use bits 15:2 anyway */ sched_offset = ((sched->record_gen - block->generation) << shift) + /* LINTED - ptr subtraction */ (uint_t) (sched_record_p - (caddr_t)buffer); if (!TAGARG_CHECK(sched_offset)) /* Record too far away to reference */ goto new_schedule; time_diff = curr_time - sched->time_base; if (!TIME_CHECK(time_diff)) /* Time delta can't fit in 32 bits */ goto new_schedule; /* * Can reuse existing schedule record * Since we did not allocate any more space, can giveback */ /* LINTED - GIVEBACK returns a pointer subtraction */ TNFW_B_GIVEBACK(wcb, fwd_p); good_ret: /* * Store return params and two common event members, return buffer */ set_p->tpd_p = ops; set_p->buffer_p = buffer; set_p->probe_p = probe_p; buffer->probe_event = ENCODED_TAG(tag_disp, sched_offset); /* LINTED - TIME_CHECK already passed, see above */ buffer->time_delta = tnf_time_delta(ops, (unsigned long)time_diff, &buffer->probe_time_delta); return (buffer); new_schedule: /* * Write a new schedule record for this thread */ #ifdef VERYVERBOSE sprintf(tmp_buf, " tnf_trace_alloc: initializing " "new schedule record\n"); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif _tnf_sched_init(sched, curr_time); time_diff = 0; if ((sched_record_p = tnf_schedule_write(ops, sched)) != NULL) { /* use one of the extra alloced words for the forwarding ptr */ /* LINTED - ptr subtraction */ *fwd_p = TNF_REF32_MAKE_RECLAIMABLE( ((sched->record_gen - block->generation) << shift) + (sched_record_p - (tnf_record_p)fwd_p)); /* LINTED - ptr subtraction */ sched_offset = PTR_DIFF(fwd_p, buffer); ASSERT(TAGARG_CHECK(sched_offset)); } else { /* Allocation failed (tracing may have been stopped) */ sched_offset = 0; *fwd_p = TNF_NULL; } goto good_ret; null_ret: /* * reset re-entrancy protector, because tnf_trace_end() will * not be called */ #ifdef VERYVERBOSE sprintf(tmp_buf, "tnf_trace_alloc: null return\n"); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif ops->busy = 0; return (NULL); } /* * tnf_trace_end * the last (usually only) function in the list of probe functions */ void tnf_trace_end(tnf_probe_setup_t *set_p) { #ifdef VERYVERBOSE char tmp_buf[512]; sprintf(tmp_buf, "tnf_trace_end: \n"); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif (set_p->probe_p->commit_func)(set_p); set_p->tpd_p->busy = 0; } /* * tnf_trace_commit * a probe commit function that really commits trace data */ void tnf_trace_commit(tnf_probe_setup_t *set_p) { #ifdef VERYVERBOSE char tmp_buf[512]; sprintf(tmp_buf, "tnf_trace_commit: \n\n"); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif (void) set_p->tpd_p->commit(&(set_p->tpd_p->wcb)); return; } /* * tnf_trace_rollback * a probe commit function that unrolls trace data */ void tnf_trace_rollback(tnf_probe_setup_t *set_p) { #ifdef VERYVERBOSE char tmp_buf[512]; sprintf(tmp_buf, "tnf_trace_rollback: \n\n"); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif (void) set_p->tpd_p->rollback(&(set_p->tpd_p->wcb)); return; } /* * tnf_allocate * exported interface for allocating trace memory */ void * tnf_allocate(tnf_ops_t *ops, size_t size) { void *retval; char tmp_buf[512]; #ifdef _TNF_VERBOSE sprintf(tmp_buf, "tnf_allocate\n"); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif retval = ops->alloc(&(ops->wcb), size, ops->mode); #ifdef _TNF_VERBOSE sprintf(tmp_buf, "tnf_allocate, retval=%p\n", retval); (void) write(2, tmp_buf, strlen(tmp_buf)); #endif return (retval); }