author | sla |
Tue, 26 Aug 2014 07:55:08 +0200 | |
changeset 26201 | 40a873d21081 |
parent 25859 | jdk/src/demo/share/jvmti/hprof/hprof_init.c@3317bb8137f4 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
24967 | 2 |
* Copyright (c) 2003, 2014, 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 |
/* Main source file, the basic JVMTI connection/startup code. */ |
42 |
||
43 |
#include "hprof.h" |
|
44 |
||
45 |
#include "java_crw_demo.h" |
|
46 |
||
47 |
/* |
|
48 |
* This file contains all the startup logic (Agent_Onload) and |
|
49 |
* connection to the JVMTI interface. |
|
50 |
* All JVMTI Event callbacks are in this file. |
|
51 |
* All setting of global data (gdata) is done here. |
|
52 |
* Options are parsed here. |
|
53 |
* Option help messages are here. |
|
54 |
* Termination handled here (VM_DEATH) and shutdown (Agent_OnUnload). |
|
55 |
* Spawning of the cpu sample loop thread and listener thread is done here. |
|
56 |
* |
|
57 |
* Use of private 'static' data has been limited, most shared static data |
|
58 |
* should be found in the GlobalData structure pointed to by gdata |
|
59 |
* (see hprof.h). |
|
60 |
* |
|
61 |
*/ |
|
62 |
||
63 |
/* The default output filenames. */ |
|
64 |
||
65 |
#define DEFAULT_TXT_SUFFIX ".txt" |
|
66 |
#define DEFAULT_OUTPUTFILE "java.hprof" |
|
67 |
#define DEFAULT_OUTPUTTEMP "java.hprof.temp" |
|
68 |
||
69 |
/* The only global variable, defined by this library */ |
|
70 |
GlobalData *gdata; |
|
71 |
||
72 |
/* Experimental options */ |
|
73 |
#define EXPERIMENT_NO_EARLY_HOOK 0x1 |
|
74 |
||
75 |
/* Default trace depth */ |
|
76 |
#define DEFAULT_TRACE_DEPTH 4 |
|
77 |
||
78 |
/* Default sample interval */ |
|
79 |
#define DEFAULT_SAMPLE_INTERVAL 10 |
|
80 |
||
81 |
/* Default cutoff */ |
|
82 |
#define DEFAULT_CUTOFF_POINT 0.0001 |
|
83 |
||
84 |
/* Stringize macros for help. */ |
|
85 |
#define _TO_STR(a) #a |
|
86 |
#define TO_STR(a) _TO_STR(a) |
|
87 |
||
88 |
/* Macros to surround callback code (non-VM_DEATH callbacks). |
|
89 |
* Note that this just keeps a count of the non-VM_DEATH callbacks that |
|
90 |
* are currently active, it does not prevent these callbacks from |
|
91 |
* operating in parallel. It's the VM_DEATH callback that will wait |
|
92 |
* for all these callbacks to either complete and block, or just block. |
|
93 |
* We need to hold back these threads so they don't die during the final |
|
94 |
* VM_DEATH processing. |
|
95 |
* If the VM_DEATH callback is active in the beginning, then this callback |
|
96 |
* just blocks to prevent further execution of the thread. |
|
97 |
* If the VM_DEATH callback is active at the end, then this callback |
|
98 |
* will notify the VM_DEATH callback if it's the last one. |
|
99 |
* In all cases, the last thing they do is Enter/Exit the monitor |
|
100 |
* gdata->callbackBlock, which will block this callback if VM_DEATH |
|
101 |
* is running. |
|
102 |
* |
|
103 |
* WARNING: No not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK |
|
104 |
* block, this will mess up the count. |
|
105 |
*/ |
|
106 |
||
107 |
#define BEGIN_CALLBACK() \ |
|
108 |
{ /* BEGIN OF CALLBACK */ \ |
|
109 |
jboolean bypass; \ |
|
110 |
rawMonitorEnter(gdata->callbackLock); \ |
|
111 |
if (gdata->vm_death_callback_active) { \ |
|
112 |
/* VM_DEATH is active, we will bypass the CALLBACK CODE */ \ |
|
113 |
bypass = JNI_TRUE; \ |
|
114 |
rawMonitorExit(gdata->callbackLock); \ |
|
115 |
/* Bypassed CALLBACKS block here until VM_DEATH done */ \ |
|
116 |
rawMonitorEnter(gdata->callbackBlock); \ |
|
117 |
rawMonitorExit(gdata->callbackBlock); \ |
|
118 |
} else { \ |
|
119 |
/* We will be executing the CALLBACK CODE in this case */ \ |
|
120 |
gdata->active_callbacks++; \ |
|
121 |
bypass = JNI_FALSE; \ |
|
122 |
rawMonitorExit(gdata->callbackLock); \ |
|
123 |
} \ |
|
124 |
if ( !bypass ) { \ |
|
125 |
/* BODY OF CALLBACK CODE (with no callback locks held) */ |
|
126 |
||
127 |
#define END_CALLBACK() /* Part of bypass if body */ \ |
|
128 |
rawMonitorEnter(gdata->callbackLock); \ |
|
129 |
gdata->active_callbacks--; \ |
|
130 |
/* If VM_DEATH is active, and last one, send notify. */ \ |
|
131 |
if (gdata->vm_death_callback_active) { \ |
|
132 |
if (gdata->active_callbacks == 0) { \ |
|
133 |
rawMonitorNotifyAll(gdata->callbackLock); \ |
|
134 |
} \ |
|
135 |
} \ |
|
136 |
rawMonitorExit(gdata->callbackLock); \ |
|
137 |
/* Non-Bypassed CALLBACKS block here until VM_DEATH done */ \ |
|
138 |
rawMonitorEnter(gdata->callbackBlock); \ |
|
139 |
rawMonitorExit(gdata->callbackBlock); \ |
|
140 |
} \ |
|
141 |
} /* END OF CALLBACK */ |
|
142 |
||
143 |
/* Forward declarations */ |
|
144 |
static void set_callbacks(jboolean on); |
|
145 |
||
146 |
/* ------------------------------------------------------------------- */ |
|
147 |
/* Global data initialization */ |
|
148 |
||
149 |
/* Get initialized global data area */ |
|
150 |
static GlobalData * |
|
151 |
get_gdata(void) |
|
152 |
{ |
|
153 |
static GlobalData data; |
|
154 |
||
155 |
/* Create initial default values */ |
|
156 |
(void)memset(&data, 0, sizeof(GlobalData)); |
|
157 |
||
158 |
data.fd = -1; /* Non-zero file or socket. */ |
|
159 |
data.heap_fd = -1; /* For heap=dump, see hprof_io */ |
|
160 |
data.check_fd = -1; /* For heap=dump, see hprof_io */ |
|
161 |
data.max_trace_depth = DEFAULT_TRACE_DEPTH; |
|
162 |
data.prof_trace_depth = DEFAULT_TRACE_DEPTH; |
|
163 |
data.sample_interval = DEFAULT_SAMPLE_INTERVAL; |
|
164 |
data.lineno_in_traces = JNI_TRUE; |
|
165 |
data.output_format = 'a'; /* 'b' for binary */ |
|
166 |
data.cutoff_point = DEFAULT_CUTOFF_POINT; |
|
167 |
data.dump_on_exit = JNI_TRUE; |
|
168 |
data.gc_start_time = -1L; |
|
169 |
#ifdef DEBUG |
|
170 |
data.debug = JNI_TRUE; |
|
171 |
data.coredump = JNI_TRUE; |
|
172 |
#endif |
|
173 |
data.micro_state_accounting = JNI_FALSE; |
|
174 |
data.force_output = JNI_TRUE; |
|
175 |
data.verbose = JNI_TRUE; |
|
176 |
data.primfields = JNI_TRUE; |
|
177 |
data.primarrays = JNI_TRUE; |
|
178 |
||
179 |
data.table_serial_number_start = 1; |
|
180 |
data.class_serial_number_start = 100000; |
|
181 |
data.thread_serial_number_start = 200000; |
|
182 |
data.trace_serial_number_start = 300000; |
|
183 |
data.object_serial_number_start = 400000; |
|
184 |
data.frame_serial_number_start = 500000; |
|
185 |
data.gref_serial_number_start = 1; |
|
186 |
||
187 |
data.table_serial_number_counter = data.table_serial_number_start; |
|
188 |
data.class_serial_number_counter = data.class_serial_number_start; |
|
189 |
data.thread_serial_number_counter = data.thread_serial_number_start; |
|
190 |
data.trace_serial_number_counter = data.trace_serial_number_start; |
|
191 |
data.object_serial_number_counter = data.object_serial_number_start; |
|
192 |
data.frame_serial_number_counter = data.frame_serial_number_start; |
|
193 |
data.gref_serial_number_counter = data.gref_serial_number_start; |
|
194 |
||
195 |
data.unknown_thread_serial_num = data.thread_serial_number_counter++; |
|
196 |
return &data; |
|
197 |
} |
|
198 |
||
199 |
/* ------------------------------------------------------------------- */ |
|
200 |
/* Error handler callback for the java_crw_demo (classfile read write) functions. */ |
|
201 |
||
202 |
static void |
|
203 |
my_crw_fatal_error_handler(const char * msg, const char *file, int line) |
|
204 |
{ |
|
205 |
char errmsg[256]; |
|
206 |
||
207 |
(void)md_snprintf(errmsg, sizeof(errmsg), |
|
208 |
"%s [%s:%d]", msg, file, line); |
|
209 |
errmsg[sizeof(errmsg)-1] = 0; |
|
210 |
HPROF_ERROR(JNI_TRUE, errmsg); |
|
211 |
} |
|
212 |
||
213 |
static void |
|
214 |
list_all_tables(void) |
|
215 |
{ |
|
216 |
string_list(); |
|
217 |
class_list(); |
|
218 |
frame_list(); |
|
219 |
site_list(); |
|
220 |
object_list(); |
|
221 |
trace_list(); |
|
222 |
monitor_list(); |
|
223 |
tls_list(); |
|
224 |
loader_list(); |
|
225 |
} |
|
226 |
||
227 |
/* ------------------------------------------------------------------- */ |
|
228 |
/* Option Parsing support */ |
|
229 |
||
230 |
/** |
|
231 |
* Socket connection |
|
232 |
*/ |
|
233 |
||
234 |
/* |
|
235 |
* Return a socket connect()ed to a "hostname" that is |
|
236 |
* accept()ing heap profile data on "port." Return a value <= 0 if |
|
237 |
* such a connection can't be made. |
|
238 |
*/ |
|
239 |
static int |
|
23056 | 240 |
connect_to_socket(char *hostname, int port) |
2 | 241 |
{ |
242 |
int fd; |
|
243 |
||
244 |
if (port == 0 || port > 65535) { |
|
245 |
HPROF_ERROR(JNI_FALSE, "invalid port number"); |
|
246 |
return -1; |
|
247 |
} |
|
248 |
if (hostname == NULL) { |
|
249 |
HPROF_ERROR(JNI_FALSE, "hostname is NULL"); |
|
250 |
return -1; |
|
251 |
} |
|
252 |
||
253 |
/* create a socket */ |
|
23056 | 254 |
fd = md_connect(hostname, (unsigned short)port); |
2 | 255 |
return fd; |
256 |
} |
|
257 |
||
258 |
/* Accept a filename, and adjust the name so that it is unique for this PID */ |
|
259 |
static void |
|
260 |
make_unique_filename(char **filename) |
|
261 |
{ |
|
262 |
int fd; |
|
263 |
||
264 |
/* Find a file that doesn't exist */ |
|
265 |
fd = md_open(*filename); |
|
266 |
if ( fd >= 0 ) { |
|
267 |
int pid; |
|
268 |
char *new_name; |
|
269 |
char *old_name; |
|
270 |
char *prefix; |
|
271 |
char suffix[5]; |
|
272 |
int new_len; |
|
273 |
||
274 |
/* Close the file. */ |
|
275 |
md_close(fd); |
|
276 |
||
277 |
/* Make filename name.PID[.txt] */ |
|
278 |
pid = md_getpid(); |
|
279 |
old_name = *filename; |
|
280 |
new_len = (int)strlen(old_name)+64; |
|
281 |
new_name = HPROF_MALLOC(new_len); |
|
282 |
prefix = old_name; |
|
283 |
suffix[0] = 0; |
|
284 |
||
285 |
/* Look for .txt suffix if not binary output */ |
|
286 |
if (gdata->output_format != 'b') { |
|
287 |
char *dot; |
|
288 |
char *format_suffix; |
|
289 |
||
290 |
format_suffix = DEFAULT_TXT_SUFFIX; |
|
291 |
||
292 |
(void)strcpy(suffix, format_suffix); |
|
293 |
||
294 |
dot = strrchr(old_name, '.'); |
|
295 |
if ( dot != NULL ) { |
|
296 |
int i; |
|
297 |
int slen; |
|
298 |
int match; |
|
299 |
||
300 |
slen = (int)strlen(format_suffix); |
|
301 |
match = 1; |
|
302 |
for ( i = 0; i < slen; i++ ) { |
|
303 |
if ( dot[i]==0 || |
|
304 |
tolower(format_suffix[i]) != tolower(dot[i]) ) { |
|
305 |
match = 0; |
|
306 |
break; |
|
307 |
} |
|
308 |
} |
|
309 |
if ( match ) { |
|
310 |
(void)strcpy(suffix, dot); |
|
311 |
*dot = 0; /* truncates prefix and old_name */ |
|
312 |
} |
|
313 |
} |
|
314 |
} |
|
315 |
||
316 |
/* Construct the name */ |
|
317 |
(void)md_snprintf(new_name, new_len, |
|
318 |
"%s.%d%s", prefix, pid, suffix); |
|
319 |
*filename = new_name; |
|
320 |
HPROF_FREE(old_name); |
|
321 |
||
322 |
/* Odds are with Windows, this file may not be so unique. */ |
|
323 |
(void)remove(gdata->output_filename); |
|
324 |
} |
|
325 |
} |
|
326 |
||
327 |
static int |
|
328 |
get_tok(char **src, char *buf, int buflen, int sep) |
|
329 |
{ |
|
330 |
int len; |
|
331 |
char *p; |
|
332 |
||
333 |
buf[0] = 0; |
|
334 |
if ( **src == 0 ) { |
|
335 |
return 0; |
|
336 |
} |
|
337 |
p = strchr(*src, sep); |
|
338 |
if ( p==NULL ) { |
|
339 |
len = (int)strlen(*src); |
|
340 |
p = (*src) + len; |
|
341 |
} else { |
|
342 |
/*LINTED*/ |
|
343 |
len = (int)(p - (*src)); |
|
344 |
} |
|
345 |
if ( (len+1) > buflen ) { |
|
346 |
return 0; |
|
347 |
} |
|
348 |
(void)memcpy(buf, *src, len); |
|
349 |
buf[len] = 0; |
|
350 |
if ( *p != 0 && *p == sep ) { |
|
351 |
(*src) = p+1; |
|
352 |
} else { |
|
353 |
(*src) = p; |
|
354 |
} |
|
355 |
return len; |
|
356 |
} |
|
357 |
||
358 |
static jboolean |
|
359 |
setBinarySwitch(char **src, jboolean *ptr) |
|
360 |
{ |
|
361 |
char buf[80]; |
|
362 |
||
363 |
if (!get_tok(src, buf, (int)sizeof(buf), ',')) { |
|
364 |
return JNI_FALSE; |
|
365 |
} |
|
366 |
if (strcmp(buf, "y") == 0) { |
|
367 |
*ptr = JNI_TRUE; |
|
368 |
} else if (strcmp(buf, "n") == 0) { |
|
369 |
*ptr = JNI_FALSE; |
|
370 |
} else { |
|
371 |
return JNI_FALSE; |
|
372 |
} |
|
373 |
return JNI_TRUE; |
|
374 |
} |
|
375 |
||
376 |
static void |
|
377 |
print_usage(void) |
|
378 |
{ |
|
379 |
||
380 |
(void)fprintf(stdout, |
|
381 |
"\n" |
|
382 |
" HPROF: Heap and CPU Profiling Agent (JVMTI Demonstration Code)\n" |
|
383 |
"\n" |
|
384 |
AGENTNAME " usage: java " AGENTLIB "=[help]|[<option>=<value>, ...]\n" |
|
385 |
"\n" |
|
386 |
"Option Name and Value Description Default\n" |
|
387 |
"--------------------- ----------- -------\n" |
|
388 |
"heap=dump|sites|all heap profiling all\n" |
|
389 |
"cpu=samples|times|old CPU usage off\n" |
|
390 |
"monitor=y|n monitor contention n\n" |
|
391 |
"format=a|b text(txt) or binary output a\n" |
|
392 |
"file=<file> write data to file " DEFAULT_OUTPUTFILE "[{" DEFAULT_TXT_SUFFIX "}]\n" |
|
393 |
"net=<host>:<port> send data over a socket off\n" |
|
394 |
"depth=<size> stack trace depth " TO_STR(DEFAULT_TRACE_DEPTH) "\n" |
|
395 |
"interval=<ms> sample interval in ms " TO_STR(DEFAULT_SAMPLE_INTERVAL) "\n" |
|
396 |
"cutoff=<value> output cutoff point " TO_STR(DEFAULT_CUTOFF_POINT) "\n" |
|
397 |
"lineno=y|n line number in traces? y\n" |
|
398 |
"thread=y|n thread in traces? n\n" |
|
399 |
"doe=y|n dump on exit? y\n" |
|
400 |
"msa=y|n Solaris micro state accounting n\n" |
|
401 |
"force=y|n force output to <file> y\n" |
|
402 |
"verbose=y|n print messages about dumps y\n" |
|
403 |
"\n" |
|
404 |
"Obsolete Options\n" |
|
405 |
"----------------\n" |
|
406 |
"gc_okay=y|n\n" |
|
407 |
||
408 |
#ifdef DEBUG |
|
409 |
"\n" |
|
410 |
"DEBUG Option Description Default\n" |
|
411 |
"------------ ----------- -------\n" |
|
412 |
"primfields=y|n include primitive field values y\n" |
|
413 |
"primarrays=y|n include primitive array values y\n" |
|
414 |
"debugflags=MASK Various debug flags 0\n" |
|
415 |
" 0x01 Report refs in and of unprepared classes\n" |
|
416 |
"logflags=MASK Logging to stderr 0\n" |
|
417 |
" " TO_STR(LOG_DUMP_MISC) " Misc logging\n" |
|
418 |
" " TO_STR(LOG_DUMP_LISTS) " Dump out the tables\n" |
|
419 |
" " TO_STR(LOG_CHECK_BINARY) " Verify & dump format=b\n" |
|
420 |
"coredump=y|n Core dump on fatal n\n" |
|
421 |
"errorexit=y|n Exit on any error n\n" |
|
422 |
"pause=y|n Pause on onload & echo PID n\n" |
|
423 |
"debug=y|n Turn on all debug checking n\n" |
|
424 |
"X=MASK Internal use only 0\n" |
|
425 |
||
426 |
"\n" |
|
427 |
"Environment Variables\n" |
|
428 |
"---------------------\n" |
|
429 |
"_JAVA_HPROF_OPTIONS\n" |
|
430 |
" Options can be added externally via this environment variable.\n" |
|
431 |
" Anything contained in it will get a comma prepended to it (if needed),\n" |
|
432 |
" then it will be added to the end of the options supplied via the\n" |
|
433 |
" " XRUN " or " AGENTLIB " command line option.\n" |
|
434 |
||
435 |
#endif |
|
436 |
||
437 |
"\n" |
|
438 |
"Examples\n" |
|
439 |
"--------\n" |
|
440 |
" - Get sample cpu information every 20 millisec, with a stack depth of 3:\n" |
|
441 |
" java " AGENTLIB "=cpu=samples,interval=20,depth=3 classname\n" |
|
442 |
" - Get heap usage information based on the allocation sites:\n" |
|
443 |
" java " AGENTLIB "=heap=sites classname\n" |
|
444 |
||
445 |
#ifdef DEBUG |
|
446 |
" - Using the external option addition with csh, log details on all runs:\n" |
|
447 |
" setenv _JAVA_HPROF_OPTIONS \"logflags=0xC\"\n" |
|
448 |
" java " AGENTLIB "=cpu=samples classname\n" |
|
449 |
" is the same as:\n" |
|
450 |
" java " AGENTLIB "=cpu=samples,logflags=0xC classname\n" |
|
451 |
#endif |
|
452 |
||
453 |
"\n" |
|
454 |
"Notes\n" |
|
455 |
"-----\n" |
|
456 |
" - The option format=b cannot be used with monitor=y.\n" |
|
457 |
" - The option format=b cannot be used with cpu=old|times.\n" |
|
458 |
" - Use of the " XRUN " interface can still be used, e.g.\n" |
|
459 |
" java " XRUN ":[help]|[<option>=<value>, ...]\n" |
|
460 |
" will behave exactly the same as:\n" |
|
461 |
" java " AGENTLIB "=[help]|[<option>=<value>, ...]\n" |
|
462 |
||
463 |
#ifdef DEBUG |
|
464 |
" - The debug options and environment variables are available with both java\n" |
|
465 |
" and java_g versions.\n" |
|
466 |
#endif |
|
467 |
||
468 |
"\n" |
|
469 |
"Warnings\n" |
|
470 |
"--------\n" |
|
471 |
" - This is demonstration code for the JVMTI interface and use of BCI,\n" |
|
472 |
" it is not an official product or formal part of the JDK.\n" |
|
473 |
" - The " XRUN " interface will be removed in a future release.\n" |
|
474 |
" - The option format=b is considered experimental, this format may change\n" |
|
475 |
" in a future release.\n" |
|
476 |
||
477 |
#ifdef DEBUG |
|
478 |
" - The obsolete options may be completely removed in a future release.\n" |
|
479 |
" - The debug options and environment variables are not considered public\n" |
|
480 |
" interfaces and can change or be removed with any type of update of\n" |
|
481 |
" " AGENTNAME ", including patches.\n" |
|
482 |
#endif |
|
483 |
||
484 |
); |
|
485 |
} |
|
486 |
||
487 |
static void |
|
488 |
option_error(char *description) |
|
489 |
{ |
|
490 |
char errmsg[FILENAME_MAX+80]; |
|
491 |
||
492 |
(void)md_snprintf(errmsg, sizeof(errmsg), |
|
493 |
"%s option error: %s (%s)", AGENTNAME, description, gdata->options); |
|
494 |
errmsg[sizeof(errmsg)-1] = 0; |
|
495 |
HPROF_ERROR(JNI_FALSE, errmsg); |
|
496 |
error_exit_process(1); |
|
497 |
} |
|
498 |
||
499 |
static void |
|
500 |
parse_options(char *command_line_options) |
|
501 |
{ |
|
502 |
int file_or_net_option_seen = JNI_FALSE; |
|
503 |
char *all_options; |
|
504 |
char *extra_options; |
|
505 |
char *options; |
|
506 |
char *default_filename; |
|
507 |
int ulen; |
|
508 |
||
509 |
if (command_line_options == 0) |
|
510 |
command_line_options = ""; |
|
511 |
||
512 |
if ((strcmp(command_line_options, "help")) == 0) { |
|
513 |
print_usage(); |
|
514 |
error_exit_process(0); |
|
515 |
} |
|
516 |
||
517 |
extra_options = getenv("_JAVA_HPROF_OPTIONS"); |
|
518 |
if ( extra_options == NULL ) { |
|
519 |
extra_options = ""; |
|
520 |
} |
|
521 |
||
522 |
all_options = HPROF_MALLOC((int)strlen(command_line_options) + |
|
523 |
(int)strlen(extra_options) + 2); |
|
524 |
gdata->options = all_options; |
|
525 |
(void)strcpy(all_options, command_line_options); |
|
526 |
if ( extra_options[0] != 0 ) { |
|
527 |
if ( all_options[0] != 0 ) { |
|
528 |
(void)strcat(all_options, ","); |
|
529 |
} |
|
530 |
(void)strcat(all_options, extra_options); |
|
531 |
} |
|
532 |
options = all_options; |
|
533 |
||
534 |
LOG2("parse_options()", all_options); |
|
535 |
||
536 |
while (*options) { |
|
537 |
char option[16]; |
|
538 |
char suboption[FILENAME_MAX+1]; |
|
539 |
char *endptr; |
|
540 |
||
541 |
if (!get_tok(&options, option, (int)sizeof(option), '=')) { |
|
542 |
option_error("general syntax error parsing options"); |
|
543 |
} |
|
544 |
if (strcmp(option, "file") == 0) { |
|
545 |
if ( file_or_net_option_seen ) { |
|
546 |
option_error("file or net options should only appear once"); |
|
547 |
} |
|
548 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
549 |
option_error("syntax error parsing file=filename"); |
|
550 |
} |
|
551 |
gdata->utf8_output_filename = HPROF_MALLOC((int)strlen(suboption)+1); |
|
552 |
(void)strcpy(gdata->utf8_output_filename, suboption); |
|
553 |
file_or_net_option_seen = JNI_TRUE; |
|
554 |
} else if (strcmp(option, "net") == 0) { |
|
555 |
char port_number[16]; |
|
556 |
if (file_or_net_option_seen ) { |
|
557 |
option_error("file or net options should only appear once"); |
|
558 |
} |
|
559 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ':')) { |
|
560 |
option_error("net option missing ':'"); |
|
561 |
} |
|
562 |
if (!get_tok(&options, port_number, (int)sizeof(port_number), ',')) { |
|
563 |
option_error("net option missing port"); |
|
564 |
} |
|
565 |
gdata->net_hostname = HPROF_MALLOC((int)strlen(suboption)+1); |
|
566 |
(void)strcpy(gdata->net_hostname, suboption); |
|
567 |
gdata->net_port = (int)strtol(port_number, NULL, 10); |
|
568 |
file_or_net_option_seen = JNI_TRUE; |
|
569 |
} else if (strcmp(option, "format") == 0) { |
|
570 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
571 |
option_error("syntax error parsing format=a|b"); |
|
572 |
} |
|
573 |
if (strcmp(suboption, "a") == 0) { |
|
574 |
gdata->output_format = 'a'; |
|
575 |
} else if (strcmp(suboption, "b") == 0) { |
|
576 |
gdata->output_format = 'b'; |
|
577 |
} else { |
|
578 |
option_error("format option value must be a|b"); |
|
579 |
} |
|
580 |
} else if (strcmp(option, "depth") == 0) { |
|
581 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
582 |
option_error("syntax error parsing depth=DECIMAL"); |
|
583 |
} |
|
584 |
gdata->max_trace_depth = (int)strtol(suboption, &endptr, 10); |
|
585 |
if ((endptr != NULL && *endptr != 0) || gdata->max_trace_depth < 0) { |
|
586 |
option_error("depth option value must be decimal and >= 0"); |
|
587 |
} |
|
588 |
gdata->prof_trace_depth = gdata->max_trace_depth; |
|
589 |
} else if (strcmp(option, "interval") == 0) { |
|
590 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
591 |
option_error("syntax error parsing interval=DECIMAL"); |
|
592 |
} |
|
593 |
gdata->sample_interval = (int)strtol(suboption, &endptr, 10); |
|
594 |
if ((endptr != NULL && *endptr != 0) || gdata->sample_interval <= 0) { |
|
595 |
option_error("interval option value must be decimal and > 0"); |
|
596 |
} |
|
597 |
} else if (strcmp(option, "cutoff") == 0) { |
|
598 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
599 |
option_error("syntax error parsing cutoff=DOUBLE"); |
|
600 |
} |
|
601 |
gdata->cutoff_point = strtod(suboption, &endptr); |
|
602 |
if ((endptr != NULL && *endptr != 0) || gdata->cutoff_point < 0) { |
|
603 |
option_error("cutoff option value must be floating point and >= 0"); |
|
604 |
} |
|
605 |
} else if (strcmp(option, "cpu") == 0) { |
|
606 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
607 |
option_error("syntax error parsing cpu=y|samples|times|old"); |
|
608 |
} |
|
609 |
if ((strcmp(suboption, "samples") == 0) || |
|
610 |
(strcmp(suboption, "y") == 0)) { |
|
611 |
gdata->cpu_sampling = JNI_TRUE; |
|
612 |
} else if (strcmp(suboption, "times") == 0) { |
|
613 |
gdata->cpu_timing = JNI_TRUE; |
|
614 |
gdata->old_timing_format = JNI_FALSE; |
|
615 |
} else if (strcmp(suboption, "old") == 0) { |
|
616 |
gdata->cpu_timing = JNI_TRUE; |
|
617 |
gdata->old_timing_format = JNI_TRUE; |
|
618 |
} else { |
|
619 |
option_error("cpu option value must be y|samples|times|old"); |
|
620 |
} |
|
621 |
} else if (strcmp(option, "heap") == 0) { |
|
622 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
623 |
option_error("syntax error parsing heap=dump|sites|all"); |
|
624 |
} |
|
625 |
if (strcmp(suboption, "dump") == 0) { |
|
626 |
gdata->heap_dump = JNI_TRUE; |
|
627 |
} else if (strcmp(suboption, "sites") == 0) { |
|
628 |
gdata->alloc_sites = JNI_TRUE; |
|
629 |
} else if (strcmp(suboption, "all") == 0) { |
|
630 |
gdata->heap_dump = JNI_TRUE; |
|
631 |
gdata->alloc_sites = JNI_TRUE; |
|
632 |
} else { |
|
633 |
option_error("heap option value must be dump|sites|all"); |
|
634 |
} |
|
635 |
} else if( strcmp(option,"lineno") == 0) { |
|
636 |
if ( !setBinarySwitch(&options, &(gdata->lineno_in_traces)) ) { |
|
637 |
option_error("lineno option value must be y|n"); |
|
638 |
} |
|
639 |
} else if( strcmp(option,"thread") == 0) { |
|
640 |
if ( !setBinarySwitch(&options, &(gdata->thread_in_traces)) ) { |
|
641 |
option_error("thread option value must be y|n"); |
|
642 |
} |
|
643 |
} else if( strcmp(option,"doe") == 0) { |
|
644 |
if ( !setBinarySwitch(&options, &(gdata->dump_on_exit)) ) { |
|
645 |
option_error("doe option value must be y|n"); |
|
646 |
} |
|
647 |
} else if( strcmp(option,"msa") == 0) { |
|
648 |
if ( !setBinarySwitch(&options, &(gdata->micro_state_accounting)) ) { |
|
649 |
option_error("msa option value must be y|n"); |
|
650 |
} |
|
651 |
} else if( strcmp(option,"force") == 0) { |
|
652 |
if ( !setBinarySwitch(&options, &(gdata->force_output)) ) { |
|
653 |
option_error("force option value must be y|n"); |
|
654 |
} |
|
655 |
} else if( strcmp(option,"verbose") == 0) { |
|
656 |
if ( !setBinarySwitch(&options, &(gdata->verbose)) ) { |
|
657 |
option_error("verbose option value must be y|n"); |
|
658 |
} |
|
659 |
} else if( strcmp(option,"primfields") == 0) { |
|
660 |
if ( !setBinarySwitch(&options, &(gdata->primfields)) ) { |
|
661 |
option_error("primfields option value must be y|n"); |
|
662 |
} |
|
663 |
} else if( strcmp(option,"primarrays") == 0) { |
|
664 |
if ( !setBinarySwitch(&options, &(gdata->primarrays)) ) { |
|
665 |
option_error("primarrays option value must be y|n"); |
|
666 |
} |
|
667 |
} else if( strcmp(option,"monitor") == 0) { |
|
668 |
if ( !setBinarySwitch(&options, &(gdata->monitor_tracing)) ) { |
|
669 |
option_error("monitor option value must be y|n"); |
|
670 |
} |
|
671 |
} else if( strcmp(option,"gc_okay") == 0) { |
|
672 |
if ( !setBinarySwitch(&options, &(gdata->gc_okay)) ) { |
|
673 |
option_error("gc_okay option value must be y|n"); |
|
674 |
} |
|
675 |
} else if (strcmp(option, "logflags") == 0) { |
|
676 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
677 |
option_error("logflags option value must be numeric"); |
|
678 |
} |
|
679 |
gdata->logflags = (int)strtol(suboption, NULL, 0); |
|
680 |
} else if (strcmp(option, "debugflags") == 0) { |
|
681 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
682 |
option_error("debugflags option value must be numeric"); |
|
683 |
} |
|
684 |
gdata->debugflags = (int)strtol(suboption, NULL, 0); |
|
685 |
} else if (strcmp(option, "coredump") == 0) { |
|
686 |
if ( !setBinarySwitch(&options, &(gdata->coredump)) ) { |
|
687 |
option_error("coredump option value must be y|n"); |
|
688 |
} |
|
689 |
} else if (strcmp(option, "exitpause") == 0) { |
|
690 |
option_error("The exitpause option was removed, use -XX:OnError='cmd %%p'"); |
|
691 |
} else if (strcmp(option, "errorexit") == 0) { |
|
692 |
if ( !setBinarySwitch(&options, &(gdata->errorexit)) ) { |
|
693 |
option_error("errorexit option value must be y|n"); |
|
694 |
} |
|
695 |
} else if (strcmp(option, "pause") == 0) { |
|
696 |
if ( !setBinarySwitch(&options, &(gdata->pause)) ) { |
|
697 |
option_error("pause option value must be y|n"); |
|
698 |
} |
|
699 |
} else if (strcmp(option, "debug") == 0) { |
|
700 |
if ( !setBinarySwitch(&options, &(gdata->debug)) ) { |
|
701 |
option_error("debug option value must be y|n"); |
|
702 |
} |
|
703 |
} else if (strcmp(option, "precrash") == 0) { |
|
704 |
option_error("The precrash option was removed, use -XX:OnError='precrash -p %%p'"); |
|
705 |
} else if (strcmp(option, "X") == 0) { |
|
706 |
if (!get_tok(&options, suboption, (int)sizeof(suboption), ',')) { |
|
707 |
option_error("X option value must be numeric"); |
|
708 |
} |
|
709 |
gdata->experiment = (int)strtol(suboption, NULL, 0); |
|
710 |
} else { |
|
711 |
char errmsg[80]; |
|
712 |
(void)strcpy(errmsg, "Unknown option: "); |
|
713 |
(void)strcat(errmsg, option); |
|
714 |
option_error(errmsg); |
|
715 |
} |
|
716 |
} |
|
717 |
||
718 |
if (gdata->output_format == 'b') { |
|
719 |
if (gdata->cpu_timing) { |
|
720 |
option_error("cpu=times|old is not supported with format=b"); |
|
721 |
} |
|
722 |
if (gdata->monitor_tracing) { |
|
723 |
option_error("monitor=y is not supported with format=b"); |
|
724 |
} |
|
725 |
} |
|
726 |
||
727 |
if (gdata->old_timing_format) { |
|
728 |
gdata->prof_trace_depth = 2; |
|
729 |
} |
|
730 |
||
731 |
if (gdata->output_format == 'b') { |
|
732 |
default_filename = DEFAULT_OUTPUTFILE; |
|
733 |
} else { |
|
734 |
default_filename = DEFAULT_OUTPUTFILE DEFAULT_TXT_SUFFIX; |
|
735 |
} |
|
736 |
||
737 |
if (!file_or_net_option_seen) { |
|
738 |
gdata->utf8_output_filename = HPROF_MALLOC((int)strlen(default_filename)+1); |
|
739 |
(void)strcpy(gdata->utf8_output_filename, default_filename); |
|
740 |
} |
|
741 |
||
742 |
if ( gdata->utf8_output_filename != NULL ) { |
|
24967 | 743 |
// Don't attempt to convert output filename. |
744 |
// If fileystem uses the same encoding as the rest of the OS it will work as is. |
|
2 | 745 |
ulen = (int)strlen(gdata->utf8_output_filename); |
746 |
gdata->output_filename = (char*)HPROF_MALLOC(ulen*3+3); |
|
747 |
(void)strcpy(gdata->output_filename, gdata->utf8_output_filename); |
|
748 |
} |
|
749 |
||
750 |
/* By default we turn on gdata->alloc_sites and gdata->heap_dump */ |
|
751 |
if ( !gdata->cpu_timing && |
|
752 |
!gdata->cpu_sampling && |
|
753 |
!gdata->monitor_tracing && |
|
754 |
!gdata->alloc_sites && |
|
755 |
!gdata->heap_dump) { |
|
756 |
gdata->heap_dump = JNI_TRUE; |
|
757 |
gdata->alloc_sites = JNI_TRUE; |
|
758 |
} |
|
759 |
||
760 |
if ( gdata->alloc_sites || gdata->heap_dump ) { |
|
761 |
gdata->obj_watch = JNI_TRUE; |
|
762 |
} |
|
763 |
if ( gdata->obj_watch || gdata->cpu_timing ) { |
|
764 |
gdata->bci = JNI_TRUE; |
|
765 |
} |
|
766 |
||
767 |
/* Create files & sockets needed */ |
|
768 |
if (gdata->heap_dump) { |
|
769 |
char *base; |
|
770 |
int len; |
|
771 |
||
772 |
/* Get a fast tempfile for the heap information */ |
|
773 |
base = gdata->output_filename; |
|
774 |
if ( base==NULL ) { |
|
775 |
base = default_filename; |
|
776 |
} |
|
777 |
len = (int)strlen(base); |
|
778 |
gdata->heapfilename = HPROF_MALLOC(len + 5); |
|
779 |
(void)strcpy(gdata->heapfilename, base); |
|
780 |
(void)strcat(gdata->heapfilename, ".TMP"); |
|
781 |
make_unique_filename(&(gdata->heapfilename)); |
|
782 |
(void)remove(gdata->heapfilename); |
|
783 |
if (gdata->output_format == 'b') { |
|
784 |
if ( gdata->logflags & LOG_CHECK_BINARY ) { |
|
785 |
char * check_suffix; |
|
786 |
||
787 |
check_suffix = ".check" DEFAULT_TXT_SUFFIX; |
|
788 |
gdata->checkfilename = |
|
789 |
HPROF_MALLOC((int)strlen(default_filename)+ |
|
790 |
(int)strlen(check_suffix)+1); |
|
791 |
(void)strcpy(gdata->checkfilename, default_filename); |
|
792 |
(void)strcat(gdata->checkfilename, check_suffix); |
|
793 |
(void)remove(gdata->checkfilename); |
|
794 |
gdata->check_fd = md_creat(gdata->checkfilename); |
|
795 |
} |
|
796 |
if ( gdata->debug ) { |
|
797 |
gdata->logflags |= LOG_CHECK_BINARY; |
|
798 |
} |
|
799 |
gdata->heap_fd = md_creat_binary(gdata->heapfilename); |
|
800 |
} else { |
|
801 |
gdata->heap_fd = md_creat(gdata->heapfilename); |
|
802 |
} |
|
803 |
if ( gdata->heap_fd < 0 ) { |
|
804 |
char errmsg[FILENAME_MAX+80]; |
|
805 |
||
806 |
(void)md_snprintf(errmsg, sizeof(errmsg), |
|
807 |
"can't create temp heap file: %s", gdata->heapfilename); |
|
808 |
errmsg[sizeof(errmsg)-1] = 0; |
|
809 |
HPROF_ERROR(JNI_TRUE, errmsg); |
|
810 |
} |
|
811 |
} |
|
812 |
||
813 |
if ( gdata->net_port > 0 ) { |
|
814 |
LOG2("Agent_OnLoad", "Connecting to socket"); |
|
23056 | 815 |
gdata->fd = connect_to_socket(gdata->net_hostname, gdata->net_port); |
2 | 816 |
if (gdata->fd <= 0) { |
817 |
char errmsg[120]; |
|
818 |
||
819 |
(void)md_snprintf(errmsg, sizeof(errmsg), |
|
820 |
"can't connect to %s:%u", gdata->net_hostname, gdata->net_port); |
|
821 |
errmsg[sizeof(errmsg)-1] = 0; |
|
822 |
HPROF_ERROR(JNI_FALSE, errmsg); |
|
823 |
error_exit_process(1); |
|
824 |
} |
|
825 |
gdata->socket = JNI_TRUE; |
|
826 |
} else { |
|
827 |
/* If going out to a file, obey the force=y|n option */ |
|
828 |
if ( !gdata->force_output ) { |
|
829 |
make_unique_filename(&(gdata->output_filename)); |
|
830 |
} |
|
831 |
/* Make doubly sure this file does NOT exist */ |
|
832 |
(void)remove(gdata->output_filename); |
|
833 |
/* Create the file */ |
|
834 |
if (gdata->output_format == 'b') { |
|
835 |
gdata->fd = md_creat_binary(gdata->output_filename); |
|
836 |
} else { |
|
837 |
gdata->fd = md_creat(gdata->output_filename); |
|
838 |
} |
|
839 |
if (gdata->fd < 0) { |
|
840 |
char errmsg[FILENAME_MAX+80]; |
|
841 |
||
842 |
(void)md_snprintf(errmsg, sizeof(errmsg), |
|
843 |
"can't create profile file: %s", gdata->output_filename); |
|
844 |
errmsg[sizeof(errmsg)-1] = 0; |
|
845 |
HPROF_ERROR(JNI_FALSE, errmsg); |
|
846 |
error_exit_process(1); |
|
847 |
} |
|
848 |
} |
|
849 |
||
850 |
} |
|
851 |
||
852 |
/* ------------------------------------------------------------------- */ |
|
853 |
/* Data reset and dump functions */ |
|
854 |
||
855 |
static void |
|
856 |
reset_all_data(void) |
|
857 |
{ |
|
858 |
if (gdata->cpu_sampling || gdata->cpu_timing || gdata->monitor_tracing) { |
|
859 |
rawMonitorEnter(gdata->data_access_lock); |
|
860 |
} |
|
861 |
||
862 |
if (gdata->cpu_sampling || gdata->cpu_timing) { |
|
863 |
trace_clear_cost(); |
|
864 |
} |
|
865 |
if (gdata->monitor_tracing) { |
|
866 |
monitor_clear(); |
|
867 |
} |
|
868 |
||
869 |
if (gdata->cpu_sampling || gdata->cpu_timing || gdata->monitor_tracing) { |
|
870 |
rawMonitorExit(gdata->data_access_lock); |
|
871 |
} |
|
872 |
} |
|
873 |
||
874 |
static void reset_class_load_status(JNIEnv *env, jthread thread); |
|
875 |
||
876 |
static void |
|
877 |
dump_all_data(JNIEnv *env) |
|
878 |
{ |
|
879 |
verbose_message("Dumping"); |
|
880 |
if (gdata->monitor_tracing) { |
|
881 |
verbose_message(" contended monitor usage ..."); |
|
882 |
tls_dump_monitor_state(env); |
|
883 |
monitor_write_contended_time(env, gdata->cutoff_point); |
|
884 |
} |
|
885 |
if (gdata->heap_dump) { |
|
886 |
verbose_message(" Java heap ..."); |
|
887 |
/* Update the class table */ |
|
888 |
reset_class_load_status(env, NULL); |
|
889 |
site_heapdump(env); |
|
890 |
} |
|
891 |
if (gdata->alloc_sites) { |
|
892 |
verbose_message(" allocation sites ..."); |
|
893 |
site_write(env, 0, gdata->cutoff_point); |
|
894 |
} |
|
895 |
if (gdata->cpu_sampling) { |
|
896 |
verbose_message(" CPU usage by sampling running threads ..."); |
|
897 |
trace_output_cost(env, gdata->cutoff_point); |
|
898 |
} |
|
899 |
if (gdata->cpu_timing) { |
|
900 |
if (!gdata->old_timing_format) { |
|
901 |
verbose_message(" CPU usage by timing methods ..."); |
|
902 |
trace_output_cost(env, gdata->cutoff_point); |
|
903 |
} else { |
|
904 |
verbose_message(" CPU usage in old prof format ..."); |
|
905 |
trace_output_cost_in_prof_format(env); |
|
906 |
} |
|
907 |
} |
|
908 |
reset_all_data(); |
|
909 |
io_flush(); |
|
910 |
verbose_message(" done.\n"); |
|
911 |
} |
|
912 |
||
913 |
/* ------------------------------------------------------------------- */ |
|
914 |
/* Dealing with class load and unload status */ |
|
915 |
||
916 |
static void |
|
917 |
reset_class_load_status(JNIEnv *env, jthread thread) |
|
918 |
{ |
|
919 |
||
920 |
WITH_LOCAL_REFS(env, 1) { |
|
921 |
jint class_count; |
|
922 |
jclass *classes; |
|
923 |
jint i; |
|
924 |
||
925 |
/* Get all classes from JVMTI, make sure they are in the class table. */ |
|
926 |
getLoadedClasses(&classes, &class_count); |
|
927 |
||
928 |
/* We don't know if the class list has changed really, so we |
|
929 |
* guess by the class count changing. Don't want to do |
|
930 |
* a bunch of work on classes when it's unnecessary. |
|
931 |
* I assume that even though we have global references on the |
|
932 |
* jclass object that the class is still considered unloaded. |
|
933 |
* (e.g. GC of jclass isn't required for it to be included |
|
934 |
* in the unloaded list, or not in the load list) |
|
935 |
* [Note: Use of Weak references was a performance problem.] |
|
936 |
*/ |
|
937 |
if ( class_count != gdata->class_count ) { |
|
938 |
||
939 |
rawMonitorEnter(gdata->data_access_lock); { |
|
940 |
||
941 |
/* Unmark the classes in the load list */ |
|
942 |
class_all_status_remove(CLASS_IN_LOAD_LIST); |
|
943 |
||
944 |
/* Pretend like it was a class load event */ |
|
945 |
for ( i = 0 ; i < class_count ; i++ ) { |
|
946 |
jobject loader; |
|
947 |
||
948 |
loader = getClassLoader(classes[i]); |
|
949 |
event_class_load(env, thread, classes[i], loader); |
|
950 |
} |
|
951 |
||
952 |
/* Process the classes that have been unloaded */ |
|
953 |
class_do_unloads(env); |
|
954 |
||
955 |
} rawMonitorExit(gdata->data_access_lock); |
|
956 |
||
957 |
} |
|
958 |
||
959 |
/* Free the space and save the count. */ |
|
960 |
jvmtiDeallocate(classes); |
|
961 |
gdata->class_count = class_count; |
|
962 |
||
963 |
} END_WITH_LOCAL_REFS; |
|
964 |
||
965 |
} |
|
966 |
||
967 |
/* A GC or Death event has happened, so do some cleanup */ |
|
968 |
static void |
|
969 |
object_free_cleanup(JNIEnv *env, jboolean force_class_table_reset) |
|
970 |
{ |
|
971 |
Stack *stack; |
|
972 |
||
973 |
/* Then we process the ObjectFreeStack */ |
|
974 |
rawMonitorEnter(gdata->object_free_lock); { |
|
975 |
stack = gdata->object_free_stack; |
|
976 |
gdata->object_free_stack = NULL; /* Will trigger new stack */ |
|
977 |
} rawMonitorExit(gdata->object_free_lock); |
|
978 |
||
979 |
/* Notice we just grabbed the stack of freed objects so |
|
980 |
* any object free events will create a new stack. |
|
981 |
*/ |
|
982 |
if ( stack != NULL ) { |
|
983 |
int count; |
|
984 |
int i; |
|
985 |
||
986 |
count = stack_depth(stack); |
|
987 |
||
988 |
/* If we saw something freed in this GC */ |
|
989 |
if ( count > 0 ) { |
|
990 |
||
991 |
for ( i = 0 ; i < count ; i++ ) { |
|
992 |
ObjectIndex object_index; |
|
993 |
jlong tag; |
|
994 |
||
995 |
tag = *(jlong*)stack_element(stack,i); |
|
996 |
object_index = tag_extract(tag); |
|
997 |
||
998 |
(void)object_free(object_index); |
|
999 |
} |
|
1000 |
||
1001 |
/* We reset the class load status (only do this once) */ |
|
1002 |
reset_class_load_status(env, NULL); |
|
1003 |
force_class_table_reset = JNI_FALSE; |
|
1004 |
||
1005 |
} |
|
1006 |
||
1007 |
/* Just terminate this stack object */ |
|
1008 |
stack_term(stack); |
|
1009 |
} |
|
1010 |
||
1011 |
/* We reset the class load status if we haven't and need to */ |
|
1012 |
if ( force_class_table_reset ) { |
|
1013 |
reset_class_load_status(env, NULL); |
|
1014 |
} |
|
1015 |
||
1016 |
} |
|
1017 |
||
1018 |
/* Main function for thread that watches for GC finish events */ |
|
1019 |
static void JNICALL |
|
1020 |
gc_finish_watcher(jvmtiEnv *jvmti, JNIEnv *env, void *p) |
|
1021 |
{ |
|
1022 |
jboolean active; |
|
1023 |
||
1024 |
active = JNI_TRUE; |
|
1025 |
||
1026 |
/* Indicate the watcher thread is active */ |
|
1027 |
rawMonitorEnter(gdata->gc_finish_lock); { |
|
1028 |
gdata->gc_finish_active = JNI_TRUE; |
|
1029 |
} rawMonitorExit(gdata->gc_finish_lock); |
|
1030 |
||
1031 |
/* Loop while active */ |
|
1032 |
while ( active ) { |
|
1033 |
jboolean do_cleanup; |
|
1034 |
||
1035 |
do_cleanup = JNI_FALSE; |
|
1036 |
rawMonitorEnter(gdata->gc_finish_lock); { |
|
1037 |
/* Don't wait if VM_DEATH wants us to quit */ |
|
1038 |
if ( gdata->gc_finish_stop_request ) { |
|
1039 |
/* Time to terminate */ |
|
1040 |
active = JNI_FALSE; |
|
1041 |
} else { |
|
1042 |
/* Wait for notification to do cleanup, or terminate */ |
|
1043 |
rawMonitorWait(gdata->gc_finish_lock, 0); |
|
1044 |
/* After wait, check to see if VM_DEATH wants us to quit */ |
|
1045 |
if ( gdata->gc_finish_stop_request ) { |
|
1046 |
/* Time to terminate */ |
|
1047 |
active = JNI_FALSE; |
|
1048 |
} |
|
1049 |
} |
|
1050 |
if ( active && gdata->gc_finish > 0 ) { |
|
1051 |
/* Time to cleanup, reset count and prepare for cleanup */ |
|
1052 |
gdata->gc_finish = 0; |
|
1053 |
do_cleanup = JNI_TRUE; |
|
1054 |
} |
|
1055 |
} rawMonitorExit(gdata->gc_finish_lock); |
|
1056 |
||
1057 |
/* Do the cleanup if requested outside gc_finish_lock */ |
|
1058 |
if ( do_cleanup ) { |
|
1059 |
/* Free up all freed objects, don't force class table reset |
|
1060 |
* We cannot let the VM_DEATH complete while we are doing |
|
1061 |
* this cleanup. So if during this, VM_DEATH happens, |
|
1062 |
* the VM_DEATH callback should block waiting for this |
|
1063 |
* loop to terminate, and send a notification to the |
|
1064 |
* VM_DEATH thread. |
|
1065 |
*/ |
|
1066 |
object_free_cleanup(env, JNI_FALSE); |
|
1067 |
||
1068 |
/* Cleanup the tls table where the Thread objects were GC'd */ |
|
1069 |
tls_garbage_collect(env); |
|
1070 |
} |
|
1071 |
||
1072 |
} |
|
1073 |
||
1074 |
/* Falling out means VM_DEATH is happening, we need to notify VM_DEATH |
|
1075 |
* that we are done doing the cleanup. VM_DEATH is waiting on this |
|
1076 |
* notify. |
|
1077 |
*/ |
|
1078 |
rawMonitorEnter(gdata->gc_finish_lock); { |
|
1079 |
gdata->gc_finish_active = JNI_FALSE; |
|
1080 |
rawMonitorNotifyAll(gdata->gc_finish_lock); |
|
1081 |
} rawMonitorExit(gdata->gc_finish_lock); |
|
1082 |
} |
|
1083 |
||
1084 |
/* ------------------------------------------------------------------- */ |
|
1085 |
/* JVMTI Event callback functions */ |
|
1086 |
||
1087 |
static void |
|
1088 |
setup_event_mode(jboolean onload_set_only, jvmtiEventMode state) |
|
1089 |
{ |
|
1090 |
if ( onload_set_only ) { |
|
1091 |
setEventNotificationMode(state, |
|
1092 |
JVMTI_EVENT_VM_INIT, NULL); |
|
1093 |
setEventNotificationMode(state, |
|
1094 |
JVMTI_EVENT_VM_DEATH, NULL); |
|
1095 |
if (gdata->bci) { |
|
1096 |
setEventNotificationMode(state, |
|
1097 |
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); |
|
1098 |
} |
|
1099 |
} else { |
|
1100 |
/* Enable all other JVMTI events of interest now. */ |
|
1101 |
setEventNotificationMode(state, |
|
1102 |
JVMTI_EVENT_THREAD_START, NULL); |
|
1103 |
setEventNotificationMode(state, |
|
1104 |
JVMTI_EVENT_THREAD_END, NULL); |
|
1105 |
setEventNotificationMode(state, |
|
1106 |
JVMTI_EVENT_CLASS_LOAD, NULL); |
|
1107 |
setEventNotificationMode(state, |
|
1108 |
JVMTI_EVENT_CLASS_PREPARE, NULL); |
|
1109 |
setEventNotificationMode(state, |
|
1110 |
JVMTI_EVENT_DATA_DUMP_REQUEST, NULL); |
|
1111 |
if (gdata->cpu_timing) { |
|
1112 |
setEventNotificationMode(state, |
|
1113 |
JVMTI_EVENT_EXCEPTION_CATCH, NULL); |
|
1114 |
} |
|
1115 |
if (gdata->monitor_tracing) { |
|
1116 |
setEventNotificationMode(state, |
|
1117 |
JVMTI_EVENT_MONITOR_WAIT, NULL); |
|
1118 |
setEventNotificationMode(state, |
|
1119 |
JVMTI_EVENT_MONITOR_WAITED, NULL); |
|
1120 |
setEventNotificationMode(state, |
|
1121 |
JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL); |
|
1122 |
setEventNotificationMode(state, |
|
1123 |
JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL); |
|
1124 |
} |
|
1125 |
if (gdata->obj_watch) { |
|
1126 |
setEventNotificationMode(state, |
|
1127 |
JVMTI_EVENT_OBJECT_FREE, NULL); |
|
1128 |
} |
|
1129 |
setEventNotificationMode(state, |
|
1130 |
JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL); |
|
1131 |
setEventNotificationMode(state, |
|
1132 |
JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL); |
|
1133 |
} |
|
1134 |
} |
|
1135 |
||
1136 |
/* JVMTI_EVENT_VM_INIT */ |
|
1137 |
static void JNICALL |
|
1138 |
cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) |
|
1139 |
{ |
|
1140 |
rawMonitorEnter(gdata->data_access_lock); { |
|
1141 |
||
1142 |
LoaderIndex loader_index; |
|
1143 |
ClassIndex cnum; |
|
1144 |
TlsIndex tls_index; |
|
1145 |
||
1146 |
gdata->jvm_initializing = JNI_TRUE; |
|
1147 |
||
1148 |
/* Header to use in heap dumps */ |
|
1149 |
gdata->header = "JAVA PROFILE 1.0.1"; |
|
1150 |
gdata->segmented = JNI_FALSE; |
|
1151 |
if (gdata->output_format == 'b') { |
|
1152 |
/* We need JNI here to call in and get the current maximum memory */ |
|
1153 |
gdata->maxMemory = getMaxMemory(env); |
|
1154 |
gdata->maxHeapSegment = (jlong)2000000000; |
|
1155 |
/* More than 2Gig triggers segments and 1.0.2 */ |
|
1156 |
if ( gdata->maxMemory >= gdata->maxHeapSegment ) { |
|
1157 |
gdata->header = "JAVA PROFILE 1.0.2"; |
|
1158 |
gdata->segmented = JNI_TRUE; /* 1.0.2 */ |
|
1159 |
} |
|
1160 |
} |
|
1161 |
||
1162 |
/* We write the initial header after the VM initializes now |
|
1163 |
* because we needed to use JNI to get maxMemory and determine if |
|
1164 |
* a 1.0.1 or a 1.0.2 header will be used. |
|
1165 |
* This used to be done in Agent_OnLoad. |
|
1166 |
*/ |
|
1167 |
io_write_file_header(); |
|
1168 |
||
1169 |
LOG("cbVMInit begin"); |
|
1170 |
||
1171 |
/* Create a system loader entry first */ |
|
1172 |
loader_index = loader_find_or_create(NULL,NULL); |
|
1173 |
||
1174 |
/* Find the thread jclass (does JNI calls) */ |
|
1175 |
gdata->thread_cnum = class_find_or_create("Ljava/lang/Thread;", |
|
1176 |
loader_index); |
|
1177 |
class_add_status(gdata->thread_cnum, CLASS_SYSTEM); |
|
1178 |
||
1179 |
/* Issue fake system thread start */ |
|
1180 |
tls_index = tls_find_or_create(env, thread); |
|
1181 |
||
1182 |
/* Setup the Tracker class (should be first class in table) */ |
|
1183 |
tracker_setup_class(); |
|
1184 |
||
1185 |
/* Find selected system classes to keep track of */ |
|
1186 |
gdata->system_class_size = 0; |
|
1187 |
cnum = class_find_or_create("Ljava/lang/Object;", loader_index); |
|
1188 |
||
1189 |
gdata->system_trace_index = tls_get_trace(tls_index, env, |
|
1190 |
gdata->max_trace_depth, JNI_FALSE); |
|
1191 |
gdata->system_object_site_index = site_find_or_create( |
|
1192 |
cnum, gdata->system_trace_index); |
|
1193 |
||
1194 |
/* Used to ID HPROF generated items */ |
|
1195 |
gdata->hprof_trace_index = tls_get_trace(tls_index, env, |
|
1196 |
gdata->max_trace_depth, JNI_FALSE); |
|
1197 |
gdata->hprof_site_index = site_find_or_create( |
|
1198 |
cnum, gdata->hprof_trace_index); |
|
1199 |
||
1200 |
if ( gdata->logflags & LOG_DUMP_LISTS ) { |
|
1201 |
list_all_tables(); |
|
1202 |
} |
|
1203 |
||
1204 |
/* Prime the class table */ |
|
1205 |
reset_class_load_status(env, thread); |
|
1206 |
||
1207 |
/* Find the tracker jclass and jmethodID's (does JNI calls) */ |
|
1208 |
if ( gdata->bci ) { |
|
1209 |
tracker_setup_methods(env); |
|
1210 |
} |
|
1211 |
||
1212 |
/* Start any agent threads (does JNI, JVMTI, and Java calls) */ |
|
1213 |
||
1214 |
/* Thread to watch for gc_finish events */ |
|
1215 |
rawMonitorEnter(gdata->gc_finish_lock); { |
|
1216 |
createAgentThread(env, "HPROF gc_finish watcher", |
|
1217 |
&gc_finish_watcher); |
|
1218 |
} rawMonitorExit(gdata->gc_finish_lock); |
|
1219 |
||
1220 |
/* Start up listener thread if we need it */ |
|
1221 |
if ( gdata->socket ) { |
|
1222 |
listener_init(env); |
|
1223 |
} |
|
1224 |
||
1225 |
/* Start up cpu sampling thread if we need it */ |
|
1226 |
if ( gdata->cpu_sampling ) { |
|
1227 |
/* Note: this could also get started later (see cpu) */ |
|
1228 |
cpu_sample_init(env); |
|
1229 |
} |
|
1230 |
||
1231 |
/* Setup event modes */ |
|
1232 |
setup_event_mode(JNI_FALSE, JVMTI_ENABLE); |
|
1233 |
||
1234 |
/* Engage tracking (sets Java Tracker field so injections call into |
|
1235 |
* agent library). |
|
1236 |
*/ |
|
1237 |
if ( gdata->bci ) { |
|
1238 |
tracker_engage(env); |
|
1239 |
} |
|
1240 |
||
1241 |
/* Indicate the VM is initialized now */ |
|
1242 |
gdata->jvm_initialized = JNI_TRUE; |
|
1243 |
gdata->jvm_initializing = JNI_FALSE; |
|
1244 |
||
1245 |
LOG("cbVMInit end"); |
|
1246 |
||
1247 |
} rawMonitorExit(gdata->data_access_lock); |
|
1248 |
} |
|
1249 |
||
1250 |
/* JVMTI_EVENT_VM_DEATH */ |
|
1251 |
static void JNICALL |
|
1252 |
cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env) |
|
1253 |
{ |
|
1254 |
/* |
|
1255 |
* Use local flag to minimize gdata->dump_lock hold time. |
|
1256 |
*/ |
|
1257 |
jboolean need_to_dump = JNI_FALSE; |
|
1258 |
||
1259 |
LOG("cbVMDeath"); |
|
1260 |
||
1261 |
/* Shutdown thread watching gc_finish, outside CALLBACK locks. |
|
1262 |
* We need to make sure the watcher thread is done doing any cleanup |
|
1263 |
* work before we continue here. |
|
1264 |
*/ |
|
1265 |
rawMonitorEnter(gdata->gc_finish_lock); { |
|
1266 |
/* Notify watcher thread to finish up, it will send |
|
1267 |
* another notify when done. If the watcher thread is busy |
|
1268 |
* cleaning up, it will detect gc_finish_stop_request when it's done. |
|
1269 |
* Then it sets gc_finish_active to JNI_FALSE and will notify us. |
|
1270 |
* If the watcher thread is waiting to be notified, then the |
|
1271 |
* notification wakes it up. |
|
1272 |
* We do not want to do the VM_DEATH while the gc_finish |
|
1273 |
* watcher thread is in the middle of a cleanup. |
|
1274 |
*/ |
|
1275 |
gdata->gc_finish_stop_request = JNI_TRUE; |
|
1276 |
rawMonitorNotifyAll(gdata->gc_finish_lock); |
|
1277 |
/* Wait for the gc_finish watcher thread to notify us it's done */ |
|
1278 |
while ( gdata->gc_finish_active ) { |
|
1279 |
rawMonitorWait(gdata->gc_finish_lock,0); |
|
1280 |
} |
|
1281 |
} rawMonitorExit(gdata->gc_finish_lock); |
|
1282 |
||
1283 |
/* The gc_finish watcher thread should be done now, or done shortly. */ |
|
1284 |
||
1285 |
||
1286 |
/* BEGIN_CALLBACK/END_CALLBACK handling. */ |
|
1287 |
||
1288 |
/* The callbackBlock prevents any active callbacks from returning |
|
1289 |
* back to the VM, and also blocks all new callbacks. |
|
1290 |
* We want to prevent any threads from premature death, so |
|
1291 |
* that we don't have worry about that during thread queries |
|
1292 |
* in this final dump process. |
|
1293 |
*/ |
|
1294 |
rawMonitorEnter(gdata->callbackBlock); { |
|
1295 |
||
1296 |
/* We need to wait for all callbacks actively executing to block |
|
1297 |
* on exit, and new ones will block on entry. |
|
1298 |
* The BEGIN_CALLBACK/END_CALLBACK macros keep track of callbacks |
|
1299 |
* that are active. |
|
1300 |
* Once the last active callback is done, it will notify this |
|
1301 |
* thread and block. |
|
1302 |
*/ |
|
1303 |
||
1304 |
rawMonitorEnter(gdata->callbackLock); { |
|
1305 |
/* Turn off native calls */ |
|
1306 |
if ( gdata->bci ) { |
|
1307 |
tracker_disengage(env); |
|
1308 |
} |
|
1309 |
gdata->vm_death_callback_active = JNI_TRUE; |
|
1310 |
while (gdata->active_callbacks > 0) { |
|
1311 |
rawMonitorWait(gdata->callbackLock, 0); |
|
1312 |
} |
|
1313 |
} rawMonitorExit(gdata->callbackLock); |
|
1314 |
||
1315 |
/* Now we know that no threads will die on us, being blocked |
|
1316 |
* on some event callback, at a minimum ThreadEnd. |
|
1317 |
*/ |
|
1318 |
||
1319 |
/* Make some basic checks. */ |
|
1320 |
rawMonitorEnter(gdata->data_access_lock); { |
|
1321 |
if ( gdata->jvm_initializing ) { |
|
1322 |
HPROF_ERROR(JNI_TRUE, "VM Death during VM Init"); |
|
1323 |
return; |
|
1324 |
} |
|
1325 |
if ( !gdata->jvm_initialized ) { |
|
1326 |
HPROF_ERROR(JNI_TRUE, "VM Death before VM Init"); |
|
1327 |
return; |
|
1328 |
} |
|
1329 |
if (gdata->jvm_shut_down) { |
|
1330 |
HPROF_ERROR(JNI_TRUE, "VM Death more than once?"); |
|
1331 |
return; |
|
1332 |
} |
|
1333 |
} rawMonitorExit(gdata->data_access_lock); |
|
1334 |
||
1335 |
/* Shutdown the cpu loop thread */ |
|
1336 |
if ( gdata->cpu_sampling ) { |
|
1337 |
cpu_sample_term(env); |
|
1338 |
} |
|
1339 |
||
1340 |
/* Time to dump the final data */ |
|
1341 |
rawMonitorEnter(gdata->dump_lock); { |
|
1342 |
||
1343 |
gdata->jvm_shut_down = JNI_TRUE; |
|
1344 |
||
1345 |
if (!gdata->dump_in_process) { |
|
1346 |
need_to_dump = JNI_TRUE; |
|
1347 |
gdata->dump_in_process = JNI_TRUE; |
|
1348 |
/* |
|
1349 |
* Setting gdata->dump_in_process will cause cpu sampling to pause |
|
1350 |
* (if we are sampling). We don't resume sampling after the |
|
1351 |
* dump_all_data() call below because the VM is shutting |
|
1352 |
* down. |
|
1353 |
*/ |
|
1354 |
} |
|
1355 |
||
1356 |
} rawMonitorExit(gdata->dump_lock); |
|
1357 |
||
1358 |
/* Dump everything if we need to */ |
|
1359 |
if (gdata->dump_on_exit && need_to_dump) { |
|
1360 |
||
1361 |
dump_all_data(env); |
|
1362 |
} |
|
1363 |
||
1364 |
/* Disable all events and callbacks now, all of them. |
|
1365 |
* NOTE: It's important that this be done after the dump |
|
1366 |
* it prevents other threads from messing up the data |
|
1367 |
* because they will block on ThreadStart and ThreadEnd |
|
1368 |
* events due to the CALLBACK block. |
|
1369 |
*/ |
|
1370 |
set_callbacks(JNI_FALSE); |
|
1371 |
setup_event_mode(JNI_FALSE, JVMTI_DISABLE); |
|
1372 |
setup_event_mode(JNI_TRUE, JVMTI_DISABLE); |
|
1373 |
||
1374 |
/* Write tail of file */ |
|
1375 |
io_write_file_footer(); |
|
1376 |
||
1377 |
} rawMonitorExit(gdata->callbackBlock); |
|
1378 |
||
1379 |
/* Shutdown the listener thread and socket, or flush I/O buffers */ |
|
1380 |
if (gdata->socket) { |
|
1381 |
listener_term(env); |
|
1382 |
} else { |
|
1383 |
io_flush(); |
|
1384 |
} |
|
1385 |
||
1386 |
/* Close the file descriptors down */ |
|
1387 |
if ( gdata->fd >= 0 ) { |
|
1388 |
(void)md_close(gdata->fd); |
|
1389 |
gdata->fd = -1; |
|
1390 |
if ( gdata->logflags & LOG_CHECK_BINARY ) { |
|
1391 |
if (gdata->output_format == 'b' && gdata->output_filename != NULL) { |
|
1392 |
check_binary_file(gdata->output_filename); |
|
1393 |
} |
|
1394 |
} |
|
1395 |
} |
|
1396 |
if ( gdata->heap_fd >= 0 ) { |
|
1397 |
(void)md_close(gdata->heap_fd); |
|
1398 |
gdata->heap_fd = -1; |
|
1399 |
} |
|
1400 |
||
1401 |
if ( gdata->check_fd >= 0 ) { |
|
1402 |
(void)md_close(gdata->check_fd); |
|
1403 |
gdata->check_fd = -1; |
|
1404 |
} |
|
1405 |
||
1406 |
/* Remove the temporary heap file */ |
|
1407 |
if (gdata->heap_dump) { |
|
1408 |
(void)remove(gdata->heapfilename); |
|
1409 |
} |
|
1410 |
||
1411 |
/* If logging, dump the tables */ |
|
1412 |
if ( gdata->logflags & LOG_DUMP_LISTS ) { |
|
1413 |
list_all_tables(); |
|
1414 |
} |
|
1415 |
||
1416 |
/* Make sure all global references are deleted */ |
|
1417 |
class_delete_global_references(env); |
|
1418 |
loader_delete_global_references(env); |
|
1419 |
tls_delete_global_references(env); |
|
1420 |
||
1421 |
} |
|
1422 |
||
1423 |
/* JVMTI_EVENT_THREAD_START */ |
|
1424 |
static void JNICALL |
|
1425 |
cbThreadStart(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) |
|
1426 |
{ |
|
1427 |
LOG3("cbThreadStart", "thread is", (int)(long)(ptrdiff_t)thread); |
|
1428 |
||
1429 |
BEGIN_CALLBACK() { |
|
1430 |
event_thread_start(env, thread); |
|
1431 |
} END_CALLBACK(); |
|
1432 |
} |
|
1433 |
||
1434 |
/* JVMTI_EVENT_THREAD_END */ |
|
1435 |
static void JNICALL |
|
1436 |
cbThreadEnd(jvmtiEnv *jvmti, JNIEnv *env, jthread thread) |
|
1437 |
{ |
|
1438 |
LOG3("cbThreadEnd", "thread is", (int)(long)(ptrdiff_t)thread); |
|
1439 |
||
1440 |
BEGIN_CALLBACK() { |
|
1441 |
event_thread_end(env, thread); |
|
1442 |
} END_CALLBACK(); |
|
1443 |
} |
|
1444 |
||
1445 |
/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */ |
|
1446 |
static void JNICALL |
|
1447 |
cbClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv* env, |
|
1448 |
jclass class_being_redefined, jobject loader, |
|
1449 |
const char* name, jobject protection_domain, |
|
1450 |
jint class_data_len, const unsigned char* class_data, |
|
1451 |
jint* new_class_data_len, unsigned char** new_class_data) |
|
1452 |
{ |
|
1453 |
||
1454 |
/* WARNING: This will be called before VM_INIT. */ |
|
1455 |
||
1456 |
LOG2("cbClassFileLoadHook:",(name==NULL?"Unknown":name)); |
|
1457 |
||
1458 |
if (!gdata->bci) { |
|
1459 |
return; |
|
1460 |
} |
|
1461 |
||
1462 |
BEGIN_CALLBACK() { |
|
1463 |
rawMonitorEnter(gdata->data_access_lock); { |
|
1464 |
const char *classname; |
|
1465 |
||
1466 |
if ( gdata->bci_counter == 0 ) { |
|
1467 |
/* Prime the system classes */ |
|
1468 |
class_prime_system_classes(); |
|
1469 |
} |
|
1470 |
||
1471 |
gdata->bci_counter++; |
|
1472 |
||
1473 |
*new_class_data_len = 0; |
|
1474 |
*new_class_data = NULL; |
|
1475 |
||
1476 |
/* Name could be NULL */ |
|
1477 |
if ( name == NULL ) { |
|
1478 |
classname = ((JavaCrwDemoClassname) |
|
1479 |
(gdata->java_crw_demo_classname_function)) |
|
1480 |
(class_data, class_data_len, &my_crw_fatal_error_handler); |
|
1481 |
if ( classname == NULL ) { |
|
1482 |
HPROF_ERROR(JNI_TRUE, "No classname in classfile"); |
|
1483 |
} |
|
1484 |
} else { |
|
1485 |
classname = strdup(name); |
|
1486 |
if ( classname == NULL ) { |
|
1487 |
HPROF_ERROR(JNI_TRUE, "Ran out of malloc() space"); |
|
1488 |
} |
|
1489 |
} |
|
1490 |
||
1491 |
/* The tracker class itself? */ |
|
1492 |
if ( strcmp(classname, TRACKER_CLASS_NAME) != 0 ) { |
|
1493 |
ClassIndex cnum; |
|
1494 |
int system_class; |
|
1495 |
unsigned char * new_image; |
|
1496 |
long new_length; |
|
1497 |
int len; |
|
1498 |
char *signature; |
|
1499 |
LoaderIndex loader_index; |
|
1500 |
||
1501 |
LOG2("cbClassFileLoadHook injecting class" , classname); |
|
1502 |
||
1503 |
/* Define a unique class number for this class */ |
|
1504 |
len = (int)strlen(classname); |
|
1505 |
signature = HPROF_MALLOC(len+3); |
|
1506 |
signature[0] = JVM_SIGNATURE_CLASS; |
|
1507 |
(void)memcpy(signature+1, classname, len); |
|
1508 |
signature[len+1] = JVM_SIGNATURE_ENDCLASS; |
|
1509 |
signature[len+2] = 0; |
|
1510 |
loader_index = loader_find_or_create(env,loader); |
|
1511 |
if ( class_being_redefined != NULL ) { |
|
1512 |
cnum = class_find_or_create(signature, loader_index); |
|
1513 |
} else { |
|
1514 |
cnum = class_create(signature, loader_index); |
|
1515 |
} |
|
1516 |
HPROF_FREE(signature); |
|
1517 |
signature = NULL; |
|
1518 |
||
1519 |
/* Make sure class doesn't get unloaded by accident */ |
|
1520 |
class_add_status(cnum, CLASS_IN_LOAD_LIST); |
|
1521 |
||
1522 |
/* Is it a system class? */ |
|
1523 |
system_class = 0; |
|
1524 |
if ( (!gdata->jvm_initialized) |
|
1525 |
&& (!gdata->jvm_initializing) |
|
1526 |
&& ( ( class_get_status(cnum) & CLASS_SYSTEM) != 0 |
|
1527 |
|| gdata->bci_counter < 8 ) ) { |
|
1528 |
system_class = 1; |
|
1529 |
LOG2(classname, " is a system class"); |
|
1530 |
} |
|
1531 |
||
1532 |
new_image = NULL; |
|
1533 |
new_length = 0; |
|
1534 |
||
1535 |
/* Call the class file reader/write demo code */ |
|
1536 |
((JavaCrwDemo)(gdata->java_crw_demo_function))( |
|
1537 |
cnum, |
|
1538 |
classname, |
|
1539 |
class_data, |
|
1540 |
class_data_len, |
|
1541 |
system_class, |
|
1542 |
TRACKER_CLASS_NAME, |
|
1543 |
TRACKER_CLASS_SIG, |
|
1544 |
(gdata->cpu_timing)?TRACKER_CALL_NAME:NULL, |
|
1545 |
(gdata->cpu_timing)?TRACKER_CALL_SIG:NULL, |
|
1546 |
(gdata->cpu_timing)?TRACKER_RETURN_NAME:NULL, |
|
1547 |
(gdata->cpu_timing)?TRACKER_RETURN_SIG:NULL, |
|
1548 |
(gdata->obj_watch)?TRACKER_OBJECT_INIT_NAME:NULL, |
|
1549 |
(gdata->obj_watch)?TRACKER_OBJECT_INIT_SIG:NULL, |
|
1550 |
(gdata->obj_watch)?TRACKER_NEWARRAY_NAME:NULL, |
|
1551 |
(gdata->obj_watch)?TRACKER_NEWARRAY_SIG:NULL, |
|
1552 |
&new_image, |
|
1553 |
&new_length, |
|
1554 |
&my_crw_fatal_error_handler, |
|
1555 |
&class_set_methods); |
|
1556 |
||
1557 |
if ( new_length > 0 ) { |
|
1558 |
unsigned char *jvmti_space; |
|
1559 |
||
1560 |
LOG2("cbClassFileLoadHook DID inject this class", classname); |
|
1561 |
jvmti_space = (unsigned char *)jvmtiAllocate((jint)new_length); |
|
1562 |
(void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length); |
|
1563 |
*new_class_data_len = (jint)new_length; |
|
1564 |
*new_class_data = jvmti_space; /* VM will deallocate */ |
|
1565 |
} else { |
|
1566 |
LOG2("cbClassFileLoadHook DID NOT inject this class", classname); |
|
1567 |
*new_class_data_len = 0; |
|
1568 |
*new_class_data = NULL; |
|
1569 |
} |
|
1570 |
if ( new_image != NULL ) { |
|
1571 |
(void)free((void*)new_image); /* Free malloc() space with free() */ |
|
1572 |
} |
|
1573 |
} |
|
1574 |
(void)free((void*)classname); |
|
1575 |
} rawMonitorExit(gdata->data_access_lock); |
|
1576 |
} END_CALLBACK(); |
|
1577 |
} |
|
1578 |
||
1579 |
/* JVMTI_EVENT_CLASS_LOAD */ |
|
1580 |
static void JNICALL |
|
1581 |
cbClassLoad(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) |
|
1582 |
{ |
|
1583 |
||
1584 |
/* WARNING: This MAY be called before VM_INIT. */ |
|
1585 |
||
1586 |
LOG("cbClassLoad"); |
|
1587 |
||
1588 |
BEGIN_CALLBACK() { |
|
1589 |
rawMonitorEnter(gdata->data_access_lock); { |
|
1590 |
||
1591 |
WITH_LOCAL_REFS(env, 1) { |
|
1592 |
jobject loader; |
|
1593 |
||
1594 |
loader = getClassLoader(klass); |
|
1595 |
event_class_load(env, thread, klass, loader); |
|
1596 |
} END_WITH_LOCAL_REFS; |
|
1597 |
||
1598 |
} rawMonitorExit(gdata->data_access_lock); |
|
1599 |
} END_CALLBACK(); |
|
1600 |
} |
|
1601 |
||
1602 |
/* JVMTI_EVENT_CLASS_PREPARE */ |
|
1603 |
static void JNICALL |
|
1604 |
cbClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass) |
|
1605 |
{ |
|
1606 |
||
1607 |
/* WARNING: This will be called before VM_INIT. */ |
|
1608 |
||
1609 |
LOG("cbClassPrepare"); |
|
1610 |
||
1611 |
BEGIN_CALLBACK() { |
|
1612 |
rawMonitorEnter(gdata->data_access_lock); { |
|
1613 |
||
1614 |
WITH_LOCAL_REFS(env, 1) { |
|
1615 |
jobject loader; |
|
1616 |
||
1617 |
loader = NULL; |
|
1618 |
loader = getClassLoader(klass); |
|
1619 |
event_class_prepare(env, thread, klass, loader); |
|
1620 |
} END_WITH_LOCAL_REFS; |
|
1621 |
||
1622 |
} rawMonitorExit(gdata->data_access_lock); |
|
1623 |
} END_CALLBACK(); |
|
1624 |
||
1625 |
} |
|
1626 |
||
1627 |
/* JVMTI_EVENT_DATA_DUMP_REQUEST */ |
|
1628 |
static void JNICALL |
|
1629 |
cbDataDumpRequest(jvmtiEnv *jvmti) |
|
1630 |
{ |
|
1631 |
jboolean need_to_dump; |
|
1632 |
||
1633 |
LOG("cbDataDumpRequest"); |
|
1634 |
||
1635 |
BEGIN_CALLBACK() { |
|
1636 |
need_to_dump = JNI_FALSE; |
|
1637 |
rawMonitorEnter(gdata->dump_lock); { |
|
1638 |
if (!gdata->dump_in_process) { |
|
1639 |
need_to_dump = JNI_TRUE; |
|
1640 |
gdata->dump_in_process = JNI_TRUE; |
|
1641 |
} |
|
1642 |
} rawMonitorExit(gdata->dump_lock); |
|
1643 |
||
1644 |
if (need_to_dump) { |
|
1645 |
dump_all_data(getEnv()); |
|
1646 |
||
1647 |
rawMonitorEnter(gdata->dump_lock); { |
|
1648 |
gdata->dump_in_process = JNI_FALSE; |
|
1649 |
} rawMonitorExit(gdata->dump_lock); |
|
1650 |
||
1651 |
if (gdata->cpu_sampling && !gdata->jvm_shut_down) { |
|
1652 |
cpu_sample_on(NULL, 0); /* resume sampling */ |
|
1653 |
} |
|
1654 |
} |
|
1655 |
} END_CALLBACK(); |
|
1656 |
||
1657 |
} |
|
1658 |
||
1659 |
/* JVMTI_EVENT_EXCEPTION_CATCH */ |
|
1660 |
static void JNICALL |
|
1661 |
cbExceptionCatch(jvmtiEnv *jvmti, JNIEnv* env, |
|
1662 |
jthread thread, jmethodID method, jlocation location, |
|
1663 |
jobject exception) |
|
1664 |
{ |
|
1665 |
LOG("cbExceptionCatch"); |
|
1666 |
||
1667 |
BEGIN_CALLBACK() { |
|
1668 |
event_exception_catch(env, thread, method, location, exception); |
|
1669 |
} END_CALLBACK(); |
|
1670 |
} |
|
1671 |
||
1672 |
/* JVMTI_EVENT_MONITOR_WAIT */ |
|
1673 |
static void JNICALL |
|
1674 |
cbMonitorWait(jvmtiEnv *jvmti, JNIEnv* env, |
|
1675 |
jthread thread, jobject object, jlong timeout) |
|
1676 |
{ |
|
1677 |
LOG("cbMonitorWait"); |
|
1678 |
||
1679 |
BEGIN_CALLBACK() { |
|
1680 |
monitor_wait_event(env, thread, object, timeout); |
|
1681 |
} END_CALLBACK(); |
|
1682 |
} |
|
1683 |
||
1684 |
/* JVMTI_EVENT_MONITOR_WAITED */ |
|
1685 |
static void JNICALL |
|
1686 |
cbMonitorWaited(jvmtiEnv *jvmti, JNIEnv* env, |
|
1687 |
jthread thread, jobject object, jboolean timed_out) |
|
1688 |
{ |
|
1689 |
LOG("cbMonitorWaited"); |
|
1690 |
||
1691 |
BEGIN_CALLBACK() { |
|
1692 |
monitor_waited_event(env, thread, object, timed_out); |
|
1693 |
} END_CALLBACK(); |
|
1694 |
} |
|
1695 |
||
1696 |
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */ |
|
1697 |
static void JNICALL |
|
1698 |
cbMonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv* env, |
|
1699 |
jthread thread, jobject object) |
|
1700 |
{ |
|
1701 |
LOG("cbMonitorContendedEnter"); |
|
1702 |
||
1703 |
BEGIN_CALLBACK() { |
|
1704 |
monitor_contended_enter_event(env, thread, object); |
|
1705 |
} END_CALLBACK(); |
|
1706 |
} |
|
1707 |
||
1708 |
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */ |
|
1709 |
static void JNICALL |
|
1710 |
cbMonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv* env, |
|
1711 |
jthread thread, jobject object) |
|
1712 |
{ |
|
1713 |
LOG("cbMonitorContendedEntered"); |
|
1714 |
||
1715 |
BEGIN_CALLBACK() { |
|
1716 |
monitor_contended_entered_event(env, thread, object); |
|
1717 |
} END_CALLBACK(); |
|
1718 |
} |
|
1719 |
||
1720 |
/* JVMTI_EVENT_GARBAGE_COLLECTION_START */ |
|
1721 |
static void JNICALL |
|
1722 |
cbGarbageCollectionStart(jvmtiEnv *jvmti) |
|
1723 |
{ |
|
1724 |
LOG("cbGarbageCollectionStart"); |
|
1725 |
||
1726 |
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit |
|
1727 |
* are allowed here (see the JVMTI Spec). |
|
1728 |
*/ |
|
1729 |
||
1730 |
gdata->gc_start_time = md_get_timemillis(); |
|
1731 |
} |
|
1732 |
||
1733 |
/* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */ |
|
1734 |
static void JNICALL |
|
1735 |
cbGarbageCollectionFinish(jvmtiEnv *jvmti) |
|
1736 |
{ |
|
1737 |
LOG("cbGarbageCollectionFinish"); |
|
1738 |
||
1739 |
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit |
|
1740 |
* are allowed here (see the JVMTI Spec). |
|
1741 |
*/ |
|
1742 |
||
1743 |
if ( gdata->gc_start_time != -1L ) { |
|
1744 |
gdata->time_in_gc += (md_get_timemillis() - gdata->gc_start_time); |
|
1745 |
gdata->gc_start_time = -1L; |
|
1746 |
} |
|
1747 |
||
1748 |
/* Increment gc_finish counter, notify watcher thread */ |
|
1749 |
rawMonitorEnter(gdata->gc_finish_lock); { |
|
1750 |
/* If VM_DEATH is trying to shut it down, don't do anything at all. |
|
1751 |
* Never send notify if VM_DEATH wants the watcher thread to quit. |
|
1752 |
*/ |
|
1753 |
if ( gdata->gc_finish_active ) { |
|
1754 |
gdata->gc_finish++; |
|
1755 |
rawMonitorNotifyAll(gdata->gc_finish_lock); |
|
1756 |
} |
|
1757 |
} rawMonitorExit(gdata->gc_finish_lock); |
|
1758 |
} |
|
1759 |
||
1760 |
/* JVMTI_EVENT_OBJECT_FREE */ |
|
1761 |
static void JNICALL |
|
1762 |
cbObjectFree(jvmtiEnv *jvmti, jlong tag) |
|
1763 |
{ |
|
1764 |
LOG3("cbObjectFree", "tag", (int)tag); |
|
1765 |
||
1766 |
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit |
|
1767 |
* are allowed here (see the JVMTI Spec). |
|
1768 |
*/ |
|
1769 |
||
1770 |
HPROF_ASSERT(tag!=(jlong)0); |
|
1771 |
rawMonitorEnter(gdata->object_free_lock); { |
|
1772 |
if ( !gdata->jvm_shut_down ) { |
|
1773 |
Stack *stack; |
|
1774 |
||
1775 |
stack = gdata->object_free_stack; |
|
1776 |
if ( stack == NULL ) { |
|
1777 |
gdata->object_free_stack = stack_init(512, 512, sizeof(jlong)); |
|
1778 |
stack = gdata->object_free_stack; |
|
1779 |
} |
|
1780 |
stack_push(stack, (void*)&tag); |
|
1781 |
} |
|
1782 |
} rawMonitorExit(gdata->object_free_lock); |
|
1783 |
} |
|
1784 |
||
1785 |
static void |
|
1786 |
set_callbacks(jboolean on) |
|
1787 |
{ |
|
1788 |
jvmtiEventCallbacks callbacks; |
|
1789 |
||
1790 |
(void)memset(&callbacks,0,sizeof(callbacks)); |
|
1791 |
if ( ! on ) { |
|
1792 |
setEventCallbacks(&callbacks); |
|
1793 |
return; |
|
1794 |
} |
|
1795 |
||
1796 |
/* JVMTI_EVENT_VM_INIT */ |
|
1797 |
callbacks.VMInit = &cbVMInit; |
|
1798 |
/* JVMTI_EVENT_VM_DEATH */ |
|
1799 |
callbacks.VMDeath = &cbVMDeath; |
|
1800 |
/* JVMTI_EVENT_THREAD_START */ |
|
1801 |
callbacks.ThreadStart = &cbThreadStart; |
|
1802 |
/* JVMTI_EVENT_THREAD_END */ |
|
1803 |
callbacks.ThreadEnd = &cbThreadEnd; |
|
1804 |
/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */ |
|
1805 |
callbacks.ClassFileLoadHook = &cbClassFileLoadHook; |
|
1806 |
/* JVMTI_EVENT_CLASS_LOAD */ |
|
1807 |
callbacks.ClassLoad = &cbClassLoad; |
|
1808 |
/* JVMTI_EVENT_CLASS_PREPARE */ |
|
1809 |
callbacks.ClassPrepare = &cbClassPrepare; |
|
1810 |
/* JVMTI_EVENT_DATA_DUMP_REQUEST */ |
|
1811 |
callbacks.DataDumpRequest = &cbDataDumpRequest; |
|
1812 |
/* JVMTI_EVENT_EXCEPTION_CATCH */ |
|
1813 |
callbacks.ExceptionCatch = &cbExceptionCatch; |
|
1814 |
/* JVMTI_EVENT_MONITOR_WAIT */ |
|
1815 |
callbacks.MonitorWait = &cbMonitorWait; |
|
1816 |
/* JVMTI_EVENT_MONITOR_WAITED */ |
|
1817 |
callbacks.MonitorWaited = &cbMonitorWaited; |
|
1818 |
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */ |
|
1819 |
callbacks.MonitorContendedEnter = &cbMonitorContendedEnter; |
|
1820 |
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */ |
|
1821 |
callbacks.MonitorContendedEntered = &cbMonitorContendedEntered; |
|
1822 |
/* JVMTI_EVENT_GARBAGE_COLLECTION_START */ |
|
1823 |
callbacks.GarbageCollectionStart = &cbGarbageCollectionStart; |
|
1824 |
/* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */ |
|
1825 |
callbacks.GarbageCollectionFinish = &cbGarbageCollectionFinish; |
|
1826 |
/* JVMTI_EVENT_OBJECT_FREE */ |
|
1827 |
callbacks.ObjectFree = &cbObjectFree; |
|
1828 |
||
1829 |
setEventCallbacks(&callbacks); |
|
1830 |
||
1831 |
} |
|
1832 |
||
1833 |
static void |
|
1834 |
getCapabilities(void) |
|
1835 |
{ |
|
1836 |
jvmtiCapabilities needed_capabilities; |
|
1837 |
jvmtiCapabilities potential_capabilities; |
|
1838 |
||
1839 |
/* Fill in ones that we must have */ |
|
1840 |
(void)memset(&needed_capabilities,0,sizeof(needed_capabilities)); |
|
1841 |
needed_capabilities.can_generate_garbage_collection_events = 1; |
|
1842 |
needed_capabilities.can_tag_objects = 1; |
|
1843 |
if (gdata->bci) { |
|
1844 |
needed_capabilities.can_generate_all_class_hook_events = 1; |
|
1845 |
} |
|
1846 |
if (gdata->obj_watch) { |
|
1847 |
needed_capabilities.can_generate_object_free_events = 1; |
|
1848 |
} |
|
1849 |
if (gdata->cpu_timing || gdata->cpu_sampling) { |
|
1850 |
#if 0 /* Not needed until we call JVMTI for CpuTime */ |
|
1851 |
needed_capabilities.can_get_thread_cpu_time = 1; |
|
1852 |
needed_capabilities.can_get_current_thread_cpu_time = 1; |
|
1853 |
#endif |
|
1854 |
needed_capabilities.can_generate_exception_events = 1; |
|
1855 |
} |
|
1856 |
if (gdata->monitor_tracing) { |
|
1857 |
#if 0 /* Not needed until we call JVMTI for CpuTime */ |
|
1858 |
needed_capabilities.can_get_thread_cpu_time = 1; |
|
1859 |
needed_capabilities.can_get_current_thread_cpu_time = 1; |
|
1860 |
#endif |
|
1861 |
needed_capabilities.can_get_owned_monitor_info = 1; |
|
1862 |
needed_capabilities.can_get_current_contended_monitor = 1; |
|
1863 |
needed_capabilities.can_get_monitor_info = 1; |
|
1864 |
needed_capabilities.can_generate_monitor_events = 1; |
|
1865 |
} |
|
1866 |
||
1867 |
/* Get potential capabilities */ |
|
1868 |
getPotentialCapabilities(&potential_capabilities); |
|
1869 |
||
1870 |
/* Some capabilities would be nicer to have */ |
|
1871 |
needed_capabilities.can_get_source_file_name = |
|
1872 |
potential_capabilities.can_get_source_file_name; |
|
1873 |
needed_capabilities.can_get_line_numbers = |
|
1874 |
potential_capabilities.can_get_line_numbers; |
|
1875 |
||
1876 |
/* Add the capabilities */ |
|
1877 |
addCapabilities(&needed_capabilities); |
|
1878 |
||
1879 |
} |
|
1880 |
||
1881 |
/* Dynamic library loading */ |
|
1882 |
static void * |
|
1883 |
load_library(char *name) |
|
1884 |
{ |
|
1885 |
char lname[FILENAME_MAX+1]; |
|
1886 |
char err_buf[256+FILENAME_MAX+1]; |
|
1887 |
char *boot_path; |
|
1888 |
void *handle; |
|
1889 |
||
1890 |
handle = NULL; |
|
1891 |
||
1892 |
/* The library may be located in different ways, try both, but |
|
1893 |
* if it comes from outside the SDK/jre it isn't ours. |
|
1894 |
*/ |
|
1895 |
getSystemProperty("sun.boot.library.path", &boot_path); |
|
1896 |
md_build_library_name(lname, FILENAME_MAX, boot_path, name); |
|
16057
16a81953a291
8009397: test/com/sun/jdi/PrivateTransportTest.sh: ERROR: transport library missing onLoad entry: private_dt_socket
sla
parents:
14698
diff
changeset
|
1897 |
if ( strlen(lname) == 0 ) { |
16a81953a291
8009397: test/com/sun/jdi/PrivateTransportTest.sh: ERROR: transport library missing onLoad entry: private_dt_socket
sla
parents:
14698
diff
changeset
|
1898 |
HPROF_ERROR(JNI_TRUE, "Could not find library"); |
16a81953a291
8009397: test/com/sun/jdi/PrivateTransportTest.sh: ERROR: transport library missing onLoad entry: private_dt_socket
sla
parents:
14698
diff
changeset
|
1899 |
} |
14698
9294fcf94c46
7200297: agent code does not handle multiple boot library path elements correctly
dholmes
parents:
14342
diff
changeset
|
1900 |
jvmtiDeallocate(boot_path); |
2 | 1901 |
handle = md_load_library(lname, err_buf, (int)sizeof(err_buf)); |
1902 |
if ( handle == NULL ) { |
|
1903 |
/* This may be necessary on Windows. */ |
|
1904 |
md_build_library_name(lname, FILENAME_MAX, "", name); |
|
16057
16a81953a291
8009397: test/com/sun/jdi/PrivateTransportTest.sh: ERROR: transport library missing onLoad entry: private_dt_socket
sla
parents:
14698
diff
changeset
|
1905 |
if ( strlen(lname) == 0 ) { |
16a81953a291
8009397: test/com/sun/jdi/PrivateTransportTest.sh: ERROR: transport library missing onLoad entry: private_dt_socket
sla
parents:
14698
diff
changeset
|
1906 |
HPROF_ERROR(JNI_TRUE, "Could not find library"); |
16a81953a291
8009397: test/com/sun/jdi/PrivateTransportTest.sh: ERROR: transport library missing onLoad entry: private_dt_socket
sla
parents:
14698
diff
changeset
|
1907 |
} |
2 | 1908 |
handle = md_load_library(lname, err_buf, (int)sizeof(err_buf)); |
1909 |
if ( handle == NULL ) { |
|
1910 |
HPROF_ERROR(JNI_TRUE, err_buf); |
|
1911 |
} |
|
1912 |
} |
|
1913 |
return handle; |
|
1914 |
} |
|
1915 |
||
1916 |
/* Lookup dynamic function pointer in shared library */ |
|
1917 |
static void * |
|
1918 |
lookup_library_symbol(void *library, char **symbols, int nsymbols) |
|
1919 |
{ |
|
1920 |
void *addr; |
|
1921 |
int i; |
|
1922 |
||
1923 |
addr = NULL; |
|
1924 |
for( i = 0 ; i < nsymbols; i++ ) { |
|
1925 |
addr = md_find_library_entry(library, symbols[i]); |
|
1926 |
if ( addr != NULL ) { |
|
1927 |
break; |
|
1928 |
} |
|
1929 |
} |
|
1930 |
if ( addr == NULL ) { |
|
1931 |
char errmsg[256]; |
|
1932 |
||
1933 |
(void)md_snprintf(errmsg, sizeof(errmsg), |
|
1934 |
"Cannot find library symbol '%s'", symbols[0]); |
|
1935 |
HPROF_ERROR(JNI_TRUE, errmsg); |
|
1936 |
} |
|
1937 |
return addr; |
|
1938 |
} |
|
1939 |
||
1940 |
/* ------------------------------------------------------------------- */ |
|
1941 |
/* The OnLoad interface */ |
|
1942 |
||
1943 |
JNIEXPORT jint JNICALL |
|
1944 |
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) |
|
1945 |
{ |
|
14698
9294fcf94c46
7200297: agent code does not handle multiple boot library path elements correctly
dholmes
parents:
14342
diff
changeset
|
1946 |
char *boot_path = NULL; |
9294fcf94c46
7200297: agent code does not handle multiple boot library path elements correctly
dholmes
parents:
14342
diff
changeset
|
1947 |
|
2 | 1948 |
/* See if it's already loaded */ |
1949 |
if ( gdata!=NULL && gdata->isLoaded==JNI_TRUE ) { |
|
1950 |
HPROF_ERROR(JNI_TRUE, "Cannot load this JVM TI agent twice, check your java command line for duplicate hprof options."); |
|
1951 |
return JNI_ERR; |
|
1952 |
} |
|
1953 |
||
1954 |
gdata = get_gdata(); |
|
1955 |
||
1956 |
gdata->isLoaded = JNI_TRUE; |
|
1957 |
||
1958 |
error_setup(); |
|
1959 |
||
1960 |
LOG2("Agent_OnLoad", "gdata setup"); |
|
1961 |
||
1962 |
gdata->jvm = vm; |
|
1963 |
||
14698
9294fcf94c46
7200297: agent code does not handle multiple boot library path elements correctly
dholmes
parents:
14342
diff
changeset
|
1964 |
/* Get the JVMTI environment */ |
9294fcf94c46
7200297: agent code does not handle multiple boot library path elements correctly
dholmes
parents:
14342
diff
changeset
|
1965 |
getJvmti(); |
9294fcf94c46
7200297: agent code does not handle multiple boot library path elements correctly
dholmes
parents:
14342
diff
changeset
|
1966 |
|
2 | 1967 |
/* Lock needed to protect debug_malloc() code, which is not MT safe */ |
1968 |
#ifdef DEBUG |
|
1969 |
gdata->debug_malloc_lock = createRawMonitor("HPROF debug_malloc lock"); |
|
1970 |
#endif |
|
1971 |
||
1972 |
parse_options(options); |
|
1973 |
||
1974 |
LOG2("Agent_OnLoad", "Has jvmtiEnv and options parsed"); |
|
1975 |
||
1976 |
/* Initialize machine dependent code (micro state accounting) */ |
|
1977 |
md_init(); |
|
1978 |
||
1979 |
string_init(); /* Table index values look like: 0x10000000 */ |
|
1980 |
||
1981 |
class_init(); /* Table index values look like: 0x20000000 */ |
|
1982 |
tls_init(); /* Table index values look like: 0x30000000 */ |
|
1983 |
trace_init(); /* Table index values look like: 0x40000000 */ |
|
1984 |
object_init(); /* Table index values look like: 0x50000000 */ |
|
1985 |
||
1986 |
site_init(); /* Table index values look like: 0x60000000 */ |
|
1987 |
frame_init(); /* Table index values look like: 0x70000000 */ |
|
1988 |
monitor_init(); /* Table index values look like: 0x80000000 */ |
|
1989 |
loader_init(); /* Table index values look like: 0x90000000 */ |
|
1990 |
||
1991 |
LOG2("Agent_OnLoad", "Tables initialized"); |
|
1992 |
||
1993 |
if ( gdata->pause ) { |
|
1994 |
error_do_pause(); |
|
1995 |
} |
|
1996 |
||
1997 |
getCapabilities(); |
|
1998 |
||
1999 |
/* Set the JVMTI callback functions (do this only once)*/ |
|
2000 |
set_callbacks(JNI_TRUE); |
|
2001 |
||
2002 |
/* Create basic locks */ |
|
2003 |
gdata->dump_lock = createRawMonitor("HPROF dump lock"); |
|
2004 |
gdata->data_access_lock = createRawMonitor("HPROF data access lock"); |
|
2005 |
gdata->callbackLock = createRawMonitor("HPROF callback lock"); |
|
2006 |
gdata->callbackBlock = createRawMonitor("HPROF callback block"); |
|
2007 |
gdata->object_free_lock = createRawMonitor("HPROF object free lock"); |
|
2008 |
gdata->gc_finish_lock = createRawMonitor("HPROF gc_finish lock"); |
|
2009 |
||
2010 |
/* Set Onload events mode. */ |
|
2011 |
setup_event_mode(JNI_TRUE, JVMTI_ENABLE); |
|
2012 |
||
2013 |
LOG2("Agent_OnLoad", "JVMTI capabilities, callbacks and initial notifications setup"); |
|
2014 |
||
2015 |
/* Used in VM_DEATH to wait for callbacks to complete */ |
|
2016 |
gdata->jvm_initializing = JNI_FALSE; |
|
2017 |
gdata->jvm_initialized = JNI_FALSE; |
|
2018 |
gdata->vm_death_callback_active = JNI_FALSE; |
|
2019 |
gdata->active_callbacks = 0; |
|
2020 |
||
2021 |
/* Write the header information */ |
|
2022 |
io_setup(); |
|
2023 |
||
2024 |
/* We sample the start time now so that the time increments can be |
|
2025 |
* placed in the various heap dump segments in micro seconds. |
|
2026 |
*/ |
|
2027 |
gdata->micro_sec_ticks = md_get_microsecs(); |
|
2028 |
||
2029 |
/* Load java_crw_demo library and find function "java_crw_demo" */ |
|
2030 |
if ( gdata->bci ) { |
|
2031 |
||
2032 |
/* Load the library or get the handle to it */ |
|
2033 |
gdata->java_crw_demo_library = load_library("java_crw_demo"); |
|
2034 |
||
2035 |
{ /* "java_crw_demo" */ |
|
2036 |
static char *symbols[] = JAVA_CRW_DEMO_SYMBOLS; |
|
2037 |
gdata->java_crw_demo_function = |
|
2038 |
lookup_library_symbol(gdata->java_crw_demo_library, |
|
2039 |
symbols, (int)(sizeof(symbols)/sizeof(char*))); |
|
2040 |
} |
|
2041 |
{ /* "java_crw_demo_classname" */ |
|
2042 |
static char *symbols[] = JAVA_CRW_DEMO_CLASSNAME_SYMBOLS; |
|
2043 |
gdata->java_crw_demo_classname_function = |
|
2044 |
lookup_library_symbol(gdata->java_crw_demo_library, |
|
2045 |
symbols, (int)(sizeof(symbols)/sizeof(char*))); |
|
2046 |
} |
|
2047 |
} |
|
2048 |
||
2049 |
return JNI_OK; |
|
2050 |
} |
|
2051 |
||
2052 |
JNIEXPORT void JNICALL |
|
2053 |
Agent_OnUnload(JavaVM *vm) |
|
2054 |
{ |
|
2055 |
Stack *stack; |
|
2056 |
||
2057 |
LOG("Agent_OnUnload"); |
|
2058 |
||
2059 |
gdata->isLoaded = JNI_FALSE; |
|
2060 |
||
2061 |
stack = gdata->object_free_stack; |
|
2062 |
gdata->object_free_stack = NULL; |
|
2063 |
if ( stack != NULL ) { |
|
2064 |
stack_term(stack); |
|
2065 |
} |
|
2066 |
||
2067 |
io_cleanup(); |
|
2068 |
loader_cleanup(); |
|
2069 |
tls_cleanup(); |
|
2070 |
monitor_cleanup(); |
|
2071 |
trace_cleanup(); |
|
2072 |
site_cleanup(); |
|
2073 |
object_cleanup(); |
|
2074 |
frame_cleanup(); |
|
2075 |
class_cleanup(); |
|
2076 |
string_cleanup(); |
|
2077 |
||
2078 |
/* Deallocate any memory in gdata */ |
|
2079 |
if ( gdata->net_hostname != NULL ) { |
|
2080 |
HPROF_FREE(gdata->net_hostname); |
|
2081 |
} |
|
2082 |
if ( gdata->utf8_output_filename != NULL ) { |
|
2083 |
HPROF_FREE(gdata->utf8_output_filename); |
|
2084 |
} |
|
2085 |
if ( gdata->output_filename != NULL ) { |
|
2086 |
HPROF_FREE(gdata->output_filename); |
|
2087 |
} |
|
2088 |
if ( gdata->heapfilename != NULL ) { |
|
2089 |
HPROF_FREE(gdata->heapfilename); |
|
2090 |
} |
|
2091 |
if ( gdata->checkfilename != NULL ) { |
|
2092 |
HPROF_FREE(gdata->checkfilename); |
|
2093 |
} |
|
2094 |
if ( gdata->options != NULL ) { |
|
2095 |
HPROF_FREE(gdata->options); |
|
2096 |
} |
|
2097 |
||
2098 |
/* Verify all allocated memory has been taken care of. */ |
|
2099 |
malloc_police(); |
|
2100 |
||
2101 |
/* Cleanup is hard to do when other threads might still be running |
|
2102 |
* so we skip destroying some raw monitors which still might be in use |
|
2103 |
* and we skip disposal of the jvmtiEnv* which might still be needed. |
|
2104 |
* Only raw monitors that could be held by other threads are left |
|
2105 |
* alone. So we explicitly do NOT do this: |
|
2106 |
* destroyRawMonitor(gdata->callbackLock); |
|
2107 |
* destroyRawMonitor(gdata->callbackBlock); |
|
2108 |
* destroyRawMonitor(gdata->gc_finish_lock); |
|
2109 |
* destroyRawMonitor(gdata->object_free_lock); |
|
2110 |
* destroyRawMonitor(gdata->listener_loop_lock); |
|
2111 |
* destroyRawMonitor(gdata->cpu_loop_lock); |
|
2112 |
* disposeEnvironment(); |
|
2113 |
* gdata->jvmti = NULL; |
|
2114 |
*/ |
|
2115 |
||
2116 |
/* Destroy basic locks */ |
|
2117 |
destroyRawMonitor(gdata->dump_lock); |
|
2118 |
gdata->dump_lock = NULL; |
|
2119 |
destroyRawMonitor(gdata->data_access_lock); |
|
2120 |
gdata->data_access_lock = NULL; |
|
2121 |
if ( gdata->cpu_sample_lock != NULL ) { |
|
2122 |
destroyRawMonitor(gdata->cpu_sample_lock); |
|
2123 |
gdata->cpu_sample_lock = NULL; |
|
2124 |
} |
|
2125 |
#ifdef DEBUG |
|
2126 |
destroyRawMonitor(gdata->debug_malloc_lock); |
|
2127 |
gdata->debug_malloc_lock = NULL; |
|
2128 |
#endif |
|
2129 |
||
2130 |
/* Unload java_crw_demo library */ |
|
2131 |
if ( gdata->bci && gdata->java_crw_demo_library != NULL ) { |
|
2132 |
md_unload_library(gdata->java_crw_demo_library); |
|
2133 |
gdata->java_crw_demo_library = NULL; |
|
2134 |
} |
|
2135 |
||
2136 |
/* You would think you could clear out gdata and set it to NULL, but |
|
2137 |
* turns out that isn't a good idea. Some of the threads could be |
|
2138 |
* blocked inside the CALLBACK*() macros, where they got blocked up |
|
2139 |
* waiting for the VM_DEATH callback to complete. They only have |
|
2140 |
* some raw monitor actions to do, but they need access to gdata to do it. |
|
2141 |
* So do not do this: |
|
2142 |
* (void)memset(gdata, 0, sizeof(GlobalData)); |
|
2143 |
* gdata = NULL; |
|
2144 |
*/ |
|
2145 |
} |