/* * @(#)hprof_site.c 1.43 10/03/23 * * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistribution of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Oracle or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed, licensed or intended * for use in the design, construction, operation or maintenance of any * nuclear facility. */ /* Allocation site table. */ /* * Every object allocation will have a place where it was allocated, * this is the purpose of the SiteIndex. * * The allocation site or SiteIndex is unique via a (class,trace) pair. * * The allocation statistics are accumulated in the SiteInfo for each * site. * * This file also contains the heap iterate logic, which is closely * associated with the site table, the object table, and the * reference table. Each object has an element in the object table * and as the heap is traversed, and information contained in each * object is saved as a linked list of references. * */ #include "hprof.h" typedef struct SiteKey { ClassIndex cnum; /* Unique class number */ TraceIndex trace_index; /* Trace number */ } SiteKey; typedef struct SiteInfo { int changed; /* Objects at this site changed? */ unsigned n_alloced_instances; /* Total allocated instances */ unsigned n_alloced_bytes; /* Total bytes allocated from here */ unsigned n_live_instances; /* Live instances for this site. */ unsigned n_live_bytes; /* Live byte count for this site. */ } SiteInfo; typedef struct IterateInfo { SiteIndex * site_nums; int count; int changed_only; } IterateInfo; /* Private internal functions. */ static SiteKey* get_pkey(SiteIndex index) { void *key_ptr; int key_len; table_get_key(gdata->site_table, index, &key_ptr, &key_len); HPROF_ASSERT(key_len==sizeof(SiteKey)); HPROF_ASSERT(key_ptr!=NULL); return (SiteKey*)key_ptr; } ClassIndex site_get_class_index(SiteIndex index) { SiteKey *pkey; pkey = get_pkey(index); return pkey->cnum; } TraceIndex site_get_trace_index(SiteIndex index) { SiteKey *pkey; pkey = get_pkey(index); return pkey->trace_index; } static SiteInfo * get_info(SiteIndex index) { SiteInfo *info; info = (SiteInfo*)table_get_info(gdata->site_table, index); return info; } static void list_item(TableIndex i, void *key_ptr, int key_len, void *info_ptr, void *arg) { SiteKey *pkey; jlong n_alloced_instances; jlong n_alloced_bytes; jlong n_live_instances; jlong n_live_bytes; HPROF_ASSERT(key_ptr!=NULL); HPROF_ASSERT(key_len==sizeof(SiteKey)); pkey = (SiteKey*)key_ptr; if ( info_ptr != NULL ) { SiteInfo *info; info = (SiteInfo *)info_ptr; n_alloced_instances = info->n_alloced_instances; n_alloced_bytes = info->n_alloced_bytes; n_live_instances = info->n_live_instances; n_live_bytes = info->n_live_bytes; } else { n_alloced_instances = 0; n_alloced_bytes = 0; n_live_instances = 0; n_live_bytes = 0; } debug_message( "Site 0x%08x: class=0x%08x, trace=0x%08x, " "Ninst=(%d,%d), Nbytes=(%d,%d), " "Nlive=(%d,%d), NliveBytes=(%d,%d)\n", i, pkey->cnum, pkey->trace_index, jlong_high(n_alloced_instances), jlong_low(n_alloced_instances), jlong_high(n_alloced_bytes), jlong_low(n_alloced_bytes), jlong_high(n_live_instances), jlong_low(n_live_instances), jlong_high(n_live_bytes), jlong_low(n_live_bytes)); } static void collect_iterator(TableIndex i, void *key_ptr, int key_len, void *info_ptr, void *arg) { IterateInfo *iterate; HPROF_ASSERT(key_ptr!=NULL); HPROF_ASSERT(key_len==sizeof(SiteKey)); HPROF_ASSERT(arg!=NULL); iterate = (IterateInfo *)arg; if ( iterate->changed_only ) { SiteInfo *info; info = (SiteInfo *)info_ptr; if ( info==NULL || !info->changed ) { return; } } iterate->site_nums[iterate->count++] = i; } static void mark_unchanged_iterator(TableIndex i, void *key_ptr, int key_len, void *info_ptr, void *arg) { SiteInfo *info; HPROF_ASSERT(key_ptr!=NULL); HPROF_ASSERT(key_len==sizeof(SiteKey)); info = (SiteInfo *)info_ptr; if ( info != NULL ) { info->changed = 0; } } static int qsort_compare_allocated_bytes(const void *p_site1, const void *p_site2) { SiteIndex site1; SiteIndex site2; SiteInfo *info1; SiteInfo *info2; HPROF_ASSERT(p_site1!=NULL); HPROF_ASSERT(p_site2!=NULL); site1 = *(SiteIndex *)p_site1; site2 = *(SiteIndex *)p_site2; info1 = get_info(site1); info2 = get_info(site2); return info2->n_alloced_bytes - info1->n_alloced_bytes; } static int qsort_compare_live_bytes(const void *p_site1, const void *p_site2) { SiteIndex site1; SiteIndex site2; SiteInfo *info1; SiteInfo *info2; HPROF_ASSERT(p_site1!=NULL); HPROF_ASSERT(p_site2!=NULL); site1 = *(SiteIndex *)p_site1; site2 = *(SiteIndex *)p_site2; info1 = get_info(site1); info2 = get_info(site2); return info2->n_live_bytes - info1->n_live_bytes; } static ClassIndex find_cnum(jlong class_tag) { ClassIndex cnum; ObjectIndex class_object_index; SiteIndex class_site_index; SiteKey *pkey; HPROF_ASSERT(class_tag!=(jlong)0); class_object_index = tag_extract(class_tag); class_site_index = object_get_site(class_object_index); pkey = get_pkey(class_site_index); cnum = pkey->cnum; return cnum; } /* Create tag and object entry for an untagged object (should be rare) */ static jlong make_new_tag(jlong class_tag, jlong size, TraceIndex trace_index, SerialNumber thread_serial_num, ObjectIndex *pindex, SiteIndex *psite) { ObjectIndex object_index; SiteIndex object_site_index; HPROF_ASSERT(class_tag!=(jlong)0); object_site_index = site_find_or_create(find_cnum(class_tag), trace_index); object_index = object_new(object_site_index, (jint)size, OBJECT_SYSTEM, thread_serial_num); if ( pindex != NULL ) { *pindex = object_index; } if ( psite != NULL ) { *psite = object_site_index; } return tag_create(object_index); } /* Setup tag on root object, if tagged return object index and site index */ static void setup_tag_on_root(jlong *tag_ptr, jlong class_tag, jlong size, SerialNumber thread_serial_num, ObjectIndex *pindex, SiteIndex *psite) { HPROF_ASSERT(class_tag!=(jlong)0); if ( (*tag_ptr) != (jlong)0 ) { if ( pindex != NULL ) { *pindex = tag_extract(*tag_ptr); } if ( psite != NULL ) { *psite = object_get_site(tag_extract(*tag_ptr)); } } else { /* Create and set the tag. */ *tag_ptr = make_new_tag(class_tag, size, gdata->system_trace_index, thread_serial_num, pindex, psite); } } /* External interfaces */ SiteIndex site_find_or_create(ClassIndex cnum, TraceIndex trace_index) { SiteIndex index; static SiteKey empty_key; SiteKey key; key = empty_key; HPROF_ASSERT(cnum!=0); HPROF_ASSERT(trace_index!=0); key.cnum = cnum; key.trace_index = trace_index; index = table_find_or_create_entry(gdata->site_table, &key, (int)sizeof(key), NULL, NULL); return index; } void site_init(void) { HPROF_ASSERT(gdata->site_table==NULL); gdata->site_table = table_initialize("Site", 1024, 1024, 511, (int)sizeof(SiteInfo)); } void site_list(void) { debug_message( "--------------------- Site Table ------------------------\n"); table_walk_items(gdata->site_table, &list_item, NULL); debug_message( "----------------------------------------------------------\n"); } void site_cleanup(void) { table_cleanup(gdata->site_table, NULL, NULL); gdata->site_table = NULL; } void site_update_stats(SiteIndex index, jint size, jint hits) { SiteInfo *info; table_lock_enter(gdata->site_table); { info = get_info(index); info->n_live_instances += hits; info->n_live_bytes += size; info->changed = 1; gdata->total_live_bytes += size; gdata->total_live_instances += hits; if ( size > 0 ) { info->n_alloced_instances += hits; info->n_alloced_bytes += size; gdata->total_alloced_bytes = jlong_add(gdata->total_alloced_bytes, jint_to_jlong(size)); gdata->total_alloced_instances = jlong_add(gdata->total_alloced_instances, jint_to_jlong(hits)); } } table_lock_exit(gdata->site_table); } /* Output allocation sites, up to the given cut-off point, and according * to the given flags: * * SITE_DUMP_INCREMENTAL only dump what's changed since last dump. * SITE_SORT_BY_ALLOC sort sites by total allocation rather * than live data. * SITE_FORCE_GC force a GC before the site dump. */ void site_write(JNIEnv *env, int flags, double cutoff) { HPROF_ASSERT(gdata->site_table!=NULL); LOG3("site_write", "flags", flags); if (flags & SITE_FORCE_GC) { runGC(); } HPROF_ASSERT(gdata->total_live_bytes!=0); rawMonitorEnter(gdata->data_access_lock); { IterateInfo iterate; int site_table_size; double accum_percent; void * comment_str; int i; int cutoff_count; int nbytes; accum_percent = 0; site_table_size = table_element_count(gdata->site_table); (void)memset(&iterate, 0, sizeof(iterate)); nbytes = site_table_size * (int)sizeof(SiteIndex); if ( nbytes > 0 ) { iterate.site_nums = HPROF_MALLOC(nbytes); (void)memset(iterate.site_nums, 0, nbytes); } iterate.count = 0; iterate.changed_only = flags & SITE_DUMP_INCREMENTAL; table_walk_items(gdata->site_table, &collect_iterator, &iterate); site_table_size = iterate.count; if (flags & SITE_SORT_BY_ALLOC) { comment_str = "allocated bytes"; qsort(iterate.site_nums, site_table_size, sizeof(SiteIndex), &qsort_compare_allocated_bytes); } else { comment_str = "live bytes"; qsort(iterate.site_nums, site_table_size, sizeof(SiteIndex), &qsort_compare_live_bytes); } trace_output_unmarked(env); cutoff_count = 0; for (i = 0; i < site_table_size; i++) { SiteInfo *info; SiteIndex index; double ratio; index= iterate.site_nums[i]; HPROF_ASSERT(index!=0); info = get_info(index); ratio = (double)info->n_live_bytes / (double)gdata->total_live_bytes; if (ratio < cutoff) { break; } cutoff_count++; } io_write_sites_header( comment_str, flags, cutoff, gdata->total_live_bytes, gdata->total_live_instances, gdata->total_alloced_bytes, gdata->total_alloced_instances, cutoff_count); for (i = 0; i < cutoff_count; i++) { SiteInfo *info; SiteKey *pkey; SiteIndex index; char *class_signature; double ratio; index = iterate.site_nums[i]; pkey = get_pkey(index); info = get_info(index); ratio = (double)info->n_live_bytes / (double)gdata->total_live_bytes; accum_percent += ratio; class_signature = string_get(class_get_signature(pkey->cnum)); io_write_sites_elem(i + 1, ratio, accum_percent, class_signature, class_get_serial_number(pkey->cnum), trace_get_serial_number(pkey->trace_index), info->n_live_bytes, info->n_live_instances, info->n_alloced_bytes, info->n_alloced_instances); } io_write_sites_footer(); table_walk_items(gdata->site_table, &mark_unchanged_iterator, NULL); if ( iterate.site_nums != NULL ) { HPROF_FREE(iterate.site_nums); } } rawMonitorExit(gdata->data_access_lock); } /* Primitive array data callback for FollowReferences */ static jint JNICALL cbPrimArrayData(jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data) { ObjectIndex object_index; RefIndex ref_index; RefIndex prev_ref_index; HPROF_ASSERT(tag_ptr!=NULL); HPROF_ASSERT(class_tag!=(jlong)0); HPROF_ASSERT((*tag_ptr)!=(jlong)0); if ( class_tag == (jlong)0 || (*tag_ptr) == (jlong)0 ) { /* We can't do anything with a class_tag==0, just skip it */ return JVMTI_VISIT_OBJECTS; } /* Assume object has been tagged, get object index */ object_index = tag_extract((*tag_ptr)); /* Save string data */ prev_ref_index = object_get_references(object_index); ref_index = reference_prim_array(prev_ref_index, element_type, elements, element_count); object_set_references(object_index, ref_index); return JVMTI_VISIT_OBJECTS; } /* Primitive field data callback for FollowReferences */ static jint JNICALL cbPrimFieldData(jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong* tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data) { ObjectIndex object_index; jint field_index; RefIndex ref_index; RefIndex prev_ref_index; HPROF_ASSERT(tag_ptr!=NULL); HPROF_ASSERT(class_tag!=(jlong)0); HPROF_ASSERT((*tag_ptr)!=(jlong)0); if ( class_tag == (jlong)0 || (*tag_ptr) == (jlong)0 ) { /* We can't do anything with a class_tag==0, just skip it */ return JVMTI_VISIT_OBJECTS; } /* If the field is 0, just skip it, we assume 0 */ if ( value.j == (jlong)0 ) { return JVMTI_VISIT_OBJECTS; } /* Get field index */ field_index = reference_info->field.index; /* We assume the object was tagged */ object_index = tag_extract((*tag_ptr)); /* Save primitive field data */ prev_ref_index = object_get_references(object_index); ref_index = reference_prim_field(prev_ref_index, reference_kind, value_type, value, field_index); object_set_references(object_index, ref_index); return JVMTI_VISIT_OBJECTS; } static SerialNumber checkThreadSerialNumber(SerialNumber thread_serial_num) { TlsIndex tls_index; if ( thread_serial_num == gdata->unknown_thread_serial_num ) { return thread_serial_num; } tls_index = tls_find(thread_serial_num); if ( tls_index != 0 && tls_get_in_heap_dump(tls_index) != 0 ) { return thread_serial_num; } return gdata->unknown_thread_serial_num; } /* Get the object index and thread serial number for this local object */ static void localReference(jlong *tag_ptr, jlong class_tag, jlong thread_tag, jlong size, ObjectIndex *pobject_index, SerialNumber *pthread_serial_num) { ObjectIndex object_index; SerialNumber thread_serial_num; HPROF_ASSERT(pobject_index!=NULL); HPROF_ASSERT(pthread_serial_num!=NULL); HPROF_ASSERT(tag_ptr!=NULL); HPROF_ASSERT(class_tag!=(jlong)0); if ( (*tag_ptr) != (jlong)0 ) { object_index = tag_extract(*tag_ptr); thread_serial_num = object_get_thread_serial_number(object_index); thread_serial_num = checkThreadSerialNumber(thread_serial_num); } else { if ( thread_tag != (jlong)0 ) { ObjectIndex thread_object_index; thread_object_index = tag_extract(thread_tag); thread_serial_num = object_get_thread_serial_number(thread_object_index); thread_serial_num = checkThreadSerialNumber(thread_serial_num); } else { thread_serial_num = gdata->unknown_thread_serial_num; } /* Create and set the tag. */ *tag_ptr = make_new_tag(class_tag, size, gdata->system_trace_index, thread_serial_num, &object_index, NULL); } HPROF_ASSERT(thread_serial_num!=0); HPROF_ASSERT(object_index!=0); *pobject_index = object_index; *pthread_serial_num = thread_serial_num; } /* Store away plain object reference information */ static jint objectReference(jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length) { ObjectIndex object_index; jint reference_index; RefIndex ref_index; RefIndex prev_ref_index; ObjectIndex referrer_object_index; jlong object_tag; HPROF_ASSERT(tag_ptr!=NULL); HPROF_ASSERT(class_tag!=(jlong)0); HPROF_ASSERT(referrer_tag_ptr!=NULL); HPROF_ASSERT((*referrer_tag_ptr)!=(jlong)0); if ( class_tag == (jlong)0 || (*referrer_tag_ptr) == (jlong)0 ) { /* We can't do anything with a class_tag==0, just skip it */ return JVMTI_VISIT_OBJECTS; } switch ( reference_kind ) { case JVMTI_HEAP_REFERENCE_CLASS_LOADER: case JVMTI_HEAP_REFERENCE_INTERFACE: default: /* Currently we don't need these */ return JVMTI_VISIT_OBJECTS; case JVMTI_HEAP_REFERENCE_FIELD: case JVMTI_HEAP_REFERENCE_STATIC_FIELD: reference_index = reference_info->field.index; break; case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: reference_index = reference_info->array.index; break; case JVMTI_HEAP_REFERENCE_CONSTANT_POOL: reference_index = reference_info->constant_pool.index; break; case JVMTI_HEAP_REFERENCE_SIGNERS: case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN: reference_index = 0; break; } /* We assume the referrer is tagged */ referrer_object_index = tag_extract((*referrer_tag_ptr)); /* Now check the referree */ object_tag = *tag_ptr; if ( object_tag != (jlong)0 ) { object_index = tag_extract(object_tag); } else { /* Create and set the tag. */ object_tag = make_new_tag(class_tag, size, gdata->system_trace_index, gdata->unknown_thread_serial_num, &object_index, NULL); *tag_ptr = object_tag; } HPROF_ASSERT(object_index!=0); /* Save reference information */ prev_ref_index = object_get_references(referrer_object_index); ref_index = reference_obj(prev_ref_index, reference_kind, object_index, reference_index, length); object_set_references(referrer_object_index, ref_index); return JVMTI_VISIT_OBJECTS; } /* FollowReferences heap_reference_callback */ static jint JNICALL cbReference(jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data) { ObjectIndex object_index; /* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit * are allowed here (see the JVMTI Spec). */ HPROF_ASSERT(tag_ptr!=NULL); HPROF_ASSERT(class_tag!=(jlong)0); if ( class_tag == (jlong)0 ) { /* We can't do anything with a class_tag==0, just skip it */ return JVMTI_VISIT_OBJECTS; } switch ( reference_kind ) { case JVMTI_HEAP_REFERENCE_FIELD: case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT: case JVMTI_HEAP_REFERENCE_CLASS_LOADER: case JVMTI_HEAP_REFERENCE_SIGNERS: case JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN: case JVMTI_HEAP_REFERENCE_INTERFACE: case JVMTI_HEAP_REFERENCE_STATIC_FIELD: case JVMTI_HEAP_REFERENCE_CONSTANT_POOL: return objectReference(reference_kind, reference_info, class_tag, size, tag_ptr, referrer_tag_ptr, length); case JVMTI_HEAP_REFERENCE_JNI_GLOBAL: { SerialNumber trace_serial_num; SerialNumber gref_serial_num; TraceIndex trace_index; SiteIndex object_site_index; setup_tag_on_root(tag_ptr, class_tag, size, gdata->unknown_thread_serial_num, &object_index, &object_site_index); if ( object_site_index != 0 ) { SiteKey *pkey; pkey = get_pkey(object_site_index); trace_index = pkey->trace_index; } else { trace_index = gdata->system_trace_index; } trace_serial_num = trace_get_serial_number(trace_index); gref_serial_num = gdata->gref_serial_number_counter++; io_heap_root_jni_global(object_index, gref_serial_num, trace_serial_num); } break; case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS: { char *sig; SerialNumber class_serial_num; SiteIndex object_site_index; setup_tag_on_root(tag_ptr, class_tag, size, gdata->unknown_thread_serial_num, &object_index, &object_site_index); sig = "Unknown"; class_serial_num = 0; if ( object_site_index != 0 ) { SiteKey *pkey; pkey = get_pkey(object_site_index); sig = string_get(class_get_signature(pkey->cnum)); class_serial_num = class_get_serial_number(pkey->cnum); } io_heap_root_system_class(object_index, sig, class_serial_num); } break; case JVMTI_HEAP_REFERENCE_MONITOR: setup_tag_on_root(tag_ptr, class_tag, size, gdata->unknown_thread_serial_num, &object_index, NULL); io_heap_root_monitor(object_index); break; case JVMTI_HEAP_REFERENCE_STACK_LOCAL: { SerialNumber thread_serial_num; jlong thread_tag; thread_tag = reference_info->stack_local.thread_tag; localReference(tag_ptr, class_tag, thread_tag, size, &object_index, &thread_serial_num); io_heap_root_java_frame(object_index, thread_serial_num, reference_info->stack_local.depth); } break; case JVMTI_HEAP_REFERENCE_JNI_LOCAL: { SerialNumber thread_serial_num; jlong thread_tag; thread_tag = reference_info->jni_local.thread_tag; localReference(tag_ptr, class_tag, thread_tag, size, &object_index, &thread_serial_num); io_heap_root_jni_local(object_index, thread_serial_num, reference_info->jni_local.depth); } break; case JVMTI_HEAP_REFERENCE_THREAD: { SerialNumber thread_serial_num; SerialNumber trace_serial_num; TraceIndex trace_index; SiteIndex object_site_index; TlsIndex tls_index; /* It is assumed that tag_ptr is referring to a * java.lang.Thread object here. */ if ( (*tag_ptr) != (jlong)0 ) { setup_tag_on_root(tag_ptr, class_tag, size, 0, &object_index, &object_site_index); trace_index = site_get_trace_index(object_site_index); /* Hopefully the ThreadStart event put this thread's * correct serial number on it's object. */ thread_serial_num = object_get_thread_serial_number(object_index); } else { /* Rare situation that a Thread object is not tagged. * Create special unique thread serial number in this * case, probably means we never saw a thread start * or thread end, or even an allocation of the thread * object. */ thread_serial_num = gdata->thread_serial_number_counter++; setup_tag_on_root(tag_ptr, class_tag, size, thread_serial_num, &object_index, &object_site_index); trace_index = gdata->system_trace_index; } /* Get tls_index and set in_heap_dump, if we find it. */ tls_index = tls_find(thread_serial_num); if ( tls_index != 0 ) { tls_set_in_heap_dump(tls_index, 1); } trace_serial_num = trace_get_serial_number(trace_index); /* Issue thread object (must be before thread root) */ io_heap_root_thread_object(object_index, thread_serial_num, trace_serial_num); /* Issue thread root */ io_heap_root_thread(object_index, thread_serial_num); } break; case JVMTI_HEAP_REFERENCE_OTHER: setup_tag_on_root(tag_ptr, class_tag, size, gdata->unknown_thread_serial_num, &object_index, NULL); io_heap_root_unknown(object_index); break; default: /* Ignore anything else */ break; } return JVMTI_VISIT_OBJECTS; } void site_heapdump(JNIEnv *env) { rawMonitorEnter(gdata->data_access_lock); { jvmtiHeapCallbacks heapCallbacks; /* Remove class dumped status, all classes must be dumped */ class_all_status_remove(CLASS_DUMPED); /* Clear in_heap_dump flag */ tls_clear_in_heap_dump(); /* Dump the last thread traces and get the lists back we need */ tls_dump_traces(env); /* Write header for heap dump */ io_heap_header(gdata->total_live_instances, gdata->total_live_bytes); /* Setup a clean reference table */ reference_init(); /* Walk over all reachable objects and dump out roots */ gdata->gref_serial_number_counter = gdata->gref_serial_number_start; /* Issue thread object for fake non-existent unknown thread * just in case someone refers to it. Real threads are handled * during iterate over reachable objects. */ io_heap_root_thread_object(0, gdata->unknown_thread_serial_num, trace_get_serial_number(gdata->system_trace_index)); /* Iterate over heap and get the real stuff */ (void)memset(&heapCallbacks, 0, sizeof(heapCallbacks)); /* Select callbacks */ heapCallbacks.heap_reference_callback = &cbReference; if ( gdata->primfields == JNI_TRUE ) { heapCallbacks.primitive_field_callback = &cbPrimFieldData; } if ( gdata->primarrays == JNI_TRUE ) { heapCallbacks.array_primitive_value_callback = &cbPrimArrayData; } followReferences(&heapCallbacks, (void*)NULL); /* Process reference information. */ object_reference_dump(env); object_clear_references(); reference_cleanup(); /* Dump the last thread traces and get the lists back we need */ tls_dump_traces(env); /* Write out footer for heap dump */ io_heap_footer(); } rawMonitorExit(gdata->data_access_lock); }