Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/jvmti/hprof/hprof_init.c
38829 views
1
/*
2
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
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
*
15
* - Neither the name of Oracle nor the names of its
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
32
/*
33
* This source code is provided to illustrate the usage of a given feature
34
* or technique and has been deliberately simplified. Additional steps
35
* required for a production-quality application, such as security checks,
36
* input validation and proper error handling, might not be present in
37
* this sample code.
38
*/
39
40
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
240
connect_to_socket(char *hostname, unsigned short port)
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 */
254
fd = md_connect(hostname, port);
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 ) {
743
/* UTF-8 to platform encoding (fill in gdata->output_filename) */
744
ulen = (int)strlen(gdata->utf8_output_filename);
745
gdata->output_filename = (char*)HPROF_MALLOC(ulen*3+3);
746
#ifdef SKIP_NPT
747
(void)strcpy(gdata->output_filename, gdata->utf8_output_filename);
748
#else
749
(void)(gdata->npt->utf8ToPlatform)
750
(gdata->npt->utf, (jbyte*)gdata->utf8_output_filename, ulen,
751
gdata->output_filename, ulen*3+3);
752
#endif
753
}
754
755
/* By default we turn on gdata->alloc_sites and gdata->heap_dump */
756
if ( !gdata->cpu_timing &&
757
!gdata->cpu_sampling &&
758
!gdata->monitor_tracing &&
759
!gdata->alloc_sites &&
760
!gdata->heap_dump) {
761
gdata->heap_dump = JNI_TRUE;
762
gdata->alloc_sites = JNI_TRUE;
763
}
764
765
if ( gdata->alloc_sites || gdata->heap_dump ) {
766
gdata->obj_watch = JNI_TRUE;
767
}
768
if ( gdata->obj_watch || gdata->cpu_timing ) {
769
gdata->bci = JNI_TRUE;
770
}
771
772
/* Create files & sockets needed */
773
if (gdata->heap_dump) {
774
char *base;
775
int len;
776
777
/* Get a fast tempfile for the heap information */
778
base = gdata->output_filename;
779
if ( base==NULL ) {
780
base = default_filename;
781
}
782
len = (int)strlen(base);
783
gdata->heapfilename = HPROF_MALLOC(len + 5);
784
(void)strcpy(gdata->heapfilename, base);
785
(void)strcat(gdata->heapfilename, ".TMP");
786
make_unique_filename(&(gdata->heapfilename));
787
(void)remove(gdata->heapfilename);
788
if (gdata->output_format == 'b') {
789
if ( gdata->logflags & LOG_CHECK_BINARY ) {
790
char * check_suffix;
791
792
check_suffix = ".check" DEFAULT_TXT_SUFFIX;
793
gdata->checkfilename =
794
HPROF_MALLOC((int)strlen(default_filename)+
795
(int)strlen(check_suffix)+1);
796
(void)strcpy(gdata->checkfilename, default_filename);
797
(void)strcat(gdata->checkfilename, check_suffix);
798
(void)remove(gdata->checkfilename);
799
gdata->check_fd = md_creat(gdata->checkfilename);
800
}
801
if ( gdata->debug ) {
802
gdata->logflags |= LOG_CHECK_BINARY;
803
}
804
gdata->heap_fd = md_creat_binary(gdata->heapfilename);
805
} else {
806
gdata->heap_fd = md_creat(gdata->heapfilename);
807
}
808
if ( gdata->heap_fd < 0 ) {
809
char errmsg[FILENAME_MAX+80];
810
811
(void)md_snprintf(errmsg, sizeof(errmsg),
812
"can't create temp heap file: %s", gdata->heapfilename);
813
errmsg[sizeof(errmsg)-1] = 0;
814
HPROF_ERROR(JNI_TRUE, errmsg);
815
}
816
}
817
818
if ( gdata->net_port > 0 ) {
819
LOG2("Agent_OnLoad", "Connecting to socket");
820
gdata->fd = connect_to_socket(gdata->net_hostname, (unsigned short)gdata->net_port);
821
if (gdata->fd <= 0) {
822
char errmsg[120];
823
824
(void)md_snprintf(errmsg, sizeof(errmsg),
825
"can't connect to %s:%u", gdata->net_hostname, gdata->net_port);
826
errmsg[sizeof(errmsg)-1] = 0;
827
HPROF_ERROR(JNI_FALSE, errmsg);
828
error_exit_process(1);
829
}
830
gdata->socket = JNI_TRUE;
831
} else {
832
/* If going out to a file, obey the force=y|n option */
833
if ( !gdata->force_output ) {
834
make_unique_filename(&(gdata->output_filename));
835
}
836
/* Make doubly sure this file does NOT exist */
837
(void)remove(gdata->output_filename);
838
/* Create the file */
839
if (gdata->output_format == 'b') {
840
gdata->fd = md_creat_binary(gdata->output_filename);
841
} else {
842
gdata->fd = md_creat(gdata->output_filename);
843
}
844
if (gdata->fd < 0) {
845
char errmsg[FILENAME_MAX+80];
846
847
(void)md_snprintf(errmsg, sizeof(errmsg),
848
"can't create profile file: %s", gdata->output_filename);
849
errmsg[sizeof(errmsg)-1] = 0;
850
HPROF_ERROR(JNI_FALSE, errmsg);
851
error_exit_process(1);
852
}
853
}
854
855
}
856
857
/* ------------------------------------------------------------------- */
858
/* Data reset and dump functions */
859
860
static void
861
reset_all_data(void)
862
{
863
if (gdata->cpu_sampling || gdata->cpu_timing || gdata->monitor_tracing) {
864
rawMonitorEnter(gdata->data_access_lock);
865
}
866
867
if (gdata->cpu_sampling || gdata->cpu_timing) {
868
trace_clear_cost();
869
}
870
if (gdata->monitor_tracing) {
871
monitor_clear();
872
}
873
874
if (gdata->cpu_sampling || gdata->cpu_timing || gdata->monitor_tracing) {
875
rawMonitorExit(gdata->data_access_lock);
876
}
877
}
878
879
static void reset_class_load_status(JNIEnv *env, jthread thread);
880
881
static void
882
dump_all_data(JNIEnv *env)
883
{
884
verbose_message("Dumping");
885
if (gdata->monitor_tracing) {
886
verbose_message(" contended monitor usage ...");
887
tls_dump_monitor_state(env);
888
monitor_write_contended_time(env, gdata->cutoff_point);
889
}
890
if (gdata->heap_dump) {
891
verbose_message(" Java heap ...");
892
/* Update the class table */
893
reset_class_load_status(env, NULL);
894
site_heapdump(env);
895
}
896
if (gdata->alloc_sites) {
897
verbose_message(" allocation sites ...");
898
site_write(env, 0, gdata->cutoff_point);
899
}
900
if (gdata->cpu_sampling) {
901
verbose_message(" CPU usage by sampling running threads ...");
902
trace_output_cost(env, gdata->cutoff_point);
903
}
904
if (gdata->cpu_timing) {
905
if (!gdata->old_timing_format) {
906
verbose_message(" CPU usage by timing methods ...");
907
trace_output_cost(env, gdata->cutoff_point);
908
} else {
909
verbose_message(" CPU usage in old prof format ...");
910
trace_output_cost_in_prof_format(env);
911
}
912
}
913
reset_all_data();
914
io_flush();
915
verbose_message(" done.\n");
916
}
917
918
/* ------------------------------------------------------------------- */
919
/* Dealing with class load and unload status */
920
921
static void
922
reset_class_load_status(JNIEnv *env, jthread thread)
923
{
924
925
WITH_LOCAL_REFS(env, 1) {
926
jint class_count;
927
jclass *classes;
928
jint i;
929
930
/* Get all classes from JVMTI, make sure they are in the class table. */
931
getLoadedClasses(&classes, &class_count);
932
933
/* We don't know if the class list has changed really, so we
934
* guess by the class count changing. Don't want to do
935
* a bunch of work on classes when it's unnecessary.
936
* I assume that even though we have global references on the
937
* jclass object that the class is still considered unloaded.
938
* (e.g. GC of jclass isn't required for it to be included
939
* in the unloaded list, or not in the load list)
940
* [Note: Use of Weak references was a performance problem.]
941
*/
942
if ( class_count != gdata->class_count ) {
943
944
rawMonitorEnter(gdata->data_access_lock); {
945
946
/* Unmark the classes in the load list */
947
class_all_status_remove(CLASS_IN_LOAD_LIST);
948
949
/* Pretend like it was a class load event */
950
for ( i = 0 ; i < class_count ; i++ ) {
951
jobject loader;
952
953
loader = getClassLoader(classes[i]);
954
event_class_load(env, thread, classes[i], loader);
955
}
956
957
/* Process the classes that have been unloaded */
958
class_do_unloads(env);
959
960
} rawMonitorExit(gdata->data_access_lock);
961
962
}
963
964
/* Free the space and save the count. */
965
jvmtiDeallocate(classes);
966
gdata->class_count = class_count;
967
968
} END_WITH_LOCAL_REFS;
969
970
}
971
972
/* A GC or Death event has happened, so do some cleanup */
973
static void
974
object_free_cleanup(JNIEnv *env, jboolean force_class_table_reset)
975
{
976
Stack *stack;
977
978
/* Then we process the ObjectFreeStack */
979
rawMonitorEnter(gdata->object_free_lock); {
980
stack = gdata->object_free_stack;
981
gdata->object_free_stack = NULL; /* Will trigger new stack */
982
} rawMonitorExit(gdata->object_free_lock);
983
984
/* Notice we just grabbed the stack of freed objects so
985
* any object free events will create a new stack.
986
*/
987
if ( stack != NULL ) {
988
int count;
989
int i;
990
991
count = stack_depth(stack);
992
993
/* If we saw something freed in this GC */
994
if ( count > 0 ) {
995
996
for ( i = 0 ; i < count ; i++ ) {
997
ObjectIndex object_index;
998
jlong tag;
999
1000
tag = *(jlong*)stack_element(stack,i);
1001
object_index = tag_extract(tag);
1002
1003
(void)object_free(object_index);
1004
}
1005
1006
/* We reset the class load status (only do this once) */
1007
reset_class_load_status(env, NULL);
1008
force_class_table_reset = JNI_FALSE;
1009
1010
}
1011
1012
/* Just terminate this stack object */
1013
stack_term(stack);
1014
}
1015
1016
/* We reset the class load status if we haven't and need to */
1017
if ( force_class_table_reset ) {
1018
reset_class_load_status(env, NULL);
1019
}
1020
1021
}
1022
1023
/* Main function for thread that watches for GC finish events */
1024
static void JNICALL
1025
gc_finish_watcher(jvmtiEnv *jvmti, JNIEnv *env, void *p)
1026
{
1027
jboolean active;
1028
1029
active = JNI_TRUE;
1030
1031
/* Indicate the watcher thread is active */
1032
rawMonitorEnter(gdata->gc_finish_lock); {
1033
gdata->gc_finish_active = JNI_TRUE;
1034
} rawMonitorExit(gdata->gc_finish_lock);
1035
1036
/* Loop while active */
1037
while ( active ) {
1038
jboolean do_cleanup;
1039
1040
do_cleanup = JNI_FALSE;
1041
rawMonitorEnter(gdata->gc_finish_lock); {
1042
/* Don't wait if VM_DEATH wants us to quit */
1043
if ( gdata->gc_finish_stop_request ) {
1044
/* Time to terminate */
1045
active = JNI_FALSE;
1046
} else {
1047
/* Wait for notification to do cleanup, or terminate */
1048
rawMonitorWait(gdata->gc_finish_lock, 0);
1049
/* After wait, check to see if VM_DEATH wants us to quit */
1050
if ( gdata->gc_finish_stop_request ) {
1051
/* Time to terminate */
1052
active = JNI_FALSE;
1053
}
1054
}
1055
if ( active && gdata->gc_finish > 0 ) {
1056
/* Time to cleanup, reset count and prepare for cleanup */
1057
gdata->gc_finish = 0;
1058
do_cleanup = JNI_TRUE;
1059
}
1060
} rawMonitorExit(gdata->gc_finish_lock);
1061
1062
/* Do the cleanup if requested outside gc_finish_lock */
1063
if ( do_cleanup ) {
1064
/* Free up all freed objects, don't force class table reset
1065
* We cannot let the VM_DEATH complete while we are doing
1066
* this cleanup. So if during this, VM_DEATH happens,
1067
* the VM_DEATH callback should block waiting for this
1068
* loop to terminate, and send a notification to the
1069
* VM_DEATH thread.
1070
*/
1071
object_free_cleanup(env, JNI_FALSE);
1072
1073
/* Cleanup the tls table where the Thread objects were GC'd */
1074
tls_garbage_collect(env);
1075
}
1076
1077
}
1078
1079
/* Falling out means VM_DEATH is happening, we need to notify VM_DEATH
1080
* that we are done doing the cleanup. VM_DEATH is waiting on this
1081
* notify.
1082
*/
1083
rawMonitorEnter(gdata->gc_finish_lock); {
1084
gdata->gc_finish_active = JNI_FALSE;
1085
rawMonitorNotifyAll(gdata->gc_finish_lock);
1086
} rawMonitorExit(gdata->gc_finish_lock);
1087
}
1088
1089
/* ------------------------------------------------------------------- */
1090
/* JVMTI Event callback functions */
1091
1092
static void
1093
setup_event_mode(jboolean onload_set_only, jvmtiEventMode state)
1094
{
1095
if ( onload_set_only ) {
1096
setEventNotificationMode(state,
1097
JVMTI_EVENT_VM_INIT, NULL);
1098
setEventNotificationMode(state,
1099
JVMTI_EVENT_VM_DEATH, NULL);
1100
if (gdata->bci) {
1101
setEventNotificationMode(state,
1102
JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
1103
}
1104
} else {
1105
/* Enable all other JVMTI events of interest now. */
1106
setEventNotificationMode(state,
1107
JVMTI_EVENT_THREAD_START, NULL);
1108
setEventNotificationMode(state,
1109
JVMTI_EVENT_THREAD_END, NULL);
1110
setEventNotificationMode(state,
1111
JVMTI_EVENT_CLASS_LOAD, NULL);
1112
setEventNotificationMode(state,
1113
JVMTI_EVENT_CLASS_PREPARE, NULL);
1114
setEventNotificationMode(state,
1115
JVMTI_EVENT_DATA_DUMP_REQUEST, NULL);
1116
if (gdata->cpu_timing) {
1117
setEventNotificationMode(state,
1118
JVMTI_EVENT_EXCEPTION_CATCH, NULL);
1119
}
1120
if (gdata->monitor_tracing) {
1121
setEventNotificationMode(state,
1122
JVMTI_EVENT_MONITOR_WAIT, NULL);
1123
setEventNotificationMode(state,
1124
JVMTI_EVENT_MONITOR_WAITED, NULL);
1125
setEventNotificationMode(state,
1126
JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL);
1127
setEventNotificationMode(state,
1128
JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL);
1129
}
1130
if (gdata->obj_watch) {
1131
setEventNotificationMode(state,
1132
JVMTI_EVENT_OBJECT_FREE, NULL);
1133
}
1134
setEventNotificationMode(state,
1135
JVMTI_EVENT_GARBAGE_COLLECTION_START, NULL);
1136
setEventNotificationMode(state,
1137
JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL);
1138
}
1139
}
1140
1141
/* JVMTI_EVENT_VM_INIT */
1142
static void JNICALL
1143
cbVMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
1144
{
1145
rawMonitorEnter(gdata->data_access_lock); {
1146
1147
LoaderIndex loader_index;
1148
ClassIndex cnum;
1149
TlsIndex tls_index;
1150
1151
gdata->jvm_initializing = JNI_TRUE;
1152
1153
/* Header to use in heap dumps */
1154
gdata->header = "JAVA PROFILE 1.0.1";
1155
gdata->segmented = JNI_FALSE;
1156
if (gdata->output_format == 'b') {
1157
/* We need JNI here to call in and get the current maximum memory */
1158
gdata->maxMemory = getMaxMemory(env);
1159
gdata->maxHeapSegment = (jlong)2000000000;
1160
/* More than 2Gig triggers segments and 1.0.2 */
1161
if ( gdata->maxMemory >= gdata->maxHeapSegment ) {
1162
gdata->header = "JAVA PROFILE 1.0.2";
1163
gdata->segmented = JNI_TRUE; /* 1.0.2 */
1164
}
1165
}
1166
1167
/* We write the initial header after the VM initializes now
1168
* because we needed to use JNI to get maxMemory and determine if
1169
* a 1.0.1 or a 1.0.2 header will be used.
1170
* This used to be done in Agent_OnLoad.
1171
*/
1172
io_write_file_header();
1173
1174
LOG("cbVMInit begin");
1175
1176
/* Create a system loader entry first */
1177
loader_index = loader_find_or_create(NULL,NULL);
1178
1179
/* Find the thread jclass (does JNI calls) */
1180
gdata->thread_cnum = class_find_or_create("Ljava/lang/Thread;",
1181
loader_index);
1182
class_add_status(gdata->thread_cnum, CLASS_SYSTEM);
1183
1184
/* Issue fake system thread start */
1185
tls_index = tls_find_or_create(env, thread);
1186
1187
/* Setup the Tracker class (should be first class in table) */
1188
tracker_setup_class();
1189
1190
/* Find selected system classes to keep track of */
1191
gdata->system_class_size = 0;
1192
cnum = class_find_or_create("Ljava/lang/Object;", loader_index);
1193
1194
gdata->system_trace_index = tls_get_trace(tls_index, env,
1195
gdata->max_trace_depth, JNI_FALSE);
1196
gdata->system_object_site_index = site_find_or_create(
1197
cnum, gdata->system_trace_index);
1198
1199
/* Used to ID HPROF generated items */
1200
gdata->hprof_trace_index = tls_get_trace(tls_index, env,
1201
gdata->max_trace_depth, JNI_FALSE);
1202
gdata->hprof_site_index = site_find_or_create(
1203
cnum, gdata->hprof_trace_index);
1204
1205
if ( gdata->logflags & LOG_DUMP_LISTS ) {
1206
list_all_tables();
1207
}
1208
1209
/* Prime the class table */
1210
reset_class_load_status(env, thread);
1211
1212
/* Find the tracker jclass and jmethodID's (does JNI calls) */
1213
if ( gdata->bci ) {
1214
tracker_setup_methods(env);
1215
}
1216
1217
/* Start any agent threads (does JNI, JVMTI, and Java calls) */
1218
1219
/* Thread to watch for gc_finish events */
1220
rawMonitorEnter(gdata->gc_finish_lock); {
1221
createAgentThread(env, "HPROF gc_finish watcher",
1222
&gc_finish_watcher);
1223
} rawMonitorExit(gdata->gc_finish_lock);
1224
1225
/* Start up listener thread if we need it */
1226
if ( gdata->socket ) {
1227
listener_init(env);
1228
}
1229
1230
/* Start up cpu sampling thread if we need it */
1231
if ( gdata->cpu_sampling ) {
1232
/* Note: this could also get started later (see cpu) */
1233
cpu_sample_init(env);
1234
}
1235
1236
/* Setup event modes */
1237
setup_event_mode(JNI_FALSE, JVMTI_ENABLE);
1238
1239
/* Engage tracking (sets Java Tracker field so injections call into
1240
* agent library).
1241
*/
1242
if ( gdata->bci ) {
1243
tracker_engage(env);
1244
}
1245
1246
/* Indicate the VM is initialized now */
1247
gdata->jvm_initialized = JNI_TRUE;
1248
gdata->jvm_initializing = JNI_FALSE;
1249
1250
LOG("cbVMInit end");
1251
1252
} rawMonitorExit(gdata->data_access_lock);
1253
}
1254
1255
/* JVMTI_EVENT_VM_DEATH */
1256
static void JNICALL
1257
cbVMDeath(jvmtiEnv *jvmti, JNIEnv *env)
1258
{
1259
/*
1260
* Use local flag to minimize gdata->dump_lock hold time.
1261
*/
1262
jboolean need_to_dump = JNI_FALSE;
1263
1264
LOG("cbVMDeath");
1265
1266
/* Shutdown thread watching gc_finish, outside CALLBACK locks.
1267
* We need to make sure the watcher thread is done doing any cleanup
1268
* work before we continue here.
1269
*/
1270
rawMonitorEnter(gdata->gc_finish_lock); {
1271
/* Notify watcher thread to finish up, it will send
1272
* another notify when done. If the watcher thread is busy
1273
* cleaning up, it will detect gc_finish_stop_request when it's done.
1274
* Then it sets gc_finish_active to JNI_FALSE and will notify us.
1275
* If the watcher thread is waiting to be notified, then the
1276
* notification wakes it up.
1277
* We do not want to do the VM_DEATH while the gc_finish
1278
* watcher thread is in the middle of a cleanup.
1279
*/
1280
gdata->gc_finish_stop_request = JNI_TRUE;
1281
rawMonitorNotifyAll(gdata->gc_finish_lock);
1282
/* Wait for the gc_finish watcher thread to notify us it's done */
1283
while ( gdata->gc_finish_active ) {
1284
rawMonitorWait(gdata->gc_finish_lock,0);
1285
}
1286
} rawMonitorExit(gdata->gc_finish_lock);
1287
1288
/* The gc_finish watcher thread should be done now, or done shortly. */
1289
1290
1291
/* BEGIN_CALLBACK/END_CALLBACK handling. */
1292
1293
/* The callbackBlock prevents any active callbacks from returning
1294
* back to the VM, and also blocks all new callbacks.
1295
* We want to prevent any threads from premature death, so
1296
* that we don't have worry about that during thread queries
1297
* in this final dump process.
1298
*/
1299
rawMonitorEnter(gdata->callbackBlock); {
1300
1301
/* We need to wait for all callbacks actively executing to block
1302
* on exit, and new ones will block on entry.
1303
* The BEGIN_CALLBACK/END_CALLBACK macros keep track of callbacks
1304
* that are active.
1305
* Once the last active callback is done, it will notify this
1306
* thread and block.
1307
*/
1308
1309
rawMonitorEnter(gdata->callbackLock); {
1310
/* Turn off native calls */
1311
if ( gdata->bci ) {
1312
tracker_disengage(env);
1313
}
1314
gdata->vm_death_callback_active = JNI_TRUE;
1315
while (gdata->active_callbacks > 0) {
1316
rawMonitorWait(gdata->callbackLock, 0);
1317
}
1318
} rawMonitorExit(gdata->callbackLock);
1319
1320
/* Now we know that no threads will die on us, being blocked
1321
* on some event callback, at a minimum ThreadEnd.
1322
*/
1323
1324
/* Make some basic checks. */
1325
rawMonitorEnter(gdata->data_access_lock); {
1326
if ( gdata->jvm_initializing ) {
1327
HPROF_ERROR(JNI_TRUE, "VM Death during VM Init");
1328
return;
1329
}
1330
if ( !gdata->jvm_initialized ) {
1331
HPROF_ERROR(JNI_TRUE, "VM Death before VM Init");
1332
return;
1333
}
1334
if (gdata->jvm_shut_down) {
1335
HPROF_ERROR(JNI_TRUE, "VM Death more than once?");
1336
return;
1337
}
1338
} rawMonitorExit(gdata->data_access_lock);
1339
1340
/* Shutdown the cpu loop thread */
1341
if ( gdata->cpu_sampling ) {
1342
cpu_sample_term(env);
1343
}
1344
1345
/* Time to dump the final data */
1346
rawMonitorEnter(gdata->dump_lock); {
1347
1348
gdata->jvm_shut_down = JNI_TRUE;
1349
1350
if (!gdata->dump_in_process) {
1351
need_to_dump = JNI_TRUE;
1352
gdata->dump_in_process = JNI_TRUE;
1353
/*
1354
* Setting gdata->dump_in_process will cause cpu sampling to pause
1355
* (if we are sampling). We don't resume sampling after the
1356
* dump_all_data() call below because the VM is shutting
1357
* down.
1358
*/
1359
}
1360
1361
} rawMonitorExit(gdata->dump_lock);
1362
1363
/* Dump everything if we need to */
1364
if (gdata->dump_on_exit && need_to_dump) {
1365
1366
dump_all_data(env);
1367
}
1368
1369
/* Disable all events and callbacks now, all of them.
1370
* NOTE: It's important that this be done after the dump
1371
* it prevents other threads from messing up the data
1372
* because they will block on ThreadStart and ThreadEnd
1373
* events due to the CALLBACK block.
1374
*/
1375
set_callbacks(JNI_FALSE);
1376
setup_event_mode(JNI_FALSE, JVMTI_DISABLE);
1377
setup_event_mode(JNI_TRUE, JVMTI_DISABLE);
1378
1379
/* Write tail of file */
1380
io_write_file_footer();
1381
1382
} rawMonitorExit(gdata->callbackBlock);
1383
1384
/* Shutdown the listener thread and socket, or flush I/O buffers */
1385
if (gdata->socket) {
1386
listener_term(env);
1387
} else {
1388
io_flush();
1389
}
1390
1391
/* Close the file descriptors down */
1392
if ( gdata->fd >= 0 ) {
1393
(void)md_close(gdata->fd);
1394
gdata->fd = -1;
1395
if ( gdata->logflags & LOG_CHECK_BINARY ) {
1396
if (gdata->output_format == 'b' && gdata->output_filename != NULL) {
1397
check_binary_file(gdata->output_filename);
1398
}
1399
}
1400
}
1401
if ( gdata->heap_fd >= 0 ) {
1402
(void)md_close(gdata->heap_fd);
1403
gdata->heap_fd = -1;
1404
}
1405
1406
if ( gdata->check_fd >= 0 ) {
1407
(void)md_close(gdata->check_fd);
1408
gdata->check_fd = -1;
1409
}
1410
1411
/* Remove the temporary heap file */
1412
if (gdata->heap_dump) {
1413
(void)remove(gdata->heapfilename);
1414
}
1415
1416
/* If logging, dump the tables */
1417
if ( gdata->logflags & LOG_DUMP_LISTS ) {
1418
list_all_tables();
1419
}
1420
1421
/* Make sure all global references are deleted */
1422
class_delete_global_references(env);
1423
loader_delete_global_references(env);
1424
tls_delete_global_references(env);
1425
1426
}
1427
1428
/* JVMTI_EVENT_THREAD_START */
1429
static void JNICALL
1430
cbThreadStart(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
1431
{
1432
LOG3("cbThreadStart", "thread is", (int)(long)(ptrdiff_t)thread);
1433
1434
BEGIN_CALLBACK() {
1435
event_thread_start(env, thread);
1436
} END_CALLBACK();
1437
}
1438
1439
/* JVMTI_EVENT_THREAD_END */
1440
static void JNICALL
1441
cbThreadEnd(jvmtiEnv *jvmti, JNIEnv *env, jthread thread)
1442
{
1443
LOG3("cbThreadEnd", "thread is", (int)(long)(ptrdiff_t)thread);
1444
1445
BEGIN_CALLBACK() {
1446
event_thread_end(env, thread);
1447
} END_CALLBACK();
1448
}
1449
1450
/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
1451
static void JNICALL
1452
cbClassFileLoadHook(jvmtiEnv *jvmti_env, JNIEnv* env,
1453
jclass class_being_redefined, jobject loader,
1454
const char* name, jobject protection_domain,
1455
jint class_data_len, const unsigned char* class_data,
1456
jint* new_class_data_len, unsigned char** new_class_data)
1457
{
1458
1459
/* WARNING: This will be called before VM_INIT. */
1460
1461
LOG2("cbClassFileLoadHook:",(name==NULL?"Unknown":name));
1462
1463
if (!gdata->bci) {
1464
return;
1465
}
1466
1467
BEGIN_CALLBACK() {
1468
rawMonitorEnter(gdata->data_access_lock); {
1469
const char *classname;
1470
1471
if ( gdata->bci_counter == 0 ) {
1472
/* Prime the system classes */
1473
class_prime_system_classes();
1474
}
1475
1476
gdata->bci_counter++;
1477
1478
*new_class_data_len = 0;
1479
*new_class_data = NULL;
1480
1481
/* Name could be NULL */
1482
if ( name == NULL ) {
1483
classname = ((JavaCrwDemoClassname)
1484
(gdata->java_crw_demo_classname_function))
1485
(class_data, class_data_len, &my_crw_fatal_error_handler);
1486
if ( classname == NULL ) {
1487
HPROF_ERROR(JNI_TRUE, "No classname in classfile");
1488
}
1489
} else {
1490
classname = strdup(name);
1491
if ( classname == NULL ) {
1492
HPROF_ERROR(JNI_TRUE, "Ran out of malloc() space");
1493
}
1494
}
1495
1496
/* The tracker class itself? */
1497
if ( strcmp(classname, TRACKER_CLASS_NAME) != 0 ) {
1498
ClassIndex cnum;
1499
int system_class;
1500
unsigned char * new_image;
1501
long new_length;
1502
int len;
1503
char *signature;
1504
LoaderIndex loader_index;
1505
1506
LOG2("cbClassFileLoadHook injecting class" , classname);
1507
1508
/* Define a unique class number for this class */
1509
len = (int)strlen(classname);
1510
signature = HPROF_MALLOC(len+3);
1511
signature[0] = JVM_SIGNATURE_CLASS;
1512
(void)memcpy(signature+1, classname, len);
1513
signature[len+1] = JVM_SIGNATURE_ENDCLASS;
1514
signature[len+2] = 0;
1515
loader_index = loader_find_or_create(env,loader);
1516
if ( class_being_redefined != NULL ) {
1517
cnum = class_find_or_create(signature, loader_index);
1518
} else {
1519
cnum = class_create(signature, loader_index);
1520
}
1521
HPROF_FREE(signature);
1522
signature = NULL;
1523
1524
/* Make sure class doesn't get unloaded by accident */
1525
class_add_status(cnum, CLASS_IN_LOAD_LIST);
1526
1527
/* Is it a system class? */
1528
system_class = 0;
1529
if ( (!gdata->jvm_initialized)
1530
&& (!gdata->jvm_initializing)
1531
&& ( ( class_get_status(cnum) & CLASS_SYSTEM) != 0
1532
|| gdata->bci_counter < 8 ) ) {
1533
system_class = 1;
1534
LOG2(classname, " is a system class");
1535
}
1536
1537
new_image = NULL;
1538
new_length = 0;
1539
1540
/* Call the class file reader/write demo code */
1541
((JavaCrwDemo)(gdata->java_crw_demo_function))(
1542
cnum,
1543
classname,
1544
class_data,
1545
class_data_len,
1546
system_class,
1547
TRACKER_CLASS_NAME,
1548
TRACKER_CLASS_SIG,
1549
(gdata->cpu_timing)?TRACKER_CALL_NAME:NULL,
1550
(gdata->cpu_timing)?TRACKER_CALL_SIG:NULL,
1551
(gdata->cpu_timing)?TRACKER_RETURN_NAME:NULL,
1552
(gdata->cpu_timing)?TRACKER_RETURN_SIG:NULL,
1553
(gdata->obj_watch)?TRACKER_OBJECT_INIT_NAME:NULL,
1554
(gdata->obj_watch)?TRACKER_OBJECT_INIT_SIG:NULL,
1555
(gdata->obj_watch)?TRACKER_NEWARRAY_NAME:NULL,
1556
(gdata->obj_watch)?TRACKER_NEWARRAY_SIG:NULL,
1557
&new_image,
1558
&new_length,
1559
&my_crw_fatal_error_handler,
1560
&class_set_methods);
1561
1562
if ( new_length > 0 ) {
1563
unsigned char *jvmti_space;
1564
1565
LOG2("cbClassFileLoadHook DID inject this class", classname);
1566
jvmti_space = (unsigned char *)jvmtiAllocate((jint)new_length);
1567
(void)memcpy((void*)jvmti_space, (void*)new_image, (int)new_length);
1568
*new_class_data_len = (jint)new_length;
1569
*new_class_data = jvmti_space; /* VM will deallocate */
1570
} else {
1571
LOG2("cbClassFileLoadHook DID NOT inject this class", classname);
1572
*new_class_data_len = 0;
1573
*new_class_data = NULL;
1574
}
1575
if ( new_image != NULL ) {
1576
(void)free((void*)new_image); /* Free malloc() space with free() */
1577
}
1578
}
1579
(void)free((void*)classname);
1580
} rawMonitorExit(gdata->data_access_lock);
1581
} END_CALLBACK();
1582
}
1583
1584
/* JVMTI_EVENT_CLASS_LOAD */
1585
static void JNICALL
1586
cbClassLoad(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass)
1587
{
1588
1589
/* WARNING: This MAY be called before VM_INIT. */
1590
1591
LOG("cbClassLoad");
1592
1593
BEGIN_CALLBACK() {
1594
rawMonitorEnter(gdata->data_access_lock); {
1595
1596
WITH_LOCAL_REFS(env, 1) {
1597
jobject loader;
1598
1599
loader = getClassLoader(klass);
1600
event_class_load(env, thread, klass, loader);
1601
} END_WITH_LOCAL_REFS;
1602
1603
} rawMonitorExit(gdata->data_access_lock);
1604
} END_CALLBACK();
1605
}
1606
1607
/* JVMTI_EVENT_CLASS_PREPARE */
1608
static void JNICALL
1609
cbClassPrepare(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jclass klass)
1610
{
1611
1612
/* WARNING: This will be called before VM_INIT. */
1613
1614
LOG("cbClassPrepare");
1615
1616
BEGIN_CALLBACK() {
1617
rawMonitorEnter(gdata->data_access_lock); {
1618
1619
WITH_LOCAL_REFS(env, 1) {
1620
jobject loader;
1621
1622
loader = NULL;
1623
loader = getClassLoader(klass);
1624
event_class_prepare(env, thread, klass, loader);
1625
} END_WITH_LOCAL_REFS;
1626
1627
} rawMonitorExit(gdata->data_access_lock);
1628
} END_CALLBACK();
1629
1630
}
1631
1632
/* JVMTI_EVENT_DATA_DUMP_REQUEST */
1633
static void JNICALL
1634
cbDataDumpRequest(jvmtiEnv *jvmti)
1635
{
1636
jboolean need_to_dump;
1637
1638
LOG("cbDataDumpRequest");
1639
1640
BEGIN_CALLBACK() {
1641
need_to_dump = JNI_FALSE;
1642
rawMonitorEnter(gdata->dump_lock); {
1643
if (!gdata->dump_in_process) {
1644
need_to_dump = JNI_TRUE;
1645
gdata->dump_in_process = JNI_TRUE;
1646
}
1647
} rawMonitorExit(gdata->dump_lock);
1648
1649
if (need_to_dump) {
1650
dump_all_data(getEnv());
1651
1652
rawMonitorEnter(gdata->dump_lock); {
1653
gdata->dump_in_process = JNI_FALSE;
1654
} rawMonitorExit(gdata->dump_lock);
1655
1656
if (gdata->cpu_sampling && !gdata->jvm_shut_down) {
1657
cpu_sample_on(NULL, 0); /* resume sampling */
1658
}
1659
}
1660
} END_CALLBACK();
1661
1662
}
1663
1664
/* JVMTI_EVENT_EXCEPTION_CATCH */
1665
static void JNICALL
1666
cbExceptionCatch(jvmtiEnv *jvmti, JNIEnv* env,
1667
jthread thread, jmethodID method, jlocation location,
1668
jobject exception)
1669
{
1670
LOG("cbExceptionCatch");
1671
1672
BEGIN_CALLBACK() {
1673
event_exception_catch(env, thread, method, location, exception);
1674
} END_CALLBACK();
1675
}
1676
1677
/* JVMTI_EVENT_MONITOR_WAIT */
1678
static void JNICALL
1679
cbMonitorWait(jvmtiEnv *jvmti, JNIEnv* env,
1680
jthread thread, jobject object, jlong timeout)
1681
{
1682
LOG("cbMonitorWait");
1683
1684
BEGIN_CALLBACK() {
1685
monitor_wait_event(env, thread, object, timeout);
1686
} END_CALLBACK();
1687
}
1688
1689
/* JVMTI_EVENT_MONITOR_WAITED */
1690
static void JNICALL
1691
cbMonitorWaited(jvmtiEnv *jvmti, JNIEnv* env,
1692
jthread thread, jobject object, jboolean timed_out)
1693
{
1694
LOG("cbMonitorWaited");
1695
1696
BEGIN_CALLBACK() {
1697
monitor_waited_event(env, thread, object, timed_out);
1698
} END_CALLBACK();
1699
}
1700
1701
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
1702
static void JNICALL
1703
cbMonitorContendedEnter(jvmtiEnv *jvmti, JNIEnv* env,
1704
jthread thread, jobject object)
1705
{
1706
LOG("cbMonitorContendedEnter");
1707
1708
BEGIN_CALLBACK() {
1709
monitor_contended_enter_event(env, thread, object);
1710
} END_CALLBACK();
1711
}
1712
1713
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
1714
static void JNICALL
1715
cbMonitorContendedEntered(jvmtiEnv *jvmti, JNIEnv* env,
1716
jthread thread, jobject object)
1717
{
1718
LOG("cbMonitorContendedEntered");
1719
1720
BEGIN_CALLBACK() {
1721
monitor_contended_entered_event(env, thread, object);
1722
} END_CALLBACK();
1723
}
1724
1725
/* JVMTI_EVENT_GARBAGE_COLLECTION_START */
1726
static void JNICALL
1727
cbGarbageCollectionStart(jvmtiEnv *jvmti)
1728
{
1729
LOG("cbGarbageCollectionStart");
1730
1731
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
1732
* are allowed here (see the JVMTI Spec).
1733
*/
1734
1735
gdata->gc_start_time = md_get_timemillis();
1736
}
1737
1738
/* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
1739
static void JNICALL
1740
cbGarbageCollectionFinish(jvmtiEnv *jvmti)
1741
{
1742
LOG("cbGarbageCollectionFinish");
1743
1744
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
1745
* are allowed here (see the JVMTI Spec).
1746
*/
1747
1748
if ( gdata->gc_start_time != -1L ) {
1749
gdata->time_in_gc += (md_get_timemillis() - gdata->gc_start_time);
1750
gdata->gc_start_time = -1L;
1751
}
1752
1753
/* Increment gc_finish counter, notify watcher thread */
1754
rawMonitorEnter(gdata->gc_finish_lock); {
1755
/* If VM_DEATH is trying to shut it down, don't do anything at all.
1756
* Never send notify if VM_DEATH wants the watcher thread to quit.
1757
*/
1758
if ( gdata->gc_finish_active ) {
1759
gdata->gc_finish++;
1760
rawMonitorNotifyAll(gdata->gc_finish_lock);
1761
}
1762
} rawMonitorExit(gdata->gc_finish_lock);
1763
}
1764
1765
/* JVMTI_EVENT_OBJECT_FREE */
1766
static void JNICALL
1767
cbObjectFree(jvmtiEnv *jvmti, jlong tag)
1768
{
1769
LOG3("cbObjectFree", "tag", (int)tag);
1770
1771
/* Only calls to Allocate, Deallocate, RawMonitorEnter & RawMonitorExit
1772
* are allowed here (see the JVMTI Spec).
1773
*/
1774
1775
HPROF_ASSERT(tag!=(jlong)0);
1776
rawMonitorEnter(gdata->object_free_lock); {
1777
if ( !gdata->jvm_shut_down ) {
1778
Stack *stack;
1779
1780
stack = gdata->object_free_stack;
1781
if ( stack == NULL ) {
1782
gdata->object_free_stack = stack_init(512, 512, sizeof(jlong));
1783
stack = gdata->object_free_stack;
1784
}
1785
stack_push(stack, (void*)&tag);
1786
}
1787
} rawMonitorExit(gdata->object_free_lock);
1788
}
1789
1790
static void
1791
set_callbacks(jboolean on)
1792
{
1793
jvmtiEventCallbacks callbacks;
1794
1795
(void)memset(&callbacks,0,sizeof(callbacks));
1796
if ( ! on ) {
1797
setEventCallbacks(&callbacks);
1798
return;
1799
}
1800
1801
/* JVMTI_EVENT_VM_INIT */
1802
callbacks.VMInit = &cbVMInit;
1803
/* JVMTI_EVENT_VM_DEATH */
1804
callbacks.VMDeath = &cbVMDeath;
1805
/* JVMTI_EVENT_THREAD_START */
1806
callbacks.ThreadStart = &cbThreadStart;
1807
/* JVMTI_EVENT_THREAD_END */
1808
callbacks.ThreadEnd = &cbThreadEnd;
1809
/* JVMTI_EVENT_CLASS_FILE_LOAD_HOOK */
1810
callbacks.ClassFileLoadHook = &cbClassFileLoadHook;
1811
/* JVMTI_EVENT_CLASS_LOAD */
1812
callbacks.ClassLoad = &cbClassLoad;
1813
/* JVMTI_EVENT_CLASS_PREPARE */
1814
callbacks.ClassPrepare = &cbClassPrepare;
1815
/* JVMTI_EVENT_DATA_DUMP_REQUEST */
1816
callbacks.DataDumpRequest = &cbDataDumpRequest;
1817
/* JVMTI_EVENT_EXCEPTION_CATCH */
1818
callbacks.ExceptionCatch = &cbExceptionCatch;
1819
/* JVMTI_EVENT_MONITOR_WAIT */
1820
callbacks.MonitorWait = &cbMonitorWait;
1821
/* JVMTI_EVENT_MONITOR_WAITED */
1822
callbacks.MonitorWaited = &cbMonitorWaited;
1823
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
1824
callbacks.MonitorContendedEnter = &cbMonitorContendedEnter;
1825
/* JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
1826
callbacks.MonitorContendedEntered = &cbMonitorContendedEntered;
1827
/* JVMTI_EVENT_GARBAGE_COLLECTION_START */
1828
callbacks.GarbageCollectionStart = &cbGarbageCollectionStart;
1829
/* JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
1830
callbacks.GarbageCollectionFinish = &cbGarbageCollectionFinish;
1831
/* JVMTI_EVENT_OBJECT_FREE */
1832
callbacks.ObjectFree = &cbObjectFree;
1833
1834
setEventCallbacks(&callbacks);
1835
1836
}
1837
1838
static void
1839
getCapabilities(void)
1840
{
1841
jvmtiCapabilities needed_capabilities;
1842
jvmtiCapabilities potential_capabilities;
1843
1844
/* Fill in ones that we must have */
1845
(void)memset(&needed_capabilities,0,sizeof(needed_capabilities));
1846
needed_capabilities.can_generate_garbage_collection_events = 1;
1847
needed_capabilities.can_tag_objects = 1;
1848
if (gdata->bci) {
1849
needed_capabilities.can_generate_all_class_hook_events = 1;
1850
}
1851
if (gdata->obj_watch) {
1852
needed_capabilities.can_generate_object_free_events = 1;
1853
}
1854
if (gdata->cpu_timing || gdata->cpu_sampling) {
1855
#if 0 /* Not needed until we call JVMTI for CpuTime */
1856
needed_capabilities.can_get_thread_cpu_time = 1;
1857
needed_capabilities.can_get_current_thread_cpu_time = 1;
1858
#endif
1859
needed_capabilities.can_generate_exception_events = 1;
1860
}
1861
if (gdata->monitor_tracing) {
1862
#if 0 /* Not needed until we call JVMTI for CpuTime */
1863
needed_capabilities.can_get_thread_cpu_time = 1;
1864
needed_capabilities.can_get_current_thread_cpu_time = 1;
1865
#endif
1866
needed_capabilities.can_get_owned_monitor_info = 1;
1867
needed_capabilities.can_get_current_contended_monitor = 1;
1868
needed_capabilities.can_get_monitor_info = 1;
1869
needed_capabilities.can_generate_monitor_events = 1;
1870
}
1871
1872
/* Get potential capabilities */
1873
getPotentialCapabilities(&potential_capabilities);
1874
1875
/* Some capabilities would be nicer to have */
1876
needed_capabilities.can_get_source_file_name =
1877
potential_capabilities.can_get_source_file_name;
1878
needed_capabilities.can_get_line_numbers =
1879
potential_capabilities.can_get_line_numbers;
1880
1881
/* Add the capabilities */
1882
addCapabilities(&needed_capabilities);
1883
1884
}
1885
1886
/* Dynamic library loading */
1887
static void *
1888
load_library(char *name)
1889
{
1890
char lname[FILENAME_MAX+1];
1891
char err_buf[256+FILENAME_MAX+1];
1892
char *boot_path;
1893
void *handle;
1894
1895
handle = NULL;
1896
1897
/* The library may be located in different ways, try both, but
1898
* if it comes from outside the SDK/jre it isn't ours.
1899
*/
1900
getSystemProperty("sun.boot.library.path", &boot_path);
1901
md_build_library_name(lname, FILENAME_MAX, boot_path, name);
1902
if ( strlen(lname) == 0 ) {
1903
HPROF_ERROR(JNI_TRUE, "Could not find library");
1904
}
1905
jvmtiDeallocate(boot_path);
1906
handle = md_load_library(lname, err_buf, (int)sizeof(err_buf));
1907
if ( handle == NULL ) {
1908
/* This may be necessary on Windows. */
1909
md_build_library_name(lname, FILENAME_MAX, "", name);
1910
if ( strlen(lname) == 0 ) {
1911
HPROF_ERROR(JNI_TRUE, "Could not find library");
1912
}
1913
handle = md_load_library(lname, err_buf, (int)sizeof(err_buf));
1914
if ( handle == NULL ) {
1915
HPROF_ERROR(JNI_TRUE, err_buf);
1916
}
1917
}
1918
return handle;
1919
}
1920
1921
/* Lookup dynamic function pointer in shared library */
1922
static void *
1923
lookup_library_symbol(void *library, char **symbols, int nsymbols)
1924
{
1925
void *addr;
1926
int i;
1927
1928
addr = NULL;
1929
for( i = 0 ; i < nsymbols; i++ ) {
1930
addr = md_find_library_entry(library, symbols[i]);
1931
if ( addr != NULL ) {
1932
break;
1933
}
1934
}
1935
if ( addr == NULL ) {
1936
char errmsg[256];
1937
1938
(void)md_snprintf(errmsg, sizeof(errmsg),
1939
"Cannot find library symbol '%s'", symbols[0]);
1940
HPROF_ERROR(JNI_TRUE, errmsg);
1941
}
1942
return addr;
1943
}
1944
1945
/* ------------------------------------------------------------------- */
1946
/* The OnLoad interface */
1947
1948
JNIEXPORT jint JNICALL
1949
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
1950
{
1951
char *boot_path = NULL;
1952
char npt_lib[JVM_MAXPATHLEN];
1953
1954
/* See if it's already loaded */
1955
if ( gdata!=NULL && gdata->isLoaded==JNI_TRUE ) {
1956
HPROF_ERROR(JNI_TRUE, "Cannot load this JVM TI agent twice, check your java command line for duplicate hprof options.");
1957
return JNI_ERR;
1958
}
1959
1960
gdata = get_gdata();
1961
1962
gdata->isLoaded = JNI_TRUE;
1963
1964
error_setup();
1965
1966
LOG2("Agent_OnLoad", "gdata setup");
1967
1968
gdata->jvm = vm;
1969
1970
/* Get the JVMTI environment */
1971
getJvmti();
1972
1973
#ifndef SKIP_NPT
1974
getSystemProperty("sun.boot.library.path", &boot_path);
1975
/* Load in NPT library for character conversions */
1976
md_build_library_name(npt_lib, sizeof(npt_lib), boot_path, NPT_LIBNAME);
1977
if ( strlen(npt_lib) == 0 ) {
1978
HPROF_ERROR(JNI_TRUE, "Could not find npt library");
1979
}
1980
jvmtiDeallocate(boot_path);
1981
NPT_INITIALIZE(npt_lib, &(gdata->npt), NPT_VERSION, NULL);
1982
if ( gdata->npt == NULL ) {
1983
HPROF_ERROR(JNI_TRUE, "Cannot load npt library");
1984
}
1985
gdata->npt->utf = (gdata->npt->utfInitialize)(NULL);
1986
if ( gdata->npt->utf == NULL ) {
1987
HPROF_ERROR(JNI_TRUE, "Cannot initialize npt utf functions");
1988
}
1989
#endif
1990
1991
/* Lock needed to protect debug_malloc() code, which is not MT safe */
1992
#ifdef DEBUG
1993
gdata->debug_malloc_lock = createRawMonitor("HPROF debug_malloc lock");
1994
#endif
1995
1996
parse_options(options);
1997
1998
LOG2("Agent_OnLoad", "Has jvmtiEnv and options parsed");
1999
2000
/* Initialize machine dependent code (micro state accounting) */
2001
md_init();
2002
2003
string_init(); /* Table index values look like: 0x10000000 */
2004
2005
class_init(); /* Table index values look like: 0x20000000 */
2006
tls_init(); /* Table index values look like: 0x30000000 */
2007
trace_init(); /* Table index values look like: 0x40000000 */
2008
object_init(); /* Table index values look like: 0x50000000 */
2009
2010
site_init(); /* Table index values look like: 0x60000000 */
2011
frame_init(); /* Table index values look like: 0x70000000 */
2012
monitor_init(); /* Table index values look like: 0x80000000 */
2013
loader_init(); /* Table index values look like: 0x90000000 */
2014
2015
LOG2("Agent_OnLoad", "Tables initialized");
2016
2017
if ( gdata->pause ) {
2018
error_do_pause();
2019
}
2020
2021
getCapabilities();
2022
2023
/* Set the JVMTI callback functions (do this only once)*/
2024
set_callbacks(JNI_TRUE);
2025
2026
/* Create basic locks */
2027
gdata->dump_lock = createRawMonitor("HPROF dump lock");
2028
gdata->data_access_lock = createRawMonitor("HPROF data access lock");
2029
gdata->callbackLock = createRawMonitor("HPROF callback lock");
2030
gdata->callbackBlock = createRawMonitor("HPROF callback block");
2031
gdata->object_free_lock = createRawMonitor("HPROF object free lock");
2032
gdata->gc_finish_lock = createRawMonitor("HPROF gc_finish lock");
2033
2034
/* Set Onload events mode. */
2035
setup_event_mode(JNI_TRUE, JVMTI_ENABLE);
2036
2037
LOG2("Agent_OnLoad", "JVMTI capabilities, callbacks and initial notifications setup");
2038
2039
/* Used in VM_DEATH to wait for callbacks to complete */
2040
gdata->jvm_initializing = JNI_FALSE;
2041
gdata->jvm_initialized = JNI_FALSE;
2042
gdata->vm_death_callback_active = JNI_FALSE;
2043
gdata->active_callbacks = 0;
2044
2045
/* Write the header information */
2046
io_setup();
2047
2048
/* We sample the start time now so that the time increments can be
2049
* placed in the various heap dump segments in micro seconds.
2050
*/
2051
gdata->micro_sec_ticks = md_get_microsecs();
2052
2053
/* Load java_crw_demo library and find function "java_crw_demo" */
2054
if ( gdata->bci ) {
2055
2056
/* Load the library or get the handle to it */
2057
gdata->java_crw_demo_library = load_library("java_crw_demo");
2058
2059
{ /* "java_crw_demo" */
2060
static char *symbols[] = JAVA_CRW_DEMO_SYMBOLS;
2061
gdata->java_crw_demo_function =
2062
lookup_library_symbol(gdata->java_crw_demo_library,
2063
symbols, (int)(sizeof(symbols)/sizeof(char*)));
2064
}
2065
{ /* "java_crw_demo_classname" */
2066
static char *symbols[] = JAVA_CRW_DEMO_CLASSNAME_SYMBOLS;
2067
gdata->java_crw_demo_classname_function =
2068
lookup_library_symbol(gdata->java_crw_demo_library,
2069
symbols, (int)(sizeof(symbols)/sizeof(char*)));
2070
}
2071
}
2072
2073
return JNI_OK;
2074
}
2075
2076
JNIEXPORT void JNICALL
2077
Agent_OnUnload(JavaVM *vm)
2078
{
2079
Stack *stack;
2080
2081
LOG("Agent_OnUnload");
2082
2083
gdata->isLoaded = JNI_FALSE;
2084
2085
stack = gdata->object_free_stack;
2086
gdata->object_free_stack = NULL;
2087
if ( stack != NULL ) {
2088
stack_term(stack);
2089
}
2090
2091
io_cleanup();
2092
loader_cleanup();
2093
tls_cleanup();
2094
monitor_cleanup();
2095
trace_cleanup();
2096
site_cleanup();
2097
object_cleanup();
2098
frame_cleanup();
2099
class_cleanup();
2100
string_cleanup();
2101
2102
/* Deallocate any memory in gdata */
2103
if ( gdata->net_hostname != NULL ) {
2104
HPROF_FREE(gdata->net_hostname);
2105
}
2106
if ( gdata->utf8_output_filename != NULL ) {
2107
HPROF_FREE(gdata->utf8_output_filename);
2108
}
2109
if ( gdata->output_filename != NULL ) {
2110
HPROF_FREE(gdata->output_filename);
2111
}
2112
if ( gdata->heapfilename != NULL ) {
2113
HPROF_FREE(gdata->heapfilename);
2114
}
2115
if ( gdata->checkfilename != NULL ) {
2116
HPROF_FREE(gdata->checkfilename);
2117
}
2118
if ( gdata->options != NULL ) {
2119
HPROF_FREE(gdata->options);
2120
}
2121
2122
/* Verify all allocated memory has been taken care of. */
2123
malloc_police();
2124
2125
/* Cleanup is hard to do when other threads might still be running
2126
* so we skip destroying some raw monitors which still might be in use
2127
* and we skip disposal of the jvmtiEnv* which might still be needed.
2128
* Only raw monitors that could be held by other threads are left
2129
* alone. So we explicitly do NOT do this:
2130
* destroyRawMonitor(gdata->callbackLock);
2131
* destroyRawMonitor(gdata->callbackBlock);
2132
* destroyRawMonitor(gdata->gc_finish_lock);
2133
* destroyRawMonitor(gdata->object_free_lock);
2134
* destroyRawMonitor(gdata->listener_loop_lock);
2135
* destroyRawMonitor(gdata->cpu_loop_lock);
2136
* disposeEnvironment();
2137
* gdata->jvmti = NULL;
2138
*/
2139
2140
/* Destroy basic locks */
2141
destroyRawMonitor(gdata->dump_lock);
2142
gdata->dump_lock = NULL;
2143
destroyRawMonitor(gdata->data_access_lock);
2144
gdata->data_access_lock = NULL;
2145
if ( gdata->cpu_sample_lock != NULL ) {
2146
destroyRawMonitor(gdata->cpu_sample_lock);
2147
gdata->cpu_sample_lock = NULL;
2148
}
2149
#ifdef DEBUG
2150
destroyRawMonitor(gdata->debug_malloc_lock);
2151
gdata->debug_malloc_lock = NULL;
2152
#endif
2153
2154
/* Unload java_crw_demo library */
2155
if ( gdata->bci && gdata->java_crw_demo_library != NULL ) {
2156
md_unload_library(gdata->java_crw_demo_library);
2157
gdata->java_crw_demo_library = NULL;
2158
}
2159
2160
/* You would think you could clear out gdata and set it to NULL, but
2161
* turns out that isn't a good idea. Some of the threads could be
2162
* blocked inside the CALLBACK*() macros, where they got blocked up
2163
* waiting for the VM_DEATH callback to complete. They only have
2164
* some raw monitor actions to do, but they need access to gdata to do it.
2165
* So do not do this:
2166
* (void)memset(gdata, 0, sizeof(GlobalData));
2167
* gdata = NULL;
2168
*/
2169
}
2170
2171