/*
* 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.
*/
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
* required for a production-quality application, such as security checks,
* input validation and proper error handling, might not be present in
* this sample code.
*/
/* 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);
}