| /* |
| * Copyright (c) 2003, 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: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions 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 nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* 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); |
| } |