author | goetz |
Thu, 21 Nov 2013 18:29:34 -0800 | |
changeset 22852 | 1063026e8cee |
parent 14342 | 8435a30053c1 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
14342
8435a30053c1
7197491: update copyright year to match last edit in jdk8 jdk repository
alanb
parents:
10292
diff
changeset
|
2 |
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
|
5 |
* modification, are permitted provided that the following conditions |
|
6 |
* are met: |
|
7 |
* |
|
8 |
* - Redistributions of source code must retain the above copyright |
|
9 |
* notice, this list of conditions and the following disclaimer. |
|
10 |
* |
|
11 |
* - Redistributions in binary form must reproduce the above copyright |
|
12 |
* notice, this list of conditions and the following disclaimer in the |
|
13 |
* documentation and/or other materials provided with the distribution. |
|
14 |
* |
|
5506 | 15 |
* - Neither the name of Oracle nor the names of its |
2 | 16 |
* contributors may be used to endorse or promote products derived |
17 |
* from this software without specific prior written permission. |
|
18 |
* |
|
19 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|
20 |
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|
21 |
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
22 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
23 |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
24 |
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
25 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
26 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
27 |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
28 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
29 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 |
*/ |
|
31 |
||
10292
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
32 |
/* |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
33 |
* This source code is provided to illustrate the usage of a given feature |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
34 |
* or technique and has been deliberately simplified. Additional steps |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
35 |
* required for a production-quality application, such as security checks, |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
36 |
* input validation and proper error handling, might not be present in |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
37 |
* this sample code. |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
38 |
*/ |
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
39 |
|
ed7db6a12c2a
7067811: Update demo/sample code to state it should not be used for production
nloodin
parents:
5506
diff
changeset
|
40 |
|
2 | 41 |
/* Trace table. */ |
42 |
||
43 |
/* |
|
44 |
* A trace is an optional thread serial number plus N frames. |
|
45 |
* |
|
46 |
* The thread serial number is added to the key only if the user asks for |
|
47 |
* threads in traces, which will cause many more traces to be created. |
|
48 |
* Without it all threads share the traces. |
|
49 |
* |
|
50 |
* This is a variable length Key, depending on the number of frames. |
|
51 |
* The frames are FrameIndex values into the frame table. |
|
52 |
* |
|
53 |
* It is important that the thread serial number is used and not the |
|
54 |
* TlsIndex, threads come and go, and TlsIndex values are re-used |
|
55 |
* but the thread serial number is unique per thread. |
|
56 |
* |
|
57 |
* The cpu=times and cpu=samples dumps rely heavily on traces, the trace |
|
58 |
* dump preceeds the cpu information and uses the trace information. |
|
59 |
* Depending on the cpu= request, different sorts are applied to the |
|
60 |
* traces that are dumped. |
|
61 |
* |
|
62 |
*/ |
|
63 |
||
64 |
#include "hprof.h" |
|
65 |
||
66 |
typedef struct TraceKey { |
|
67 |
SerialNumber thread_serial_num; /* Thread serial number */ |
|
68 |
short n_frames; /* Number of frames that follow. */ |
|
69 |
jvmtiPhase phase : 8; /* Makes some traces unique */ |
|
70 |
FrameIndex frames[1]; /* Variable length */ |
|
71 |
} TraceKey; |
|
72 |
||
73 |
typedef struct TraceInfo { |
|
74 |
SerialNumber serial_num; /* Trace serial number */ |
|
75 |
jint num_hits; /* Number of hits this trace has */ |
|
76 |
jlong total_cost; /* Total cost associated with trace */ |
|
77 |
jlong self_cost; /* Total cost without children cost */ |
|
78 |
jint status; /* Status of dump of trace */ |
|
79 |
} TraceInfo; |
|
80 |
||
81 |
typedef struct IterateInfo { |
|
82 |
TraceIndex* traces; |
|
83 |
int count; |
|
84 |
jlong grand_total_cost; |
|
85 |
} IterateInfo; |
|
86 |
||
87 |
/* Private internal functions. */ |
|
88 |
||
89 |
static TraceKey* |
|
90 |
get_pkey(TraceIndex index) |
|
91 |
{ |
|
92 |
void * pkey; |
|
93 |
int key_len; |
|
94 |
||
95 |
table_get_key(gdata->trace_table, index, &pkey, &key_len); |
|
96 |
HPROF_ASSERT(pkey!=NULL); |
|
97 |
HPROF_ASSERT(key_len>=(int)sizeof(TraceKey)); |
|
98 |
HPROF_ASSERT(((TraceKey*)pkey)->n_frames<=1?key_len==(int)sizeof(TraceKey) : |
|
99 |
key_len==(int)sizeof(TraceKey)+ |
|
100 |
(int)sizeof(FrameIndex)*(((TraceKey*)pkey)->n_frames-1)); |
|
101 |
return (TraceKey*)pkey; |
|
102 |
} |
|
103 |
||
104 |
static TraceInfo * |
|
105 |
get_info(TraceIndex index) |
|
106 |
{ |
|
107 |
TraceInfo * info; |
|
108 |
||
109 |
info = (TraceInfo*)table_get_info(gdata->trace_table, index); |
|
110 |
return info; |
|
111 |
} |
|
112 |
||
113 |
static TraceIndex |
|
114 |
find_or_create(SerialNumber thread_serial_num, jint n_frames, |
|
115 |
FrameIndex *frames, jvmtiPhase phase, TraceKey *trace_key_buffer) |
|
116 |
{ |
|
117 |
TraceInfo * info; |
|
118 |
TraceKey * pkey; |
|
119 |
int key_len; |
|
120 |
TraceIndex index; |
|
121 |
jboolean new_one; |
|
122 |
static TraceKey empty_key; |
|
123 |
||
124 |
HPROF_ASSERT(frames!=NULL); |
|
125 |
HPROF_ASSERT(trace_key_buffer!=NULL); |
|
126 |
key_len = (int)sizeof(TraceKey); |
|
127 |
if ( n_frames > 1 ) { |
|
128 |
key_len += (int)((n_frames-1)*(int)sizeof(FrameIndex)); |
|
129 |
} |
|
130 |
pkey = trace_key_buffer; |
|
131 |
*pkey = empty_key; |
|
132 |
pkey->thread_serial_num = (gdata->thread_in_traces ? thread_serial_num : 0); |
|
133 |
pkey->n_frames = (short)n_frames; |
|
134 |
pkey->phase = phase; |
|
135 |
if ( n_frames > 0 ) { |
|
136 |
(void)memcpy(pkey->frames, frames, (n_frames*(int)sizeof(FrameIndex))); |
|
137 |
} |
|
138 |
||
139 |
new_one = JNI_FALSE; |
|
140 |
index = table_find_or_create_entry(gdata->trace_table, |
|
141 |
pkey, key_len, &new_one, NULL); |
|
142 |
if ( new_one ) { |
|
143 |
info = get_info(index); |
|
144 |
info->serial_num = gdata->trace_serial_number_counter++; |
|
145 |
} |
|
146 |
return index; |
|
147 |
} |
|
148 |
||
149 |
static void |
|
150 |
list_item(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg) |
|
151 |
{ |
|
152 |
TraceInfo *info; |
|
153 |
TraceKey *key; |
|
154 |
int i; |
|
155 |
||
156 |
HPROF_ASSERT(key_ptr!=NULL); |
|
157 |
HPROF_ASSERT(key_len>0); |
|
158 |
HPROF_ASSERT(info_ptr!=NULL); |
|
159 |
key = (TraceKey*)key_ptr; |
|
160 |
info = (TraceInfo *)info_ptr; |
|
161 |
||
162 |
debug_message( "Trace 0x%08x: SN=%u, threadSN=%u, n_frames=%d, frames=(", |
|
163 |
index, |
|
164 |
info->serial_num, |
|
165 |
key->thread_serial_num, |
|
166 |
key->n_frames); |
|
167 |
for ( i = 0 ; i < key->n_frames ; i++ ) { |
|
168 |
debug_message( "0x%08x, ", key->frames[i]); |
|
169 |
} |
|
170 |
debug_message( "), traceSN=%u, num_hits=%d, self_cost=(%d,%d), " |
|
171 |
"total_cost=(%d,%d), status=0x%08x\n", |
|
172 |
info->serial_num, |
|
173 |
info->num_hits, |
|
174 |
jlong_high(info->self_cost), |
|
175 |
jlong_low(info->self_cost), |
|
176 |
jlong_high(info->total_cost), |
|
177 |
jlong_low(info->total_cost), |
|
178 |
info->status); |
|
179 |
} |
|
180 |
||
181 |
static void |
|
182 |
clear_cost(TableIndex i, void *key_ptr, int key_len, void *info_ptr, void *arg) |
|
183 |
{ |
|
184 |
TraceInfo *info; |
|
185 |
||
186 |
HPROF_ASSERT(key_ptr!=NULL); |
|
187 |
HPROF_ASSERT(key_len>0); |
|
188 |
HPROF_ASSERT(info_ptr!=NULL); |
|
189 |
info = (TraceInfo *)info_ptr; |
|
190 |
info->num_hits = 0; |
|
191 |
info->total_cost = 0; |
|
192 |
info->self_cost = 0; |
|
193 |
} |
|
194 |
||
195 |
/* Get the names for a frame in order to dump it. */ |
|
196 |
static void |
|
197 |
get_frame_details(JNIEnv *env, FrameIndex frame_index, |
|
198 |
SerialNumber *frame_serial_num, char **pcsig, ClassIndex *pcnum, |
|
199 |
char **pmname, char **pmsig, char **psname, jint *plineno) |
|
200 |
{ |
|
201 |
jmethodID method; |
|
202 |
jlocation location; |
|
203 |
jint lineno; |
|
204 |
||
205 |
HPROF_ASSERT(frame_index!=0); |
|
206 |
*pmname = NULL; |
|
207 |
*pmsig = NULL; |
|
208 |
*pcsig = NULL; |
|
209 |
if ( psname != NULL ) { |
|
210 |
*psname = NULL; |
|
211 |
} |
|
212 |
if ( plineno != NULL ) { |
|
213 |
*plineno = -1; |
|
214 |
} |
|
215 |
if ( pcnum != NULL ) { |
|
216 |
*pcnum = 0; |
|
217 |
} |
|
218 |
frame_get_location(frame_index, frame_serial_num, &method, &location, &lineno); |
|
219 |
if ( plineno != NULL ) { |
|
220 |
*plineno = lineno; |
|
221 |
} |
|
222 |
WITH_LOCAL_REFS(env, 1) { |
|
223 |
jclass klass; |
|
224 |
||
225 |
getMethodClass(method, &klass); |
|
226 |
getClassSignature(klass, pcsig, NULL); |
|
227 |
if ( pcnum != NULL ) { |
|
228 |
LoaderIndex loader_index; |
|
229 |
jobject loader; |
|
230 |
||
231 |
loader = getClassLoader(klass); |
|
232 |
loader_index = loader_find_or_create(env, loader); |
|
233 |
*pcnum = class_find_or_create(*pcsig, loader_index); |
|
234 |
(void)class_new_classref(env, *pcnum, klass); |
|
235 |
} |
|
236 |
if ( psname != NULL ) { |
|
237 |
getSourceFileName(klass, psname); |
|
238 |
} |
|
239 |
} END_WITH_LOCAL_REFS; |
|
240 |
getMethodName(method, pmname, pmsig); |
|
241 |
} |
|
242 |
||
243 |
/* Write out a stack trace. */ |
|
244 |
static void |
|
245 |
output_trace(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg) |
|
246 |
{ |
|
247 |
TraceKey *key; |
|
248 |
TraceInfo *info; |
|
249 |
SerialNumber serial_num; |
|
250 |
SerialNumber thread_serial_num; |
|
251 |
jint n_frames; |
|
252 |
JNIEnv *env; |
|
253 |
int i; |
|
254 |
char *phase_str; |
|
255 |
struct FrameNames { |
|
256 |
SerialNumber serial_num; |
|
257 |
char * sname; |
|
258 |
char * csig; |
|
259 |
char * mname; |
|
260 |
int lineno; |
|
261 |
} *finfo; |
|
262 |
||
263 |
info = (TraceInfo*)info_ptr; |
|
264 |
if ( info->status != 0 ) { |
|
265 |
return; |
|
266 |
} |
|
267 |
||
268 |
env = (JNIEnv*)arg; |
|
269 |
||
270 |
key = (TraceKey*)key_ptr; |
|
271 |
thread_serial_num = key->thread_serial_num; |
|
272 |
serial_num = info->serial_num; |
|
273 |
info->status = 1; |
|
274 |
finfo = NULL; |
|
275 |
||
276 |
n_frames = (jint)key->n_frames; |
|
277 |
if ( n_frames > 0 ) { |
|
278 |
finfo = (struct FrameNames *)HPROF_MALLOC(n_frames*(int)sizeof(struct FrameNames)); |
|
279 |
||
280 |
/* Write frames, but save information for trace later */ |
|
281 |
for (i = 0; i < n_frames; i++) { |
|
282 |
FrameIndex frame_index; |
|
283 |
char *msig; |
|
284 |
ClassIndex cnum; |
|
285 |
||
286 |
frame_index = key->frames[i]; |
|
287 |
get_frame_details(env, frame_index, &finfo[i].serial_num, |
|
288 |
&finfo[i].csig, &cnum, |
|
289 |
&finfo[i].mname, &msig, &finfo[i].sname, &finfo[i].lineno); |
|
290 |
||
291 |
if (frame_get_status(frame_index) == 0) { |
|
292 |
io_write_frame(frame_index, finfo[i].serial_num, |
|
293 |
finfo[i].mname, msig, |
|
294 |
finfo[i].sname, class_get_serial_number(cnum), |
|
295 |
finfo[i].lineno); |
|
296 |
frame_set_status(frame_index, 1); |
|
297 |
} |
|
298 |
jvmtiDeallocate(msig); |
|
299 |
} |
|
300 |
} |
|
301 |
||
302 |
/* Find phase string */ |
|
303 |
if ( key->phase == JVMTI_PHASE_LIVE ) { |
|
304 |
phase_str = NULL; /* Normal trace, no phase annotation */ |
|
305 |
} else { |
|
306 |
phase_str = phaseString(key->phase); |
|
307 |
} |
|
308 |
||
309 |
io_write_trace_header(serial_num, thread_serial_num, n_frames, phase_str); |
|
310 |
||
311 |
for (i = 0; i < n_frames; i++) { |
|
312 |
io_write_trace_elem(serial_num, key->frames[i], finfo[i].serial_num, |
|
313 |
finfo[i].csig, |
|
314 |
finfo[i].mname, finfo[i].sname, finfo[i].lineno); |
|
315 |
jvmtiDeallocate(finfo[i].csig); |
|
316 |
jvmtiDeallocate(finfo[i].mname); |
|
317 |
jvmtiDeallocate(finfo[i].sname); |
|
318 |
} |
|
319 |
||
320 |
io_write_trace_footer(serial_num, thread_serial_num, n_frames); |
|
321 |
||
322 |
if ( finfo != NULL ) { |
|
323 |
HPROF_FREE(finfo); |
|
324 |
} |
|
325 |
} |
|
326 |
||
327 |
/* Output a specific list of traces. */ |
|
328 |
static void |
|
329 |
output_list(JNIEnv *env, TraceIndex *list, jint count) |
|
330 |
{ |
|
331 |
rawMonitorEnter(gdata->data_access_lock); { |
|
332 |
int i; |
|
333 |
||
334 |
for ( i = 0; i < count ; i++ ) { |
|
335 |
TraceIndex index; |
|
336 |
TraceInfo *info; |
|
337 |
void * pkey; |
|
338 |
int key_len; |
|
339 |
||
340 |
index = list[i]; |
|
341 |
table_get_key(gdata->trace_table, index, &pkey, &key_len); |
|
342 |
info = get_info(index); |
|
343 |
output_trace(index, pkey, key_len, info, (void*)env); |
|
344 |
} |
|
345 |
} rawMonitorExit(gdata->data_access_lock); |
|
346 |
} |
|
347 |
||
348 |
static void |
|
349 |
collect_iterator(TableIndex index, void *key_ptr, int key_len, void *info_ptr, void *arg) |
|
350 |
{ |
|
351 |
TraceInfo *info; |
|
352 |
IterateInfo *iterate; |
|
353 |
||
354 |
HPROF_ASSERT(key_ptr!=NULL); |
|
355 |
HPROF_ASSERT(key_len>0); |
|
356 |
HPROF_ASSERT(arg!=NULL); |
|
357 |
HPROF_ASSERT(info_ptr!=NULL); |
|
358 |
iterate = (IterateInfo *)arg; |
|
359 |
info = (TraceInfo *)info_ptr; |
|
360 |
iterate->traces[iterate->count++] = index; |
|
361 |
iterate->grand_total_cost += info->self_cost; |
|
362 |
} |
|
363 |
||
364 |
static int |
|
365 |
qsort_compare_cost(const void *p_trace1, const void *p_trace2) |
|
366 |
{ |
|
367 |
TraceIndex trace1; |
|
368 |
TraceIndex trace2; |
|
369 |
TraceInfo * info1; |
|
370 |
TraceInfo * info2; |
|
371 |
||
372 |
HPROF_ASSERT(p_trace1!=NULL); |
|
373 |
HPROF_ASSERT(p_trace2!=NULL); |
|
374 |
trace1 = *(TraceIndex *)p_trace1; |
|
375 |
trace2 = *(TraceIndex *)p_trace2; |
|
376 |
info1 = get_info(trace1); |
|
377 |
info2 = get_info(trace2); |
|
378 |
/*LINTED*/ |
|
379 |
return (int)(info2->self_cost - info1->self_cost); |
|
380 |
} |
|
381 |
||
382 |
static int |
|
383 |
qsort_compare_num_hits(const void *p_trace1, const void *p_trace2) |
|
384 |
{ |
|
385 |
TraceIndex trace1; |
|
386 |
TraceIndex trace2; |
|
387 |
TraceInfo * info1; |
|
388 |
TraceInfo * info2; |
|
389 |
||
390 |
HPROF_ASSERT(p_trace1!=NULL); |
|
391 |
HPROF_ASSERT(p_trace2!=NULL); |
|
392 |
trace1 = *(TraceIndex *)p_trace1; |
|
393 |
trace2 = *(TraceIndex *)p_trace2; |
|
394 |
info1 = get_info(trace1); |
|
395 |
info2 = get_info(trace2); |
|
396 |
return info2->num_hits - info1->num_hits; |
|
397 |
} |
|
398 |
||
399 |
/* External interfaces. */ |
|
400 |
||
401 |
void |
|
402 |
trace_init(void) |
|
403 |
{ |
|
404 |
gdata->trace_table = table_initialize("Trace", |
|
405 |
256, 256, 511, (int)sizeof(TraceInfo)); |
|
406 |
} |
|
407 |
||
408 |
void |
|
409 |
trace_list(void) |
|
410 |
{ |
|
411 |
debug_message( |
|
412 |
"--------------------- Trace Table ------------------------\n"); |
|
413 |
table_walk_items(gdata->trace_table, &list_item, NULL); |
|
414 |
debug_message( |
|
415 |
"----------------------------------------------------------\n"); |
|
416 |
} |
|
417 |
||
418 |
void |
|
419 |
trace_cleanup(void) |
|
420 |
{ |
|
421 |
table_cleanup(gdata->trace_table, NULL, NULL); |
|
422 |
gdata->trace_table = NULL; |
|
423 |
} |
|
424 |
||
425 |
SerialNumber |
|
426 |
trace_get_serial_number(TraceIndex index) |
|
427 |
{ |
|
428 |
TraceInfo *info; |
|
429 |
||
430 |
if ( index == 0 ) { |
|
431 |
return 0; |
|
432 |
} |
|
433 |
info = get_info(index); |
|
434 |
return info->serial_num; |
|
435 |
} |
|
436 |
||
437 |
void |
|
438 |
trace_increment_cost(TraceIndex index, jint num_hits, jlong self_cost, jlong total_cost) |
|
439 |
{ |
|
440 |
TraceInfo *info; |
|
441 |
||
442 |
table_lock_enter(gdata->trace_table); { |
|
443 |
info = get_info(index); |
|
444 |
info->num_hits += num_hits; |
|
445 |
info->self_cost += self_cost; |
|
446 |
info->total_cost += total_cost; |
|
447 |
} table_lock_exit(gdata->trace_table); |
|
448 |
} |
|
449 |
||
450 |
TraceIndex |
|
451 |
trace_find_or_create(SerialNumber thread_serial_num, jint n_frames, FrameIndex *frames, jvmtiFrameInfo *jframes_buffer) |
|
452 |
{ |
|
453 |
return find_or_create(thread_serial_num, n_frames, frames, getPhase(), |
|
454 |
(TraceKey*)jframes_buffer); |
|
455 |
} |
|
456 |
||
457 |
/* We may need to ask for more frames than the user asked for */ |
|
458 |
static int |
|
459 |
get_real_depth(int depth, jboolean skip_init) |
|
460 |
{ |
|
461 |
int extra_frames; |
|
462 |
||
463 |
extra_frames = 0; |
|
464 |
/* This is only needed if we are doing BCI */ |
|
465 |
if ( gdata->bci && depth > 0 ) { |
|
466 |
/* Account for Java and native Tracker methods */ |
|
467 |
extra_frames = 2; |
|
468 |
if ( skip_init ) { |
|
469 |
/* Also allow for ignoring the java.lang.Object.<init> method */ |
|
470 |
extra_frames += 1; |
|
471 |
} |
|
472 |
} |
|
473 |
return depth + extra_frames; |
|
474 |
} |
|
475 |
||
476 |
/* Fill in FrameIndex array from jvmtiFrameInfo array, return n_frames */ |
|
477 |
static int |
|
478 |
fill_frame_buffer(int depth, int real_depth, |
|
479 |
int frame_count, jboolean skip_init, |
|
480 |
jvmtiFrameInfo *jframes_buffer, FrameIndex *frames_buffer) |
|
481 |
{ |
|
482 |
int n_frames; |
|
483 |
jint topframe; |
|
484 |
||
485 |
/* If real_depth is 0, just return 0 */ |
|
486 |
if ( real_depth == 0 ) { |
|
487 |
return 0; |
|
488 |
} |
|
489 |
||
490 |
/* Assume top frame index is 0 for now */ |
|
491 |
topframe = 0; |
|
492 |
||
493 |
/* Possible top frames belong to the hprof Tracker class, remove them */ |
|
494 |
if ( gdata->bci ) { |
|
495 |
while ( ( ( frame_count - topframe ) > 0 ) && |
|
496 |
( topframe < (real_depth-depth) ) && |
|
497 |
( tracker_method(jframes_buffer[topframe].method) || |
|
498 |
( skip_init |
|
499 |
&& jframes_buffer[topframe].method==gdata->object_init_method ) ) |
|
500 |
) { |
|
501 |
topframe++; |
|
502 |
} |
|
503 |
} |
|
504 |
||
505 |
/* Adjust count to match depth request */ |
|
506 |
if ( ( frame_count - topframe ) > depth ) { |
|
507 |
frame_count = depth + topframe; |
|
508 |
} |
|
509 |
||
510 |
/* The actual frame count we will process */ |
|
511 |
n_frames = frame_count - topframe; |
|
512 |
if ( n_frames > 0 ) { |
|
513 |
int i; |
|
514 |
||
515 |
for (i = 0; i < n_frames; i++) { |
|
516 |
jmethodID method; |
|
517 |
jlocation location; |
|
518 |
||
519 |
method = jframes_buffer[i+topframe].method; |
|
520 |
location = jframes_buffer[i+topframe].location; |
|
521 |
frames_buffer[i] = frame_find_or_create(method, location); |
|
522 |
} |
|
523 |
} |
|
524 |
return n_frames; |
|
525 |
} |
|
526 |
||
527 |
/* Get the trace for the supplied thread */ |
|
528 |
TraceIndex |
|
529 |
trace_get_current(jthread thread, SerialNumber thread_serial_num, |
|
530 |
int depth, jboolean skip_init, |
|
531 |
FrameIndex *frames_buffer, |
|
532 |
jvmtiFrameInfo *jframes_buffer) |
|
533 |
{ |
|
534 |
TraceIndex index; |
|
535 |
jint frame_count; |
|
536 |
int real_depth; |
|
537 |
int n_frames; |
|
538 |
||
539 |
HPROF_ASSERT(thread!=NULL); |
|
540 |
HPROF_ASSERT(frames_buffer!=NULL); |
|
541 |
HPROF_ASSERT(jframes_buffer!=NULL); |
|
542 |
||
543 |
/* We may need to ask for more frames than the user asked for */ |
|
544 |
real_depth = get_real_depth(depth, skip_init); |
|
545 |
||
546 |
/* Get the stack trace for this one thread */ |
|
547 |
frame_count = 0; |
|
548 |
if ( real_depth > 0 ) { |
|
549 |
getStackTrace(thread, jframes_buffer, real_depth, &frame_count); |
|
550 |
} |
|
551 |
||
552 |
/* Create FrameIndex's */ |
|
553 |
n_frames = fill_frame_buffer(depth, real_depth, frame_count, skip_init, |
|
554 |
jframes_buffer, frames_buffer); |
|
555 |
||
556 |
/* Lookup or create new TraceIndex */ |
|
557 |
index = find_or_create(thread_serial_num, n_frames, frames_buffer, |
|
558 |
getPhase(), (TraceKey*)jframes_buffer); |
|
559 |
return index; |
|
560 |
} |
|
561 |
||
562 |
/* Get traces for all threads in list (traces[i]==0 if thread not running) */ |
|
563 |
void |
|
564 |
trace_get_all_current(jint thread_count, jthread *threads, |
|
565 |
SerialNumber *thread_serial_nums, |
|
566 |
int depth, jboolean skip_init, |
|
567 |
TraceIndex *traces, jboolean always_care) |
|
568 |
{ |
|
569 |
jvmtiStackInfo *stack_info; |
|
570 |
int nbytes; |
|
571 |
int real_depth; |
|
572 |
int i; |
|
573 |
FrameIndex *frames_buffer; |
|
574 |
TraceKey *trace_key_buffer; |
|
575 |
jvmtiPhase phase; |
|
576 |
||
577 |
HPROF_ASSERT(threads!=NULL); |
|
578 |
HPROF_ASSERT(thread_serial_nums!=NULL); |
|
579 |
HPROF_ASSERT(traces!=NULL); |
|
580 |
HPROF_ASSERT(thread_count > 0); |
|
581 |
||
582 |
/* Find out what the phase is for all these traces */ |
|
583 |
phase = getPhase(); |
|
584 |
||
585 |
/* We may need to ask for more frames than the user asked for */ |
|
586 |
real_depth = get_real_depth(depth, skip_init); |
|
587 |
||
588 |
/* Get the stack traces for all the threads */ |
|
589 |
getThreadListStackTraces(thread_count, threads, real_depth, &stack_info); |
|
590 |
||
591 |
/* Allocate a frames_buffer and trace key buffer */ |
|
592 |
nbytes = (int)sizeof(FrameIndex)*real_depth; |
|
593 |
frames_buffer = (FrameIndex*)HPROF_MALLOC(nbytes); |
|
594 |
nbytes += (int)sizeof(TraceKey); |
|
595 |
trace_key_buffer = (TraceKey*)HPROF_MALLOC(nbytes); |
|
596 |
||
597 |
/* Loop over the stack traces we have for these 'thread_count' threads */ |
|
598 |
for ( i = 0 ; i < thread_count ; i++ ) { |
|
599 |
int n_frames; |
|
600 |
||
601 |
/* Assume 0 at first (no trace) */ |
|
602 |
traces[i] = 0; |
|
603 |
||
604 |
/* If thread has frames, is runnable, and isn't suspended, we care */ |
|
605 |
if ( always_care || |
|
606 |
( stack_info[i].frame_count > 0 |
|
607 |
&& (stack_info[i].state & JVMTI_THREAD_STATE_RUNNABLE)!=0 |
|
608 |
&& (stack_info[i].state & JVMTI_THREAD_STATE_SUSPENDED)==0 |
|
609 |
&& (stack_info[i].state & JVMTI_THREAD_STATE_INTERRUPTED)==0 ) |
|
610 |
) { |
|
611 |
||
612 |
/* Create FrameIndex's */ |
|
613 |
n_frames = fill_frame_buffer(depth, real_depth, |
|
614 |
stack_info[i].frame_count, |
|
615 |
skip_init, |
|
616 |
stack_info[i].frame_buffer, |
|
617 |
frames_buffer); |
|
618 |
||
619 |
/* Lookup or create new TraceIndex */ |
|
620 |
traces[i] = find_or_create(thread_serial_nums[i], |
|
621 |
n_frames, frames_buffer, phase, trace_key_buffer); |
|
622 |
} |
|
623 |
} |
|
624 |
||
625 |
/* Make sure we free the space */ |
|
626 |
HPROF_FREE(frames_buffer); |
|
627 |
HPROF_FREE(trace_key_buffer); |
|
628 |
jvmtiDeallocate(stack_info); |
|
629 |
} |
|
630 |
||
631 |
/* Increment the trace costs for all the threads (for cpu=samples) */ |
|
632 |
void |
|
633 |
trace_increment_all_sample_costs(jint thread_count, jthread *threads, |
|
634 |
SerialNumber *thread_serial_nums, |
|
635 |
int depth, jboolean skip_init) |
|
636 |
{ |
|
637 |
TraceIndex *traces; |
|
638 |
int nbytes; |
|
639 |
||
640 |
HPROF_ASSERT(threads!=NULL); |
|
641 |
HPROF_ASSERT(thread_serial_nums!=NULL); |
|
642 |
HPROF_ASSERT(thread_count > 0); |
|
643 |
HPROF_ASSERT(depth >= 0); |
|
644 |
||
645 |
if ( depth == 0 ) { |
|
646 |
return; |
|
647 |
} |
|
648 |
||
649 |
/* Allocate a traces array */ |
|
650 |
nbytes = (int)sizeof(TraceIndex)*thread_count; |
|
651 |
traces = (TraceIndex*)HPROF_MALLOC(nbytes); |
|
652 |
||
653 |
/* Get all the current traces for these threads */ |
|
654 |
trace_get_all_current(thread_count, threads, thread_serial_nums, |
|
655 |
depth, skip_init, traces, JNI_FALSE); |
|
656 |
||
657 |
/* Increment the cpu=samples cost on these traces */ |
|
658 |
table_lock_enter(gdata->trace_table); { |
|
659 |
int i; |
|
660 |
||
661 |
for ( i = 0 ; i < thread_count ; i++ ) { |
|
662 |
/* Each trace gets a hit and an increment of it's total cost */ |
|
663 |
if ( traces[i] != 0 ) { |
|
664 |
TraceInfo *info; |
|
665 |
||
666 |
info = get_info(traces[i]); |
|
667 |
info->num_hits += 1; |
|
668 |
info->self_cost += (jlong)1; |
|
669 |
info->total_cost += (jlong)1; |
|
670 |
} |
|
671 |
} |
|
672 |
} table_lock_exit(gdata->trace_table); |
|
673 |
||
674 |
/* Free up the memory allocated */ |
|
675 |
HPROF_FREE(traces); |
|
676 |
} |
|
677 |
||
678 |
void |
|
679 |
trace_output_unmarked(JNIEnv *env) |
|
680 |
{ |
|
681 |
rawMonitorEnter(gdata->data_access_lock); { |
|
682 |
table_walk_items(gdata->trace_table, &output_trace, (void*)env); |
|
683 |
} rawMonitorExit(gdata->data_access_lock); |
|
684 |
} |
|
685 |
||
686 |
/* output info on the cost associated with traces */ |
|
687 |
void |
|
688 |
trace_output_cost(JNIEnv *env, double cutoff) |
|
689 |
{ |
|
690 |
IterateInfo iterate; |
|
691 |
int i, trace_table_size, n_items; |
|
692 |
double accum; |
|
693 |
int n_entries; |
|
694 |
||
695 |
rawMonitorEnter(gdata->data_access_lock); { |
|
696 |
||
697 |
n_entries = table_element_count(gdata->trace_table); |
|
698 |
iterate.traces = HPROF_MALLOC(n_entries*(int)sizeof(TraceIndex)+1); |
|
699 |
iterate.count = 0; |
|
700 |
iterate.grand_total_cost = 0; |
|
701 |
table_walk_items(gdata->trace_table, &collect_iterator, &iterate); |
|
702 |
||
703 |
trace_table_size = iterate.count; |
|
704 |
||
705 |
/* sort all the traces according to the cost */ |
|
706 |
qsort(iterate.traces, trace_table_size, sizeof(TraceIndex), |
|
707 |
&qsort_compare_cost); |
|
708 |
||
709 |
n_items = 0; |
|
710 |
for (i = 0; i < trace_table_size; i++) { |
|
711 |
TraceInfo *info; |
|
712 |
TraceIndex trace_index; |
|
713 |
double percent; |
|
714 |
||
715 |
trace_index = iterate.traces[i]; |
|
716 |
info = get_info(trace_index); |
|
717 |
/* As soon as a trace with zero hits is seen, we need no others */ |
|
718 |
if (info->num_hits == 0 ) { |
|
719 |
break; |
|
720 |
} |
|
721 |
percent = (double)info->self_cost / (double)iterate.grand_total_cost; |
|
722 |
if (percent < cutoff) { |
|
723 |
break; |
|
724 |
} |
|
725 |
n_items++; |
|
726 |
} |
|
727 |
||
728 |
/* Now write all trace we might refer to. */ |
|
729 |
output_list(env, iterate.traces, n_items); |
|
730 |
||
731 |
io_write_cpu_samples_header(iterate.grand_total_cost, n_items); |
|
732 |
||
733 |
accum = 0; |
|
734 |
||
735 |
for (i = 0; i < n_items; i++) { |
|
736 |
SerialNumber frame_serial_num; |
|
737 |
TraceInfo *info; |
|
738 |
TraceKey *key; |
|
739 |
TraceIndex trace_index; |
|
740 |
double percent; |
|
741 |
char *csig; |
|
742 |
char *mname; |
|
743 |
char *msig; |
|
744 |
||
745 |
trace_index = iterate.traces[i]; |
|
746 |
info = get_info(trace_index); |
|
747 |
key = get_pkey(trace_index); |
|
748 |
percent = ((double)info->self_cost / (double)iterate.grand_total_cost) * 100.0; |
|
749 |
accum += percent; |
|
750 |
||
751 |
csig = NULL; |
|
752 |
mname = NULL; |
|
753 |
msig = NULL; |
|
754 |
||
755 |
if (key->n_frames > 0) { |
|
756 |
get_frame_details(env, key->frames[0], &frame_serial_num, |
|
757 |
&csig, NULL, &mname, &msig, NULL, NULL); |
|
758 |
} |
|
759 |
||
760 |
io_write_cpu_samples_elem(i+1, percent, accum, info->num_hits, |
|
761 |
(jint)info->self_cost, info->serial_num, |
|
762 |
key->n_frames, csig, mname); |
|
763 |
||
764 |
jvmtiDeallocate(csig); |
|
765 |
jvmtiDeallocate(mname); |
|
766 |
jvmtiDeallocate(msig); |
|
767 |
} |
|
768 |
||
769 |
io_write_cpu_samples_footer(); |
|
770 |
||
771 |
HPROF_FREE(iterate.traces); |
|
772 |
||
773 |
} rawMonitorExit(gdata->data_access_lock); |
|
774 |
||
775 |
} |
|
776 |
||
777 |
/* output the trace cost in old prof format */ |
|
778 |
void |
|
779 |
trace_output_cost_in_prof_format(JNIEnv *env) |
|
780 |
{ |
|
781 |
IterateInfo iterate; |
|
782 |
int i, trace_table_size; |
|
783 |
int n_entries; |
|
784 |
||
785 |
rawMonitorEnter(gdata->data_access_lock); { |
|
786 |
||
787 |
n_entries = table_element_count(gdata->trace_table); |
|
788 |
iterate.traces = HPROF_MALLOC(n_entries*(int)sizeof(TraceIndex)+1); |
|
789 |
iterate.count = 0; |
|
790 |
iterate.grand_total_cost = 0; |
|
791 |
table_walk_items(gdata->trace_table, &collect_iterator, &iterate); |
|
792 |
||
793 |
trace_table_size = iterate.count; |
|
794 |
||
795 |
/* sort all the traces according to the number of hits */ |
|
796 |
qsort(iterate.traces, trace_table_size, sizeof(TraceIndex), |
|
797 |
&qsort_compare_num_hits); |
|
798 |
||
799 |
io_write_oldprof_header(); |
|
800 |
||
801 |
for (i = 0; i < trace_table_size; i++) { |
|
802 |
SerialNumber frame_serial_num; |
|
803 |
TraceInfo *info; |
|
804 |
TraceKey *key; |
|
805 |
TraceIndex trace_index; |
|
806 |
int num_frames; |
|
807 |
int num_hits; |
|
808 |
char *csig_callee; |
|
809 |
char *mname_callee; |
|
810 |
char *msig_callee; |
|
811 |
char *csig_caller; |
|
812 |
char *mname_caller; |
|
813 |
char *msig_caller; |
|
814 |
||
815 |
trace_index = iterate.traces[i]; |
|
816 |
key = get_pkey(trace_index); |
|
817 |
info = get_info(trace_index); |
|
818 |
num_hits = info->num_hits; |
|
819 |
||
820 |
if (num_hits == 0) { |
|
821 |
break; |
|
822 |
} |
|
823 |
||
824 |
csig_callee = NULL; |
|
825 |
mname_callee = NULL; |
|
826 |
msig_callee = NULL; |
|
827 |
csig_caller = NULL; |
|
828 |
mname_caller = NULL; |
|
829 |
msig_caller = NULL; |
|
830 |
||
831 |
num_frames = (int)key->n_frames; |
|
832 |
||
833 |
if (num_frames >= 1) { |
|
834 |
get_frame_details(env, key->frames[0], &frame_serial_num, |
|
835 |
&csig_callee, NULL, |
|
836 |
&mname_callee, &msig_callee, NULL, NULL); |
|
837 |
} |
|
838 |
||
839 |
if (num_frames > 1) { |
|
840 |
get_frame_details(env, key->frames[1], &frame_serial_num, |
|
841 |
&csig_caller, NULL, |
|
842 |
&mname_caller, &msig_caller, NULL, NULL); |
|
843 |
} |
|
844 |
||
845 |
io_write_oldprof_elem(info->num_hits, num_frames, |
|
846 |
csig_callee, mname_callee, msig_callee, |
|
847 |
csig_caller, mname_caller, msig_caller, |
|
848 |
(int)info->total_cost); |
|
849 |
||
850 |
jvmtiDeallocate(csig_callee); |
|
851 |
jvmtiDeallocate(mname_callee); |
|
852 |
jvmtiDeallocate(msig_callee); |
|
853 |
jvmtiDeallocate(csig_caller); |
|
854 |
jvmtiDeallocate(mname_caller); |
|
855 |
jvmtiDeallocate(msig_caller); |
|
856 |
} |
|
857 |
||
858 |
io_write_oldprof_footer(); |
|
859 |
||
860 |
HPROF_FREE(iterate.traces); |
|
861 |
||
862 |
} rawMonitorExit(gdata->data_access_lock); |
|
863 |
} |
|
864 |
||
865 |
void |
|
866 |
trace_clear_cost(void) |
|
867 |
{ |
|
868 |
table_walk_items(gdata->trace_table, &clear_cost, NULL); |
|
869 |
} |