blob: bc6e2f02daac3fb605476210cac8c085481d43e1 [file] [log] [blame]
/*
* Copyright (c) 2003, 2005, 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.
*/
/* Trace table. */
/*
* A trace is an optional thread serial number plus N frames.
*
* The thread serial number is added to the key only if the user asks for
* threads in traces, which will cause many more traces to be created.
* Without it all threads share the traces.
*
* This is a variable length Key, depending on the number of frames.
* The frames are FrameIndex values into the frame table.
*
* It is important that the thread serial number is used and not the
* TlsIndex, threads come and go, and TlsIndex values are re-used
* but the thread serial number is unique per thread.
*
* The cpu=times and cpu=samples dumps rely heavily on traces, the trace
* dump preceeds the cpu information and uses the trace information.
* Depending on the cpu= request, different sorts are applied to the
* traces that are dumped.
*
*/
#include "hprof.h"
typedef struct TraceKey {
SerialNumber thread_serial_num; /* Thread serial number */
short n_frames; /* Number of frames that follow. */
jvmtiPhase phase : 8; /* Makes some traces unique */
FrameIndex frames[1]; /* Variable length */
} TraceKey;
typedef struct TraceInfo {
SerialNumber serial_num; /* Trace serial number */
jint num_hits; /* Number of hits this trace has */
jlong total_cost; /* Total cost associated with trace */
jlong self_cost; /* Total cost without children cost */
jint status; /* Status of dump of trace */
} TraceInfo;
typedef struct IterateInfo {
TraceIndex* traces;
int count;
jlong grand_total_cost;
} IterateInfo;
/* Private internal functions. */
static TraceKey*
get_pkey(TraceIndex index)
{
void * pkey;
int key_len;
table_get_key(gdata->trace_table, index, &pkey, &key_len);
HPROF_ASSERT(pkey!=NULL);
HPROF_ASSERT(key_len>=(int)sizeof(TraceKey));
HPROF_ASSERT(((TraceKey*)pkey)->n_frames<=1?key_len==(int)sizeof(TraceKey) :
key_len==(int)sizeof(TraceKey)+
(int)sizeof(FrameIndex)*(((TraceKey*)pkey)->n_frames-1));
return (TraceKey*)pkey;
}
static TraceInfo *
get_info(TraceIndex index)
{
TraceInfo * info;
info = (TraceInfo*)table_get_info(gdata->trace_table, index);
return info;
}
static TraceIndex
find_or_create(SerialNumber thread_serial_num, jint n_frames,
FrameIndex *frames, jvmtiPhase phase, TraceKey *trace_key_buffer)
{
TraceInfo * info;
TraceKey * pkey;
int key_len;
TraceIndex index;
jboolean new_one;
static TraceKey empty_key;
HPROF_ASSERT(frames!=NULL);
HPROF_ASSERT(trace_key_buffer!=NULL);
key_len = (int)sizeof(TraceKey);
if ( n_frames > 1 ) {
key_len += (int)((n_frames-1)*(int)sizeof(FrameIndex));
}
pkey = trace_key_buffer;
*pkey = empty_key;
pkey->thread_serial_num = (gdata->thread_in_traces ? thread_serial_num : 0);
pkey->n_frames = (short)n_frames;
pkey->phase = phase;
if ( n_frames > 0 ) {
(void)memcpy(pkey->frames, frames, (n_frames*(int)sizeof(FrameIndex)));
}
new_one = JNI_FALSE;
index = table_find_or_create_entry(gdata->trace_table,
pkey, key_len, &new_one, NULL);
if ( new_one ) {
info = get_info(index);
info->serial_num = gdata->trace_serial_number_counter++;
}
return index;
}
static void
list_item(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
{
TraceInfo *info;
TraceKey *key;
int i;
HPROF_ASSERT(key_ptr!=NULL);
HPROF_ASSERT(key_len>0);
HPROF_ASSERT(info_ptr!=NULL);
key = (TraceKey*)key_ptr;
info = (TraceInfo *)info_ptr;
debug_message( "Trace 0x%08x: SN=%u, threadSN=%u, n_frames=%d, frames=(",
index,
info->serial_num,
key->thread_serial_num,
key->n_frames);
for ( i = 0 ; i < key->n_frames ; i++ ) {
debug_message( "0x%08x, ", key->frames[i]);
}
debug_message( "), traceSN=%u, num_hits=%d, self_cost=(%d,%d), "
"total_cost=(%d,%d), status=0x%08x\n",
info->serial_num,
info->num_hits,
jlong_high(info->self_cost),
jlong_low(info->self_cost),
jlong_high(info->total_cost),
jlong_low(info->total_cost),
info->status);
}
static void
clear_cost(TableIndex i, void *key_ptr, int key_len, void *info_ptr, void *arg)
{
TraceInfo *info;
HPROF_ASSERT(key_ptr!=NULL);
HPROF_ASSERT(key_len>0);
HPROF_ASSERT(info_ptr!=NULL);
info = (TraceInfo *)info_ptr;
info->num_hits = 0;
info->total_cost = 0;
info->self_cost = 0;
}
/* Get the names for a frame in order to dump it. */
static void
get_frame_details(JNIEnv *env, FrameIndex frame_index,
SerialNumber *frame_serial_num, char **pcsig, ClassIndex *pcnum,
char **pmname, char **pmsig, char **psname, jint *plineno)
{
jmethodID method;
jlocation location;
jint lineno;
HPROF_ASSERT(frame_index!=0);
*pmname = NULL;
*pmsig = NULL;
*pcsig = NULL;
if ( psname != NULL ) {
*psname = NULL;
}
if ( plineno != NULL ) {
*plineno = -1;
}
if ( pcnum != NULL ) {
*pcnum = 0;
}
frame_get_location(frame_index, frame_serial_num, &method, &location, &lineno);
if ( plineno != NULL ) {
*plineno = lineno;
}
WITH_LOCAL_REFS(env, 1) {
jclass klass;
getMethodClass(method, &klass);
getClassSignature(klass, pcsig, NULL);
if ( pcnum != NULL ) {
LoaderIndex loader_index;
jobject loader;
loader = getClassLoader(klass);
loader_index = loader_find_or_create(env, loader);
*pcnum = class_find_or_create(*pcsig, loader_index);
(void)class_new_classref(env, *pcnum, klass);
}
if ( psname != NULL ) {
getSourceFileName(klass, psname);
}
} END_WITH_LOCAL_REFS;
getMethodName(method, pmname, pmsig);
}
/* Write out a stack trace. */
static void
output_trace(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
{
TraceKey *key;
TraceInfo *info;
SerialNumber serial_num;
SerialNumber thread_serial_num;
jint n_frames;
JNIEnv *env;
int i;
char *phase_str;
struct FrameNames {
SerialNumber serial_num;
char * sname;
char * csig;
char * mname;
int lineno;
} *finfo;
info = (TraceInfo*)info_ptr;
if ( info->status != 0 ) {
return;
}
env = (JNIEnv*)arg;
key = (TraceKey*)key_ptr;
thread_serial_num = key->thread_serial_num;
serial_num = info->serial_num;
info->status = 1;
finfo = NULL;
n_frames = (jint)key->n_frames;
if ( n_frames > 0 ) {
finfo = (struct FrameNames *)HPROF_MALLOC(n_frames*(int)sizeof(struct FrameNames));
/* Write frames, but save information for trace later */
for (i = 0; i < n_frames; i++) {
FrameIndex frame_index;
char *msig;
ClassIndex cnum;
frame_index = key->frames[i];
get_frame_details(env, frame_index, &finfo[i].serial_num,
&finfo[i].csig, &cnum,
&finfo[i].mname, &msig, &finfo[i].sname, &finfo[i].lineno);
if (frame_get_status(frame_index) == 0) {
io_write_frame(frame_index, finfo[i].serial_num,
finfo[i].mname, msig,
finfo[i].sname, class_get_serial_number(cnum),
finfo[i].lineno);
frame_set_status(frame_index, 1);
}
jvmtiDeallocate(msig);
}
}
/* Find phase string */
if ( key->phase == JVMTI_PHASE_LIVE ) {
phase_str = NULL; /* Normal trace, no phase annotation */
} else {
phase_str = phaseString(key->phase);
}
io_write_trace_header(serial_num, thread_serial_num, n_frames, phase_str);
for (i = 0; i < n_frames; i++) {
io_write_trace_elem(serial_num, key->frames[i], finfo[i].serial_num,
finfo[i].csig,
finfo[i].mname, finfo[i].sname, finfo[i].lineno);
jvmtiDeallocate(finfo[i].csig);
jvmtiDeallocate(finfo[i].mname);
jvmtiDeallocate(finfo[i].sname);
}
io_write_trace_footer(serial_num, thread_serial_num, n_frames);
if ( finfo != NULL ) {
HPROF_FREE(finfo);
}
}
/* Output a specific list of traces. */
static void
output_list(JNIEnv *env, TraceIndex *list, jint count)
{
rawMonitorEnter(gdata->data_access_lock); {
int i;
for ( i = 0; i < count ; i++ ) {
TraceIndex index;
TraceInfo *info;
void * pkey;
int key_len;
index = list[i];
table_get_key(gdata->trace_table, index, &pkey, &key_len);
info = get_info(index);
output_trace(index, pkey, key_len, info, (void*)env);
}
} rawMonitorExit(gdata->data_access_lock);
}
static void
collect_iterator(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg)
{
TraceInfo *info;
IterateInfo *iterate;
HPROF_ASSERT(key_ptr!=NULL);
HPROF_ASSERT(key_len>0);
HPROF_ASSERT(arg!=NULL);
HPROF_ASSERT(info_ptr!=NULL);
iterate = (IterateInfo *)arg;
info = (TraceInfo *)info_ptr;
iterate->traces[iterate->count++] = index;
iterate->grand_total_cost += info->self_cost;
}
static int
qsort_compare_cost(const void *p_trace1, const void *p_trace2)
{
TraceIndex trace1;
TraceIndex trace2;
TraceInfo * info1;
TraceInfo * info2;
HPROF_ASSERT(p_trace1!=NULL);
HPROF_ASSERT(p_trace2!=NULL);
trace1 = *(TraceIndex *)p_trace1;
trace2 = *(TraceIndex *)p_trace2;
info1 = get_info(trace1);
info2 = get_info(trace2);
/*LINTED*/
return (int)(info2->self_cost - info1->self_cost);
}
static int
qsort_compare_num_hits(const void *p_trace1, const void *p_trace2)
{
TraceIndex trace1;
TraceIndex trace2;
TraceInfo * info1;
TraceInfo * info2;
HPROF_ASSERT(p_trace1!=NULL);
HPROF_ASSERT(p_trace2!=NULL);
trace1 = *(TraceIndex *)p_trace1;
trace2 = *(TraceIndex *)p_trace2;
info1 = get_info(trace1);
info2 = get_info(trace2);
return info2->num_hits - info1->num_hits;
}
/* External interfaces. */
void
trace_init(void)
{
gdata->trace_table = table_initialize("Trace",
256, 256, 511, (int)sizeof(TraceInfo));
}
void
trace_list(void)
{
debug_message(
"--------------------- Trace Table ------------------------\n");
table_walk_items(gdata->trace_table, &list_item, NULL);
debug_message(
"----------------------------------------------------------\n");
}
void
trace_cleanup(void)
{
table_cleanup(gdata->trace_table, NULL, NULL);
gdata->trace_table = NULL;
}
SerialNumber
trace_get_serial_number(TraceIndex index)
{
TraceInfo *info;
if ( index == 0 ) {
return 0;
}
info = get_info(index);
return info->serial_num;
}
void
trace_increment_cost(TraceIndex index, jint num_hits, jlong self_cost, jlong total_cost)
{
TraceInfo *info;
table_lock_enter(gdata->trace_table); {
info = get_info(index);
info->num_hits += num_hits;
info->self_cost += self_cost;
info->total_cost += total_cost;
} table_lock_exit(gdata->trace_table);
}
TraceIndex
trace_find_or_create(SerialNumber thread_serial_num, jint n_frames, FrameIndex *frames, jvmtiFrameInfo *jframes_buffer)
{
return find_or_create(thread_serial_num, n_frames, frames, getPhase(),
(TraceKey*)jframes_buffer);
}
/* We may need to ask for more frames than the user asked for */
static int
get_real_depth(int depth, jboolean skip_init)
{
int extra_frames;
extra_frames = 0;
/* This is only needed if we are doing BCI */
if ( gdata->bci && depth > 0 ) {
/* Account for Java and native Tracker methods */
extra_frames = 2;
if ( skip_init ) {
/* Also allow for ignoring the java.lang.Object.<init> method */
extra_frames += 1;
}
}
return depth + extra_frames;
}
/* Fill in FrameIndex array from jvmtiFrameInfo array, return n_frames */
static int
fill_frame_buffer(int depth, int real_depth,
int frame_count, jboolean skip_init,
jvmtiFrameInfo *jframes_buffer, FrameIndex *frames_buffer)
{
int n_frames;
jint topframe;
/* If real_depth is 0, just return 0 */
if ( real_depth == 0 ) {
return 0;
}
/* Assume top frame index is 0 for now */
topframe = 0;
/* Possible top frames belong to the hprof Tracker class, remove them */
if ( gdata->bci ) {
while ( ( ( frame_count - topframe ) > 0 ) &&
( topframe < (real_depth-depth) ) &&
( tracker_method(jframes_buffer[topframe].method) ||
( skip_init
&& jframes_buffer[topframe].method==gdata->object_init_method ) )
) {
topframe++;
}
}
/* Adjust count to match depth request */
if ( ( frame_count - topframe ) > depth ) {
frame_count = depth + topframe;
}
/* The actual frame count we will process */
n_frames = frame_count - topframe;
if ( n_frames > 0 ) {
int i;
for (i = 0; i < n_frames; i++) {
jmethodID method;
jlocation location;
method = jframes_buffer[i+topframe].method;
location = jframes_buffer[i+topframe].location;
frames_buffer[i] = frame_find_or_create(method, location);
}
}
return n_frames;
}
/* Get the trace for the supplied thread */
TraceIndex
trace_get_current(jthread thread, SerialNumber thread_serial_num,
int depth, jboolean skip_init,
FrameIndex *frames_buffer,
jvmtiFrameInfo *jframes_buffer)
{
TraceIndex index;
jint frame_count;
int real_depth;
int n_frames;
HPROF_ASSERT(thread!=NULL);
HPROF_ASSERT(frames_buffer!=NULL);
HPROF_ASSERT(jframes_buffer!=NULL);
/* We may need to ask for more frames than the user asked for */
real_depth = get_real_depth(depth, skip_init);
/* Get the stack trace for this one thread */
frame_count = 0;
if ( real_depth > 0 ) {
getStackTrace(thread, jframes_buffer, real_depth, &frame_count);
}
/* Create FrameIndex's */
n_frames = fill_frame_buffer(depth, real_depth, frame_count, skip_init,
jframes_buffer, frames_buffer);
/* Lookup or create new TraceIndex */
index = find_or_create(thread_serial_num, n_frames, frames_buffer,
getPhase(), (TraceKey*)jframes_buffer);
return index;
}
/* Get traces for all threads in list (traces[i]==0 if thread not running) */
void
trace_get_all_current(jint thread_count, jthread *threads,
SerialNumber *thread_serial_nums,
int depth, jboolean skip_init,
TraceIndex *traces, jboolean always_care)
{
jvmtiStackInfo *stack_info;
int nbytes;
int real_depth;
int i;
FrameIndex *frames_buffer;
TraceKey *trace_key_buffer;
jvmtiPhase phase;
HPROF_ASSERT(threads!=NULL);
HPROF_ASSERT(thread_serial_nums!=NULL);
HPROF_ASSERT(traces!=NULL);
HPROF_ASSERT(thread_count > 0);
/* Find out what the phase is for all these traces */
phase = getPhase();
/* We may need to ask for more frames than the user asked for */
real_depth = get_real_depth(depth, skip_init);
/* Get the stack traces for all the threads */
getThreadListStackTraces(thread_count, threads, real_depth, &stack_info);
/* Allocate a frames_buffer and trace key buffer */
nbytes = (int)sizeof(FrameIndex)*real_depth;
frames_buffer = (FrameIndex*)HPROF_MALLOC(nbytes);
nbytes += (int)sizeof(TraceKey);
trace_key_buffer = (TraceKey*)HPROF_MALLOC(nbytes);
/* Loop over the stack traces we have for these 'thread_count' threads */
for ( i = 0 ; i < thread_count ; i++ ) {
int n_frames;
/* Assume 0 at first (no trace) */
traces[i] = 0;
/* If thread has frames, is runnable, and isn't suspended, we care */
if ( always_care ||
( stack_info[i].frame_count > 0
&& (stack_info[i].state & JVMTI_THREAD_STATE_RUNNABLE)!=0
&& (stack_info[i].state & JVMTI_THREAD_STATE_SUSPENDED)==0
&& (stack_info[i].state & JVMTI_THREAD_STATE_INTERRUPTED)==0 )
) {
/* Create FrameIndex's */
n_frames = fill_frame_buffer(depth, real_depth,
stack_info[i].frame_count,
skip_init,
stack_info[i].frame_buffer,
frames_buffer);
/* Lookup or create new TraceIndex */
traces[i] = find_or_create(thread_serial_nums[i],
n_frames, frames_buffer, phase, trace_key_buffer);
}
}
/* Make sure we free the space */
HPROF_FREE(frames_buffer);
HPROF_FREE(trace_key_buffer);
jvmtiDeallocate(stack_info);
}
/* Increment the trace costs for all the threads (for cpu=samples) */
void
trace_increment_all_sample_costs(jint thread_count, jthread *threads,
SerialNumber *thread_serial_nums,
int depth, jboolean skip_init)
{
TraceIndex *traces;
int nbytes;
HPROF_ASSERT(threads!=NULL);
HPROF_ASSERT(thread_serial_nums!=NULL);
HPROF_ASSERT(thread_count > 0);
HPROF_ASSERT(depth >= 0);
if ( depth == 0 ) {
return;
}
/* Allocate a traces array */
nbytes = (int)sizeof(TraceIndex)*thread_count;
traces = (TraceIndex*)HPROF_MALLOC(nbytes);
/* Get all the current traces for these threads */
trace_get_all_current(thread_count, threads, thread_serial_nums,
depth, skip_init, traces, JNI_FALSE);
/* Increment the cpu=samples cost on these traces */
table_lock_enter(gdata->trace_table); {
int i;
for ( i = 0 ; i < thread_count ; i++ ) {
/* Each trace gets a hit and an increment of it's total cost */
if ( traces[i] != 0 ) {
TraceInfo *info;
info = get_info(traces[i]);
info->num_hits += 1;
info->self_cost += (jlong)1;
info->total_cost += (jlong)1;
}
}
} table_lock_exit(gdata->trace_table);
/* Free up the memory allocated */
HPROF_FREE(traces);
}
void
trace_output_unmarked(JNIEnv *env)
{
rawMonitorEnter(gdata->data_access_lock); {
table_walk_items(gdata->trace_table, &output_trace, (void*)env);
} rawMonitorExit(gdata->data_access_lock);
}
/* output info on the cost associated with traces */
void
trace_output_cost(JNIEnv *env, double cutoff)
{
IterateInfo iterate;
int i, trace_table_size, n_items;
double accum;
int n_entries;
rawMonitorEnter(gdata->data_access_lock); {
n_entries = table_element_count(gdata->trace_table);
iterate.traces = HPROF_MALLOC(n_entries*(int)sizeof(TraceIndex)+1);
iterate.count = 0;
iterate.grand_total_cost = 0;
table_walk_items(gdata->trace_table, &collect_iterator, &iterate);
trace_table_size = iterate.count;
/* sort all the traces according to the cost */
qsort(iterate.traces, trace_table_size, sizeof(TraceIndex),
&qsort_compare_cost);
n_items = 0;
for (i = 0; i < trace_table_size; i++) {
TraceInfo *info;
TraceIndex trace_index;
double percent;
trace_index = iterate.traces[i];
info = get_info(trace_index);
/* As soon as a trace with zero hits is seen, we need no others */
if (info->num_hits == 0 ) {
break;
}
percent = (double)info->self_cost / (double)iterate.grand_total_cost;
if (percent < cutoff) {
break;
}
n_items++;
}
/* Now write all trace we might refer to. */
output_list(env, iterate.traces, n_items);
io_write_cpu_samples_header(iterate.grand_total_cost, n_items);
accum = 0;
for (i = 0; i < n_items; i++) {
SerialNumber frame_serial_num;
TraceInfo *info;
TraceKey *key;
TraceIndex trace_index;
double percent;
char *csig;
char *mname;
char *msig;
trace_index = iterate.traces[i];
info = get_info(trace_index);
key = get_pkey(trace_index);
percent = ((double)info->self_cost / (double)iterate.grand_total_cost) * 100.0;
accum += percent;
csig = NULL;
mname = NULL;
msig = NULL;
if (key->n_frames > 0) {
get_frame_details(env, key->frames[0], &frame_serial_num,
&csig, NULL, &mname, &msig, NULL, NULL);
}
io_write_cpu_samples_elem(i+1, percent, accum, info->num_hits,
(jint)info->self_cost, info->serial_num,
key->n_frames, csig, mname);
jvmtiDeallocate(csig);
jvmtiDeallocate(mname);
jvmtiDeallocate(msig);
}
io_write_cpu_samples_footer();
HPROF_FREE(iterate.traces);
} rawMonitorExit(gdata->data_access_lock);
}
/* output the trace cost in old prof format */
void
trace_output_cost_in_prof_format(JNIEnv *env)
{
IterateInfo iterate;
int i, trace_table_size;
int n_entries;
rawMonitorEnter(gdata->data_access_lock); {
n_entries = table_element_count(gdata->trace_table);
iterate.traces = HPROF_MALLOC(n_entries*(int)sizeof(TraceIndex)+1);
iterate.count = 0;
iterate.grand_total_cost = 0;
table_walk_items(gdata->trace_table, &collect_iterator, &iterate);
trace_table_size = iterate.count;
/* sort all the traces according to the number of hits */
qsort(iterate.traces, trace_table_size, sizeof(TraceIndex),
&qsort_compare_num_hits);
io_write_oldprof_header();
for (i = 0; i < trace_table_size; i++) {
SerialNumber frame_serial_num;
TraceInfo *info;
TraceKey *key;
TraceIndex trace_index;
int num_frames;
int num_hits;
char *csig_callee;
char *mname_callee;
char *msig_callee;
char *csig_caller;
char *mname_caller;
char *msig_caller;
trace_index = iterate.traces[i];
key = get_pkey(trace_index);
info = get_info(trace_index);
num_hits = info->num_hits;
if (num_hits == 0) {
break;
}
csig_callee = NULL;
mname_callee = NULL;
msig_callee = NULL;
csig_caller = NULL;
mname_caller = NULL;
msig_caller = NULL;
num_frames = (int)key->n_frames;
if (num_frames >= 1) {
get_frame_details(env, key->frames[0], &frame_serial_num,
&csig_callee, NULL,
&mname_callee, &msig_callee, NULL, NULL);
}
if (num_frames > 1) {
get_frame_details(env, key->frames[1], &frame_serial_num,
&csig_caller, NULL,
&mname_caller, &msig_caller, NULL, NULL);
}
io_write_oldprof_elem(info->num_hits, num_frames,
csig_callee, mname_callee, msig_callee,
csig_caller, mname_caller, msig_caller,
(int)info->total_cost);
jvmtiDeallocate(csig_callee);
jvmtiDeallocate(mname_callee);
jvmtiDeallocate(msig_callee);
jvmtiDeallocate(csig_caller);
jvmtiDeallocate(mname_caller);
jvmtiDeallocate(msig_caller);
}
io_write_oldprof_footer();
HPROF_FREE(iterate.traces);
} rawMonitorExit(gdata->data_access_lock);
}
void
trace_clear_cost(void)
{
table_walk_items(gdata->trace_table, &clear_cost, NULL);
}