0N/A/*
2362N/A * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * Redistribution and use in source and binary forms, with or without
0N/A * modification, are permitted provided that the following conditions
0N/A * are met:
0N/A *
0N/A * - Redistributions of source code must retain the above copyright
0N/A * notice, this list of conditions and the following disclaimer.
0N/A *
0N/A * - Redistributions in binary form must reproduce the above copyright
0N/A * notice, this list of conditions and the following disclaimer in the
0N/A * documentation and/or other materials provided with the distribution.
0N/A *
2362N/A * - Neither the name of Oracle nor the names of its
0N/A * contributors may be used to endorse or promote products derived
0N/A * from this software without specific prior written permission.
0N/A *
0N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
0N/A * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0N/A * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0N/A * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0N/A * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0N/A * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0N/A * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0N/A * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0N/A * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0N/A * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0N/A * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0N/A */
0N/A
4378N/A/*
4378N/A * This source code is provided to illustrate the usage of a given feature
4378N/A * or technique and has been deliberately simplified. Additional steps
4378N/A * required for a production-quality application, such as security checks,
4378N/A * input validation and proper error handling, might not be present in
4378N/A * this sample code.
4378N/A */
4378N/A
4378N/A
0N/A#include "hprof.h"
0N/A
0N/A/* Thread Local Storage Table and method entry/exit handling. */
0N/A
0N/A/*
0N/A * The tls table items have a key of it's serial number, but may be
0N/A * searched via a walk of the table looking for a jthread match.
0N/A * This isn't a performance
0N/A * issue because the table index should normally be stored in the
0N/A * Thread Local Storage for the thread. The table is only searched
0N/A * when the jthread is seen before the Thread Local Storage is set
0N/A * (e.g. before VM_INIT or the ThreadStart).
0N/A * The key is only used when we need to lookup a tls table entry by
0N/A * way of it's serial number, which should be unique per thread.
0N/A *
0N/A * Each active thread that we have seen should have a unique TlsIndex
0N/A * which is an index into this table.
0N/A *
0N/A * For cpu=times, each table entry will have a stack to hold the method
0N/A * that have been called, effectively keeping an active stack trace
0N/A * for the thread. As each method exits, the statistics for the trace
0N/A * associated with the current stack contents is updated.
0N/A *
0N/A * For cpu=samples, each thread is checked to see if it's runnable,
0N/A * and not suspended, and has a stack associated with it, and then
0N/A * that stack trace is updated with an additional 'hit'.
0N/A *
0N/A * This file also contains the dump logic for owned monitors, and for
0N/A * threads.
0N/A *
0N/A */
0N/A
0N/A/*
0N/A * Initial number of stack elements to track per thread. This
0N/A * value should be set to a reasonable guess as to the number of
0N/A * methods deep a thread calls. This stack doubles in size for each
0N/A * reallocation and does not shrink.
0N/A */
0N/A
0N/A#define INITIAL_THREAD_STACK_LIMIT 64
0N/A
0N/Atypedef struct StackElement {
0N/A FrameIndex frame_index; /* Frame (method/location(-1)) */
0N/A jmethodID method; /* Method ID */
0N/A jlong method_start_time; /* method start time */
0N/A jlong time_in_callees; /* time in callees */
0N/A} StackElement;
0N/A
0N/Atypedef struct TlsInfo {
0N/A jint sample_status; /* Thread status for cpu sampling */
0N/A jboolean agent_thread; /* Is thread our own agent thread? */
0N/A jthread globalref; /* Global reference for thread */
0N/A Stack *stack; /* Stack of StackElements entry/exit */
0N/A MonitorIndex monitor_index; /* last contended mon */
0N/A jint tracker_status; /* If we are inside Tracker class */
0N/A FrameIndex *frames_buffer; /* Buffer used to create TraceIndex */
0N/A jvmtiFrameInfo *jframes_buffer; /* Buffer used to create TraceIndex */
0N/A int buffer_depth; /* Frames allowed in buffer */
0N/A TraceIndex last_trace; /* Last trace for this thread */
0N/A ObjectIndex thread_object_index;/* If heap=dump */
0N/A jlong monitor_start_time; /* Start time for monitor */
0N/A jint in_heap_dump; /* If we are an object in the dump */
0N/A} TlsInfo;
0N/A
0N/Atypedef struct SearchData {
0N/A JNIEnv *env;
0N/A jthread thread;
0N/A TlsIndex found;
0N/A} SearchData;
0N/A
0N/Atypedef struct IterateInfo {
0N/A TlsIndex * ptls_index;
0N/A jthread * pthreads;
0N/A jint count;
0N/A} IterateInfo;
0N/A
0N/Atypedef struct ThreadList {
0N/A jthread *threads;
0N/A SerialNumber *serial_nums;
0N/A TlsInfo **infos;
0N/A jint count;
0N/A JNIEnv *env;
0N/A} ThreadList;
0N/A
0N/Atypedef struct SampleData {
0N/A ObjectIndex thread_object_index;
0N/A jint sample_status;
0N/A} SampleData;
0N/A
0N/A/* Private internal functions. */
0N/A
0N/Astatic SerialNumber
0N/Aget_key(TlsIndex index)
0N/A{
0N/A SerialNumber *pkey;
0N/A int key_len;
0N/A
0N/A if ( index == 0 ) {
0N/A return 0;
0N/A }
0N/A pkey = NULL;
0N/A key_len = 0;
0N/A table_get_key(gdata->tls_table, index, (void**)&pkey, &key_len);
0N/A HPROF_ASSERT(pkey!=NULL);
0N/A HPROF_ASSERT(key_len==(int)sizeof(SerialNumber));
0N/A return *pkey;
0N/A}
0N/A
0N/Astatic TlsInfo *
0N/Aget_info(TlsIndex index)
0N/A{
0N/A return (TlsInfo*)table_get_info(gdata->tls_table, index);
0N/A}
0N/A
0N/Astatic void
0N/Adelete_globalref(JNIEnv *env, TlsInfo *info)
0N/A{
0N/A jthread ref;
0N/A
0N/A HPROF_ASSERT(env!=NULL);
0N/A HPROF_ASSERT(info!=NULL);
0N/A ref = info->globalref;
0N/A info->globalref = NULL;
0N/A if ( ref != NULL ) {
0N/A deleteWeakGlobalReference(env, ref);
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/Aclean_info(TlsInfo *info)
0N/A{
0N/A /* Free up any allocated space in this TlsInfo structure */
0N/A if ( info->stack != NULL ) {
0N/A stack_term(info->stack);
0N/A info->stack = NULL;
0N/A }
0N/A if ( info->frames_buffer != NULL ) {
0N/A HPROF_FREE(info->frames_buffer);
0N/A info->frames_buffer = NULL;
0N/A }
0N/A if ( info->jframes_buffer != NULL ) {
0N/A HPROF_FREE(info->jframes_buffer);
0N/A info->jframes_buffer = NULL;
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/Acleanup_item(TableIndex index, void *key_ptr, int key_len,
0N/A void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo * info;
0N/A
0N/A info = (TlsInfo*)info_ptr;
0N/A clean_info(info);
0N/A}
0N/A
0N/Astatic void
0N/Adelete_ref_item(TableIndex index, void *key_ptr, int key_len,
0N/A void *info_ptr, void *arg)
0N/A{
0N/A delete_globalref((JNIEnv*)arg, (TlsInfo*)info_ptr);
0N/A}
0N/A
0N/Astatic void
0N/Alist_item(TableIndex index, void *key_ptr, int key_len,
0N/A void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A
0N/A info = (TlsInfo*)info_ptr;
0N/A debug_message( "Tls 0x%08x: SN=%u, sample_status=%d, agent=%d, "
0N/A "thread=%p, monitor=0x%08x, "
0N/A "tracker_status=%d\n",
0N/A index,
0N/A *(SerialNumber*)key_ptr,
0N/A info->sample_status,
0N/A info->agent_thread,
0N/A (void*)info->globalref,
0N/A info->monitor_index,
0N/A info->tracker_status);
0N/A}
0N/A
0N/Astatic void
0N/Asearch_item(TableIndex index, void *key_ptr, int key_len,
0N/A void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo *info;
0N/A SearchData *data;
0N/A jobject lref;
0N/A
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A HPROF_ASSERT(arg!=NULL);
0N/A info = (TlsInfo*)info_ptr;
0N/A data = (SearchData*)arg;
0N/A lref = newLocalReference(data->env, info->globalref);
0N/A if ( lref != NULL ) {
0N/A if ( isSameObject(data->env, data->thread, lref) ) {
0N/A HPROF_ASSERT(data->found==0); /* Did we find more than one? */
0N/A data->found = index;
0N/A }
0N/A deleteLocalReference(data->env, lref);
0N/A }
0N/A}
0N/A
0N/Astatic TlsIndex
0N/Asearch(JNIEnv *env, jthread thread)
0N/A{
0N/A SearchData data;
0N/A
0N/A HPROF_ASSERT(env!=NULL);
0N/A HPROF_ASSERT(thread!=NULL);
0N/A
0N/A data.env = env;
0N/A data.thread = thread;
0N/A data.found = 0;
0N/A table_walk_items(gdata->tls_table, &search_item, (void*)&data);
0N/A return data.found;
0N/A}
0N/A
0N/Astatic void
0N/Agarbage_collect_item(TableIndex index, void *key_ptr, int key_len,
0N/A void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo *info;
0N/A JNIEnv *env;
0N/A jobject lref;
0N/A
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A HPROF_ASSERT(arg!=NULL);
0N/A info = (TlsInfo*)info_ptr;
0N/A env = (JNIEnv*)arg;
0N/A lref = newLocalReference(env, info->globalref);
0N/A if ( lref == NULL ) {
0N/A delete_globalref(env, info);
0N/A clean_info(info);
0N/A table_free_entry(gdata->tls_table, index);
0N/A } else {
0N/A deleteLocalReference(env, lref);
0N/A }
0N/A}
0N/A
0N/Avoid
0N/Atls_garbage_collect(JNIEnv *env)
0N/A{
0N/A HPROF_ASSERT(env!=NULL);
0N/A rawMonitorEnter(gdata->data_access_lock); {
0N/A table_walk_items(gdata->tls_table, &garbage_collect_item, (void*)env);
0N/A } rawMonitorExit(gdata->data_access_lock);
0N/A}
0N/A
0N/Astatic void
0N/Asum_sample_status_item(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A info = (TlsInfo*)info_ptr;
0N/A if ( !info->agent_thread ) {
0N/A (*(jint*)arg) += info->sample_status;
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/Asetup_trace_buffers(TlsInfo *info, int max_depth)
0N/A{
0N/A int nbytes;
0N/A int max_frames;
0N/A
0N/A if ( info->frames_buffer != NULL && info->buffer_depth >= max_depth ) {
0N/A return;
0N/A }
0N/A if ( info->frames_buffer != NULL ) {
0N/A HPROF_FREE(info->frames_buffer);
0N/A }
0N/A if ( info->jframes_buffer != NULL ) {
0N/A HPROF_FREE(info->jframes_buffer);
0N/A }
0N/A info->buffer_depth = max_depth;
0N/A max_frames = max_depth + 4; /* Allow for BCI & <init> */
0N/A nbytes = (int)sizeof(FrameIndex)*(max_frames+1);
0N/A info->frames_buffer = HPROF_MALLOC(nbytes);
0N/A nbytes = (int)sizeof(jvmtiFrameInfo)*(max_frames+1);
0N/A info->jframes_buffer = HPROF_MALLOC(nbytes);
0N/A}
0N/A
0N/Astatic TraceIndex
0N/Aget_trace(jthread thread, SerialNumber thread_serial_num,
0N/A int depth, jboolean skip_init,
0N/A FrameIndex *frames_buffer, jvmtiFrameInfo *jframes_buffer)
0N/A{
0N/A TraceIndex trace_index;
0N/A
0N/A trace_index = gdata->system_trace_index;
0N/A if ( thread != NULL ) {
0N/A trace_index = trace_get_current(thread,
0N/A thread_serial_num, depth, skip_init,
0N/A frames_buffer, jframes_buffer);
0N/A }
0N/A return trace_index;
0N/A}
0N/A
0N/A/* Find thread with certain object index */
0N/Astatic void
0N/Asample_setter(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A
0N/A info = (TlsInfo*)info_ptr;
0N/A if ( info->globalref != NULL && !info->agent_thread ) {
0N/A SampleData *data;
0N/A
0N/A data = (SampleData*)arg;
0N/A if ( data->thread_object_index == info->thread_object_index ) {
0N/A info->sample_status = data->sample_status;
0N/A }
0N/A }
0N/A}
0N/A
0N/A/* Get various lists on known threads */
0N/Astatic void
0N/Aget_thread_list(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
0N/A{
0N/A SerialNumber thread_serial_num;
0N/A TlsInfo *info;
0N/A ThreadList *list;
0N/A jthread thread;
0N/A
0N/A HPROF_ASSERT(key_ptr!=NULL);
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A
0N/A thread_serial_num = *(SerialNumber*)key_ptr;
0N/A info = (TlsInfo*)info_ptr;
0N/A list = (ThreadList*)arg;
0N/A thread = newLocalReference(list->env, info->globalref);
0N/A if ( thread != NULL && info->sample_status != 0 && !info->agent_thread ) {
0N/A if ( list->infos != NULL ) {
0N/A list->infos[list->count] = info;
0N/A }
0N/A if ( list->serial_nums != NULL ) {
0N/A list->serial_nums[list->count] = thread_serial_num;
0N/A }
0N/A list->threads[list->count] = thread;
0N/A list->count++;
0N/A /* Local reference gets freed by caller */
0N/A } else {
0N/A /* If we don't use the local reference, delete it now */
0N/A if ( thread != NULL ) {
0N/A deleteLocalReference(list->env, thread);
0N/A }
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/Aadjust_stats(jlong total_time, jlong self_time, TraceIndex trace_index,
0N/A StackElement *parent)
0N/A{
0N/A if ( total_time > 0 && parent != NULL ) { /* if a caller exists */
0N/A parent->time_in_callees += total_time;
0N/A }
0N/A trace_increment_cost(trace_index, 1, self_time, total_time);
0N/A}
0N/A
0N/Astatic void
0N/Apush_method(Stack *stack, jlong method_start_time, jmethodID method)
0N/A{
0N/A StackElement new_element;
0N/A FrameIndex frame_index;
0N/A
0N/A HPROF_ASSERT(method!=NULL);
0N/A HPROF_ASSERT(stack!=NULL);
0N/A
0N/A frame_index = frame_find_or_create(method, -1);
0N/A HPROF_ASSERT(frame_index != 0);
0N/A new_element.frame_index = frame_index;
0N/A new_element.method = method;
0N/A new_element.method_start_time= method_start_time;
0N/A new_element.time_in_callees = (jlong)0;
0N/A stack_push(stack, &new_element);
0N/A}
0N/A
0N/Astatic Stack *
0N/Ainsure_method_on_stack(jthread thread, TlsInfo *info, jlong current_time,
0N/A FrameIndex frame_index, jmethodID method)
0N/A{
0N/A StackElement element;
0N/A void *p;
0N/A int depth;
0N/A int count;
0N/A int fcount;
0N/A int i;
0N/A Stack *new_stack;
0N/A Stack *stack;
0N/A
0N/A stack = info->stack;
0N/A
0N/A HPROF_ASSERT(method!=NULL);
0N/A
0N/A /* If this method is on the stack, just return */
0N/A depth = stack_depth(stack);
0N/A p = stack_top(stack);
0N/A if ( p != NULL ) {
0N/A element = *(StackElement*)p;
0N/A if ( element.frame_index == frame_index ) {
0N/A return stack;
0N/A }
0N/A }
0N/A for ( i = 0 ; i < depth ; i++ ) {
0N/A p = stack_element(stack, i);
0N/A element = *(StackElement*)p;
0N/A if ( element.frame_index == frame_index ) {
0N/A return stack;
0N/A }
0N/A }
0N/A
0N/A /* It wasn't found, create a new stack */
0N/A getFrameCount(thread, &count);
0N/A if ( count <= 0 ) {
0N/A HPROF_ERROR(JNI_FALSE, "no frames, method can't be on stack");
0N/A }
0N/A setup_trace_buffers(info, count);
0N/A getStackTrace(thread, info->jframes_buffer, count, &fcount);
0N/A HPROF_ASSERT(count==fcount);
0N/A
0N/A /* Create a new stack */
0N/A new_stack = stack_init(INITIAL_THREAD_STACK_LIMIT,
0N/A INITIAL_THREAD_STACK_LIMIT,
0N/A (int)sizeof(StackElement));
0N/A for ( i = count-1; i >= 0 ; i-- ) {
0N/A push_method(new_stack, current_time, info->jframes_buffer[i].method);
0N/A }
0N/A if ( depth > 0 ) {
0N/A for ( i = depth-1 ; i >= 0; i-- ) {
0N/A stack_push(new_stack, stack_element(stack, i));
0N/A }
0N/A }
0N/A stack_term(stack);
0N/A return new_stack;
0N/A}
0N/A
0N/Astatic void
0N/Apop_method(TlsIndex index, jlong current_time, jmethodID method, FrameIndex frame_index)
0N/A{
0N/A SerialNumber thread_serial_num;
0N/A TlsInfo * info;
0N/A StackElement element;
0N/A void *p;
0N/A int depth;
0N/A int trace_depth;
0N/A jlong total_time;
0N/A jlong self_time;
0N/A int i;
0N/A TraceIndex trace_index;
0N/A
0N/A HPROF_ASSERT(method!=NULL);
0N/A HPROF_ASSERT(frame_index!=0);
0N/A
0N/A thread_serial_num = get_key(index);
0N/A info = get_info(index);
0N/A HPROF_ASSERT(info!=NULL);
0N/A HPROF_ASSERT(info->stack!=NULL);
0N/A depth = stack_depth(info->stack);
0N/A p = stack_pop(info->stack);
0N/A if (p == NULL) {
0N/A HPROF_ERROR(JNI_FALSE, "method return tracked, but stack is empty");
0N/A return;
0N/A }
0N/A element = *(StackElement*)p;
0N/A HPROF_ASSERT(element.frame_index!=0);
0N/A
0N/A /* The depth of frames we should keep track for reporting */
0N/A if (gdata->prof_trace_depth > depth) {
0N/A trace_depth = depth;
0N/A } else {
0N/A trace_depth = gdata->prof_trace_depth;
0N/A }
0N/A
0N/A /* Create a trace entry */
0N/A HPROF_ASSERT(info->frames_buffer!=NULL);
0N/A HPROF_ASSERT(info->jframes_buffer!=NULL);
0N/A setup_trace_buffers(info, trace_depth);
0N/A info->frames_buffer[0] = element.frame_index;
0N/A for (i = 1; i < trace_depth; i++) {
0N/A StackElement e;
0N/A
0N/A e = *(StackElement*)stack_element(info->stack, (depth - i) - 1);
0N/A info->frames_buffer[i] = e.frame_index;
0N/A HPROF_ASSERT(e.frame_index!=0);
0N/A }
0N/A trace_index = trace_find_or_create(thread_serial_num,
0N/A trace_depth, info->frames_buffer, info->jframes_buffer);
0N/A
0N/A /* Calculate time spent */
0N/A total_time = current_time - element.method_start_time;
0N/A if ( total_time < 0 ) {
0N/A total_time = 0;
0N/A self_time = 0;
0N/A } else {
0N/A self_time = total_time - element.time_in_callees;
0N/A }
0N/A
0N/A /* Update stats */
0N/A p = stack_top(info->stack);
0N/A if ( p != NULL ) {
0N/A adjust_stats(total_time, self_time, trace_index, (StackElement*)p);
0N/A } else {
0N/A adjust_stats(total_time, self_time, trace_index, NULL);
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/Adump_thread_state(TlsIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
0N/A{
0N/A SerialNumber thread_serial_num;
0N/A TlsInfo *info;
0N/A jthread thread;
0N/A JNIEnv *env;
0N/A
0N/A HPROF_ASSERT(key_ptr!=NULL);
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A env = (JNIEnv*)arg;
0N/A thread_serial_num = *(SerialNumber*)key_ptr;
0N/A info = (TlsInfo*)info_ptr;
0N/A thread = newLocalReference(env, info->globalref);
0N/A if ( thread != NULL ) {
0N/A jint threadState;
0N/A SerialNumber trace_serial_num;
0N/A
0N/A getThreadState(thread, &threadState);
0N/A /* A 0 trace at this time means the thread is in unknown territory.
0N/A * The trace serial number MUST be a valid serial number, so we use
0N/A * the system trace (empty) just so it has a valid trace.
0N/A */
0N/A if ( info->last_trace == 0 ) {
0N/A trace_serial_num = trace_get_serial_number(gdata->system_trace_index);
0N/A } else {
0N/A trace_serial_num = trace_get_serial_number(info->last_trace);
0N/A }
0N/A io_write_monitor_dump_thread_state(thread_serial_num,
0N/A trace_serial_num, threadState);
0N/A deleteLocalReference(env, thread);
0N/A }
0N/A}
0N/A
0N/Astatic SerialNumber
0N/Aget_serial_number(JNIEnv *env, jthread thread)
0N/A{
0N/A TlsIndex index;
0N/A
0N/A if ( thread == NULL ) {
0N/A return gdata->unknown_thread_serial_num;
0N/A }
0N/A HPROF_ASSERT(env!=NULL);
0N/A index = tls_find_or_create(env, thread);
0N/A return get_key(index);
0N/A}
0N/A
0N/Astatic void
0N/Adump_monitor_state(TlsIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo *info;
0N/A jthread thread;
0N/A JNIEnv *env;
0N/A
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A env = (JNIEnv*)arg;
0N/A info = (TlsInfo*)info_ptr;
0N/A thread = newLocalReference(env, info->globalref);
0N/A if ( thread != NULL ) {
0N/A jobject *objects;
0N/A jint ocount;
0N/A int i;
0N/A
0N/A getOwnedMonitorInfo(thread, &objects, &ocount);
0N/A if ( ocount > 0 ) {
0N/A for ( i = 0 ; i < ocount ; i++ ) {
0N/A jvmtiMonitorUsage usage;
0N/A SerialNumber *waiter_nums;
0N/A SerialNumber *notify_waiter_nums;
0N/A int t;
0N/A char * sig;
0N/A
0N/A WITH_LOCAL_REFS(env, 1) {
0N/A jclass clazz;
0N/A
0N/A clazz = getObjectClass(env, objects[i]);
0N/A getClassSignature(clazz, &sig, NULL);
0N/A } END_WITH_LOCAL_REFS;
0N/A
0N/A getObjectMonitorUsage(objects[i], &usage);
0N/A waiter_nums = HPROF_MALLOC(usage.waiter_count*
0N/A (int)sizeof(SerialNumber)+1);
0N/A for ( t = 0 ; t < usage.waiter_count ; t++ ) {
0N/A waiter_nums[t] =
0N/A get_serial_number(env, usage.waiters[t]);
0N/A }
0N/A notify_waiter_nums = HPROF_MALLOC(usage.notify_waiter_count*
0N/A (int)sizeof(SerialNumber)+1);
0N/A for ( t = 0 ; t < usage.notify_waiter_count ; t++ ) {
0N/A notify_waiter_nums[t] =
0N/A get_serial_number(env, usage.notify_waiters[t]);
0N/A }
0N/A io_write_monitor_dump_state(sig,
0N/A get_serial_number(env, usage.owner),
0N/A usage.entry_count,
0N/A waiter_nums, usage.waiter_count,
0N/A notify_waiter_nums, usage.notify_waiter_count);
0N/A jvmtiDeallocate(sig);
0N/A jvmtiDeallocate(usage.waiters);
0N/A jvmtiDeallocate(usage.notify_waiters);
0N/A HPROF_FREE(waiter_nums);
0N/A HPROF_FREE(notify_waiter_nums);
0N/A }
0N/A }
0N/A jvmtiDeallocate(objects);
0N/A deleteLocalReference(env, thread);
0N/A }
0N/A}
0N/A
0N/Astatic jlong
0N/Amonitor_time(void)
0N/A{
0N/A jlong mtime;
0N/A
0N/A mtime = md_get_timemillis(); /* gettimeofday() */
0N/A return mtime;
0N/A}
0N/A
0N/Astatic jlong
0N/Amethod_time(void)
0N/A{
0N/A jlong method_time;
0N/A
0N/A method_time = md_get_thread_cpu_timemillis(); /* thread CPU time */
0N/A return method_time;
0N/A}
0N/A
0N/A/* External interfaces */
0N/A
0N/ATlsIndex
0N/Atls_find_or_create(JNIEnv *env, jthread thread)
0N/A{
0N/A SerialNumber thread_serial_num;
0N/A static TlsInfo empty_info;
0N/A TlsInfo info;
0N/A TlsIndex index;
0N/A
0N/A HPROF_ASSERT(env!=NULL);
0N/A HPROF_ASSERT(thread!=NULL);
0N/A
0N/A /*LINTED*/
0N/A index = (TlsIndex)(ptrdiff_t)getThreadLocalStorage(thread);
0N/A if ( index != 0 ) {
0N/A HPROF_ASSERT(isSameObject(env, thread, get_info(index)->globalref));
0N/A return index;
0N/A }
0N/A index = search(env, thread);
0N/A if ( index != 0 ) {
0N/A setThreadLocalStorage(thread, (void*)(ptrdiff_t)index);
0N/A return index;
0N/A }
0N/A thread_serial_num = gdata->thread_serial_number_counter++;
0N/A info = empty_info;
0N/A info.monitor_index = 0;
0N/A info.sample_status = 1;
0N/A info.agent_thread = JNI_FALSE;
0N/A info.stack = stack_init(INITIAL_THREAD_STACK_LIMIT,
0N/A INITIAL_THREAD_STACK_LIMIT,
0N/A (int)sizeof(StackElement));
0N/A setup_trace_buffers(&info, gdata->max_trace_depth);
0N/A info.globalref = newWeakGlobalReference(env, thread);
0N/A index = table_create_entry(gdata->tls_table, &thread_serial_num, (int)sizeof(SerialNumber), (void*)&info);
0N/A setThreadLocalStorage(thread, (void*)(ptrdiff_t)index);
0N/A HPROF_ASSERT(search(env,thread)==index);
0N/A return index;
0N/A}
0N/A
0N/A/* Mark a new or existing entry as being an agent thread */
0N/Avoid
0N/Atls_agent_thread(JNIEnv *env, jthread thread)
0N/A{
0N/A TlsIndex index;
0N/A TlsInfo *info;
0N/A
0N/A index = tls_find_or_create(env, thread);
0N/A info = get_info(index);
0N/A info->agent_thread = JNI_TRUE;
0N/A}
0N/A
0N/Avoid
0N/Atls_init(void)
0N/A{
0N/A gdata->tls_table = table_initialize("TLS",
0N/A 16, 16, 16, (int)sizeof(TlsInfo));
0N/A}
0N/A
0N/Avoid
0N/Atls_list(void)
0N/A{
0N/A debug_message(
0N/A "--------------------- TLS Table ------------------------\n");
0N/A table_walk_items(gdata->tls_table, &list_item, NULL);
0N/A debug_message(
0N/A "----------------------------------------------------------\n");
0N/A}
0N/A
0N/Ajint
0N/Atls_sum_sample_status(void)
0N/A{
0N/A jint sample_status_total;
0N/A
0N/A sample_status_total = 0;
0N/A table_walk_items(gdata->tls_table, &sum_sample_status_item, (void*)&sample_status_total);
0N/A return sample_status_total;
0N/A}
0N/A
0N/Avoid
0N/Atls_set_sample_status(ObjectIndex object_index, jint sample_status)
0N/A{
0N/A SampleData data;
0N/A
0N/A data.thread_object_index = object_index;
0N/A data.sample_status = sample_status;
0N/A table_walk_items(gdata->tls_table, &sample_setter, (void*)&data);
0N/A}
0N/A
0N/Ajint
0N/Atls_get_tracker_status(JNIEnv *env, jthread thread, jboolean skip_init,
0N/A jint **ppstatus, TlsIndex* pindex,
0N/A SerialNumber *pthread_serial_num, TraceIndex *ptrace_index)
0N/A{
0N/A TlsInfo *info;
0N/A TlsIndex index;
0N/A SerialNumber thread_serial_num;
0N/A jint status;
0N/A
0N/A index = tls_find_or_create(env, thread);
0N/A info = get_info(index);
0N/A *ppstatus = &(info->tracker_status);
0N/A status = **ppstatus;
0N/A thread_serial_num = get_key(index);
0N/A
0N/A if ( pindex != NULL ) {
0N/A *pindex = index;
0N/A }
0N/A if ( status != 0 ) {
0N/A return status;
0N/A }
0N/A if ( ptrace_index != NULL ) {
0N/A setup_trace_buffers(info, gdata->max_trace_depth);
0N/A *ptrace_index = get_trace(thread, thread_serial_num,
0N/A gdata->max_trace_depth, skip_init,
0N/A info->frames_buffer, info->jframes_buffer);
0N/A }
0N/A if ( pthread_serial_num != NULL ) {
0N/A *pthread_serial_num = thread_serial_num;
0N/A }
0N/A return status;
0N/A}
0N/A
0N/AMonitorIndex
0N/Atls_get_monitor(TlsIndex index)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A info = get_info(index);
0N/A return info->monitor_index;
0N/A}
0N/A
0N/Avoid
0N/Atls_set_thread_object_index(TlsIndex index, ObjectIndex thread_object_index)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A info = get_info(index);
0N/A info->thread_object_index = thread_object_index;
0N/A}
0N/A
0N/ASerialNumber
0N/Atls_get_thread_serial_number(TlsIndex index)
0N/A{
0N/A return get_key(index);
0N/A}
0N/A
0N/Avoid
0N/Atls_set_monitor(TlsIndex index, MonitorIndex monitor_index)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A info = get_info(index);
0N/A info->monitor_index = monitor_index;
0N/A}
0N/A
0N/Avoid
0N/Atls_cleanup(void)
0N/A{
0N/A table_cleanup(gdata->tls_table, &cleanup_item, NULL);
0N/A gdata->tls_table = NULL;
0N/A}
0N/A
0N/Avoid
0N/Atls_delete_global_references(JNIEnv *env)
0N/A{
0N/A table_walk_items(gdata->tls_table, &delete_ref_item, (void*)env);
0N/A}
0N/A
0N/Avoid
0N/Atls_thread_ended(JNIEnv *env, TlsIndex index)
0N/A{
0N/A HPROF_ASSERT(env!=NULL);
0N/A
0N/A /* Sample thread stack for last time, do NOT free the entry yet. */
0N/A table_lock_enter(gdata->tls_table); {
0N/A SerialNumber thread_serial_num;
0N/A TlsInfo *info;
0N/A jthread thread;
0N/A
0N/A thread_serial_num = get_key(index);
0N/A info = get_info(index);
0N/A thread = newLocalReference(env, info->globalref);
0N/A if (gdata->heap_dump && thread!=NULL) {
0N/A setup_trace_buffers(info, gdata->max_trace_depth);
0N/A info->last_trace = get_trace(thread, thread_serial_num,
0N/A gdata->max_trace_depth, JNI_FALSE,
0N/A info->frames_buffer, info->jframes_buffer);
0N/A }
0N/A if ( thread != NULL ) {
0N/A deleteLocalReference(env, thread);
0N/A }
0N/A } table_lock_exit(gdata->tls_table);
0N/A
0N/A}
0N/A
0N/A/* Sample ALL threads and update the trace costs */
0N/Avoid
0N/Atls_sample_all_threads(JNIEnv *env)
0N/A{
0N/A ThreadList list;
0N/A jthread *threads;
0N/A SerialNumber *serial_nums;
0N/A
0N/A table_lock_enter(gdata->tls_table); {
0N/A int max_count;
0N/A int nbytes;
0N/A int i;
0N/A
0N/A /* Get buffers to hold thread list and serial number list */
0N/A max_count = table_element_count(gdata->tls_table);
0N/A nbytes = (int)sizeof(jthread)*max_count;
0N/A threads = (jthread*)HPROF_MALLOC(nbytes);
0N/A nbytes = (int)sizeof(SerialNumber)*max_count;
0N/A serial_nums = (SerialNumber*)HPROF_MALLOC(nbytes);
0N/A
0N/A /* Get list of threads and serial numbers */
0N/A list.threads = threads;
0N/A list.infos = NULL;
0N/A list.serial_nums = serial_nums;
0N/A list.count = 0;
0N/A list.env = env;
0N/A table_walk_items(gdata->tls_table, &get_thread_list, (void*)&list);
0N/A
0N/A /* Increment the cost on the traces for these threads */
0N/A trace_increment_all_sample_costs(list.count, threads, serial_nums,
0N/A gdata->max_trace_depth, JNI_FALSE);
0N/A
0N/A /* Loop over local refs and free them */
0N/A for ( i = 0 ; i < list.count ; i++ ) {
0N/A if ( threads[i] != NULL ) {
0N/A deleteLocalReference(env, threads[i]);
0N/A }
0N/A }
0N/A
0N/A } table_lock_exit(gdata->tls_table);
0N/A
0N/A /* Free up allocated space */
0N/A HPROF_FREE(threads);
0N/A HPROF_FREE(serial_nums);
0N/A
0N/A}
0N/A
0N/Avoid
0N/Atls_push_method(TlsIndex index, jmethodID method)
0N/A{
0N/A jlong method_start_time;
0N/A TlsInfo *info;
0N/A
0N/A HPROF_ASSERT(method!=NULL);
0N/A info = get_info(index);
0N/A HPROF_ASSERT(info!=NULL);
0N/A method_start_time = method_time();
0N/A HPROF_ASSERT(info->stack!=NULL);
0N/A push_method(info->stack, method_start_time, method);
0N/A}
0N/A
0N/Avoid
0N/Atls_pop_exception_catch(TlsIndex index, jthread thread, jmethodID method)
0N/A{
0N/A TlsInfo *info;
0N/A StackElement element;
0N/A void *p;
0N/A FrameIndex frame_index;
0N/A jlong current_time;
0N/A
0N/A HPROF_ASSERT(method!=NULL);
0N/A frame_index = frame_find_or_create(method, -1);
0N/A HPROF_ASSERT(frame_index != 0);
0N/A
0N/A info = get_info(index);
0N/A
0N/A HPROF_ASSERT(info!=NULL);
0N/A HPROF_ASSERT(info->stack!=NULL);
0N/A HPROF_ASSERT(frame_index!=0);
0N/A current_time = method_time();
0N/A info->stack = insure_method_on_stack(thread, info, current_time,
0N/A frame_index, method);
0N/A p = stack_top(info->stack);
0N/A if (p == NULL) {
0N/A HPROF_ERROR(JNI_FALSE, "expection pop, nothing on stack");
0N/A return;
0N/A }
0N/A element = *(StackElement*)p;
0N/A HPROF_ASSERT(element.frame_index!=0);
0N/A while ( element.frame_index != frame_index ) {
0N/A pop_method(index, current_time, element.method, frame_index);
0N/A p = stack_top(info->stack);
0N/A if ( p == NULL ) {
0N/A break;
0N/A }
0N/A element = *(StackElement*)p;
0N/A }
0N/A if (p == NULL) {
0N/A HPROF_ERROR(JNI_FALSE, "exception pop stack empty");
0N/A }
0N/A}
0N/A
0N/Avoid
0N/Atls_pop_method(TlsIndex index, jthread thread, jmethodID method)
0N/A{
0N/A TlsInfo *info;
0N/A StackElement element;
0N/A void *p;
0N/A FrameIndex frame_index;
0N/A jlong current_time;
0N/A
0N/A HPROF_ASSERT(method!=NULL);
0N/A frame_index = frame_find_or_create(method, -1);
0N/A HPROF_ASSERT(frame_index != 0);
0N/A
0N/A info = get_info(index);
0N/A HPROF_ASSERT(info!=NULL);
0N/A HPROF_ASSERT(info->stack!=NULL);
0N/A current_time = method_time();
0N/A HPROF_ASSERT(frame_index!=0);
0N/A info->stack = insure_method_on_stack(thread, info, current_time,
0N/A frame_index, method);
0N/A p = stack_top(info->stack);
0N/A HPROF_ASSERT(p!=NULL);
0N/A element = *(StackElement*)p;
0N/A while ( element.frame_index != frame_index ) {
0N/A pop_method(index, current_time, element.method, frame_index);
0N/A p = stack_top(info->stack);
0N/A if ( p == NULL ) {
0N/A break;
0N/A }
0N/A element = *(StackElement*)p;
0N/A }
0N/A pop_method(index, current_time, method, frame_index);
0N/A}
0N/A
0N/A/* For all TLS entries, update the last_trace on all threads */
0N/Astatic void
0N/Aupdate_all_last_traces(JNIEnv *env)
0N/A{
0N/A jthread *threads;
0N/A TlsInfo **infos;
0N/A SerialNumber *serial_nums;
0N/A TraceIndex *traces;
0N/A
0N/A if ( gdata->max_trace_depth == 0 ) {
0N/A return;
0N/A }
0N/A
0N/A table_lock_enter(gdata->tls_table); {
0N/A
0N/A ThreadList list;
0N/A int max_count;
0N/A int nbytes;
0N/A int i;
0N/A
0N/A /* Get buffers to hold thread list and serial number list */
0N/A max_count = table_element_count(gdata->tls_table);
0N/A nbytes = (int)sizeof(jthread)*max_count;
0N/A threads = (jthread*)HPROF_MALLOC(nbytes);
0N/A nbytes = (int)sizeof(SerialNumber)*max_count;
0N/A serial_nums = (SerialNumber*)HPROF_MALLOC(nbytes);
0N/A nbytes = (int)sizeof(TlsInfo*)*max_count;
0N/A infos = (TlsInfo**)HPROF_MALLOC(nbytes);
0N/A
0N/A /* Get list of threads, serial numbers, and info pointers */
0N/A list.threads = threads;
0N/A list.serial_nums = serial_nums;
0N/A list.infos = infos;
0N/A list.count = 0;
0N/A list.env = env;
0N/A table_walk_items(gdata->tls_table, &get_thread_list, (void*)&list);
0N/A
0N/A /* Get all stack trace index's for all these threadss */
0N/A nbytes = (int)sizeof(TraceIndex)*max_count;
0N/A traces = (TraceIndex*)HPROF_MALLOC(nbytes);
0N/A trace_get_all_current(list.count, threads, serial_nums,
0N/A gdata->max_trace_depth, JNI_FALSE,
0N/A traces, JNI_TRUE);
0N/A
0N/A /* Loop over traces and update last_trace's */
0N/A for ( i = 0 ; i < list.count ; i++ ) {
0N/A if ( threads[i] != NULL ) {
0N/A deleteLocalReference(env, threads[i]);
0N/A }
0N/A infos[i]->last_trace = traces[i];
0N/A }
0N/A
0N/A } table_lock_exit(gdata->tls_table);
0N/A
0N/A /* Free up all allocated space */
0N/A HPROF_FREE(threads);
0N/A HPROF_FREE(serial_nums);
0N/A HPROF_FREE(infos);
0N/A HPROF_FREE(traces);
0N/A
0N/A}
0N/A
0N/Avoid
0N/Atls_dump_traces(JNIEnv *env)
0N/A{
0N/A rawMonitorEnter(gdata->data_access_lock); {
0N/A update_all_last_traces(env);
0N/A trace_output_unmarked(env);
0N/A } rawMonitorExit(gdata->data_access_lock);
0N/A}
0N/A
0N/Avoid
0N/Atls_dump_monitor_state(JNIEnv *env)
0N/A{
0N/A HPROF_ASSERT(env!=NULL);
0N/A
0N/A rawMonitorEnter(gdata->data_access_lock); {
0N/A tls_dump_traces(env);
0N/A io_write_monitor_dump_header();
0N/A table_walk_items(gdata->tls_table, &dump_thread_state, (void*)env);
0N/A table_walk_items(gdata->tls_table, &dump_monitor_state, (void*)env);
0N/A io_write_monitor_dump_footer();
0N/A } rawMonitorExit(gdata->data_access_lock);
0N/A}
0N/A
0N/Avoid
0N/Atls_monitor_start_timer(TlsIndex index)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A info = get_info(index);
0N/A HPROF_ASSERT(info!=NULL);
0N/A HPROF_ASSERT(info->globalref!=NULL);
0N/A info->monitor_start_time = monitor_time();
0N/A}
0N/A
0N/Ajlong
0N/Atls_monitor_stop_timer(TlsIndex index)
0N/A{
0N/A TlsInfo *info;
0N/A jlong t;
0N/A
0N/A info = get_info(index);
0N/A HPROF_ASSERT(info!=NULL);
0N/A t = monitor_time() - info->monitor_start_time;
0N/A info->monitor_start_time = 0;
0N/A return t;
0N/A}
0N/A
0N/ATraceIndex
0N/Atls_get_trace(TlsIndex index, JNIEnv *env, int depth, jboolean skip_init)
0N/A{
0N/A SerialNumber thread_serial_num;
0N/A TraceIndex trace_index;
0N/A TlsInfo *info;
0N/A jthread thread;
0N/A
0N/A thread_serial_num = get_key(index);
0N/A info = get_info(index);
0N/A HPROF_ASSERT(info!=NULL);
0N/A setup_trace_buffers(info, depth);
0N/A thread = newLocalReference(env, info->globalref);
0N/A if ( thread != NULL ) {
0N/A trace_index = get_trace(thread, thread_serial_num, depth, skip_init,
0N/A info->frames_buffer, info->jframes_buffer);
0N/A deleteLocalReference(env, thread);
0N/A } else {
0N/A trace_index = gdata->system_trace_index;
0N/A }
0N/A return trace_index;
0N/A}
0N/A
0N/Avoid
0N/Atls_set_in_heap_dump(TlsIndex index, jint in_heap_dump)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A info = get_info(index);
0N/A info->in_heap_dump = in_heap_dump;
0N/A}
0N/A
0N/Ajint
0N/Atls_get_in_heap_dump(TlsIndex index)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A info = get_info(index);
0N/A return info->in_heap_dump;
0N/A}
0N/A
0N/Astatic void
0N/Aclean_in_heap_dump(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
0N/A{
0N/A TlsInfo *info;
0N/A
0N/A HPROF_ASSERT(info_ptr!=NULL);
0N/A info = (TlsInfo*)info_ptr;
0N/A info->in_heap_dump = 0;
0N/A}
0N/A
0N/Avoid
0N/Atls_clear_in_heap_dump(void)
0N/A{
0N/A table_walk_items(gdata->tls_table, &clean_in_heap_dump, NULL);
0N/A}
0N/A
0N/ATlsIndex
0N/Atls_find(SerialNumber thread_serial_num)
0N/A{
0N/A TlsIndex index;
0N/A
0N/A if ( thread_serial_num == 0 ) {
0N/A return 0;
0N/A }
0N/A index = table_find_entry(gdata->tls_table,
0N/A (void*)&thread_serial_num, (int)sizeof(SerialNumber));
0N/A return index;
0N/A}