mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8014135
: The JVMTI specification does not conform to recent changes in JNI specification
Added support for statically linked agents Reviewed-by: sspitsyn, bobv, coleenp
This commit is contained in:
parent
6549fc2f25
commit
993de8ba28
8 changed files with 373 additions and 65 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -260,6 +260,55 @@ FILE* os::open(int fd, const char* mode) {
|
||||||
return ::fdopen(fd, mode);
|
return ::fdopen(fd, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* os::get_default_process_handle() {
|
||||||
|
return (void*)::dlopen(NULL, RTLD_LAZY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a platform dependent Agent_OnLoad_<lib_name> function name
|
||||||
|
// which is used to find statically linked in agents.
|
||||||
|
// Parameters:
|
||||||
|
// sym_name: Symbol in library we are looking for
|
||||||
|
// lib_name: Name of library to look in, NULL for shared libs.
|
||||||
|
// is_absolute_path == true if lib_name is absolute path to agent
|
||||||
|
// such as "/a/b/libL.so"
|
||||||
|
// == false if only the base name of the library is passed in
|
||||||
|
// such as "L"
|
||||||
|
char* os::build_agent_function_name(const char *sym_name, const char *lib_name,
|
||||||
|
bool is_absolute_path) {
|
||||||
|
char *agent_entry_name;
|
||||||
|
size_t len;
|
||||||
|
size_t name_len;
|
||||||
|
size_t prefix_len = strlen(JNI_LIB_PREFIX);
|
||||||
|
size_t suffix_len = strlen(JNI_LIB_SUFFIX);
|
||||||
|
const char *start;
|
||||||
|
|
||||||
|
if (lib_name != NULL) {
|
||||||
|
len = name_len = strlen(lib_name);
|
||||||
|
if (is_absolute_path) {
|
||||||
|
// Need to strip path, prefix and suffix
|
||||||
|
if ((start = strrchr(lib_name, *os::file_separator())) != NULL) {
|
||||||
|
lib_name = ++start;
|
||||||
|
}
|
||||||
|
if (len <= (prefix_len + suffix_len)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lib_name += prefix_len;
|
||||||
|
name_len = strlen(lib_name) - suffix_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len = (lib_name != NULL ? name_len : 0) + strlen(sym_name) + 2;
|
||||||
|
agent_entry_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len, mtThread);
|
||||||
|
if (agent_entry_name == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
strcpy(agent_entry_name, sym_name);
|
||||||
|
if (lib_name != NULL) {
|
||||||
|
strcat(agent_entry_name, "_");
|
||||||
|
strncat(agent_entry_name, lib_name, name_len);
|
||||||
|
}
|
||||||
|
return agent_entry_name;
|
||||||
|
}
|
||||||
|
|
||||||
os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() {
|
os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() {
|
||||||
assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread");
|
assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5394,6 +5394,75 @@ inline BOOL os::Advapi32Dll::AdvapiAvailable() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* os::get_default_process_handle() {
|
||||||
|
return (void*)GetModuleHandle(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a platform dependent Agent_OnLoad_<lib_name> function name
|
||||||
|
// which is used to find statically linked in agents.
|
||||||
|
// Additionally for windows, takes into account __stdcall names.
|
||||||
|
// Parameters:
|
||||||
|
// sym_name: Symbol in library we are looking for
|
||||||
|
// lib_name: Name of library to look in, NULL for shared libs.
|
||||||
|
// is_absolute_path == true if lib_name is absolute path to agent
|
||||||
|
// such as "C:/a/b/L.dll"
|
||||||
|
// == false if only the base name of the library is passed in
|
||||||
|
// such as "L"
|
||||||
|
char* os::build_agent_function_name(const char *sym_name, const char *lib_name,
|
||||||
|
bool is_absolute_path) {
|
||||||
|
char *agent_entry_name;
|
||||||
|
size_t len;
|
||||||
|
size_t name_len;
|
||||||
|
size_t prefix_len = strlen(JNI_LIB_PREFIX);
|
||||||
|
size_t suffix_len = strlen(JNI_LIB_SUFFIX);
|
||||||
|
const char *start;
|
||||||
|
|
||||||
|
if (lib_name != NULL) {
|
||||||
|
len = name_len = strlen(lib_name);
|
||||||
|
if (is_absolute_path) {
|
||||||
|
// Need to strip path, prefix and suffix
|
||||||
|
if ((start = strrchr(lib_name, *os::file_separator())) != NULL) {
|
||||||
|
lib_name = ++start;
|
||||||
|
} else {
|
||||||
|
// Need to check for C:
|
||||||
|
if ((start = strchr(lib_name, ':')) != NULL) {
|
||||||
|
lib_name = ++start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len <= (prefix_len + suffix_len)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lib_name += prefix_len;
|
||||||
|
name_len = strlen(lib_name) - suffix_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len = (lib_name != NULL ? name_len : 0) + strlen(sym_name) + 2;
|
||||||
|
agent_entry_name = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len, mtThread);
|
||||||
|
if (agent_entry_name == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (lib_name != NULL) {
|
||||||
|
const char *p = strrchr(sym_name, '@');
|
||||||
|
if (p != NULL && p != sym_name) {
|
||||||
|
// sym_name == _Agent_OnLoad@XX
|
||||||
|
strncpy(agent_entry_name, sym_name, (p - sym_name));
|
||||||
|
agent_entry_name[(p-sym_name)] = '\0';
|
||||||
|
// agent_entry_name == _Agent_OnLoad
|
||||||
|
strcat(agent_entry_name, "_");
|
||||||
|
strncat(agent_entry_name, lib_name, name_len);
|
||||||
|
strcat(agent_entry_name, p);
|
||||||
|
// agent_entry_name == _Agent_OnLoad_lib_name@XX
|
||||||
|
} else {
|
||||||
|
strcpy(agent_entry_name, sym_name);
|
||||||
|
strcat(agent_entry_name, "_");
|
||||||
|
strncat(agent_entry_name, lib_name, name_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strcpy(agent_entry_name, sym_name);
|
||||||
|
}
|
||||||
|
return agent_entry_name;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Kernel32 API
|
// Kernel32 API
|
||||||
typedef BOOL (WINAPI* SwitchToThread_Fn)(void);
|
typedef BOOL (WINAPI* SwitchToThread_Fn)(void);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
<?xml-stylesheet type="text/xsl" href="jvmti.xsl"?>
|
<?xml-stylesheet type="text/xsl" href="jvmti.xsl"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
|
Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
|
||||||
This code is free software; you can redistribute it and/or modify it
|
This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -358,7 +358,7 @@
|
||||||
<specification label="JVM(TM) Tool Interface"
|
<specification label="JVM(TM) Tool Interface"
|
||||||
majorversion="1"
|
majorversion="1"
|
||||||
minorversion="2"
|
minorversion="2"
|
||||||
microversion="2">
|
microversion="3">
|
||||||
<title subtitle="Version">
|
<title subtitle="Version">
|
||||||
<tm>JVM</tm> Tool Interface
|
<tm>JVM</tm> Tool Interface
|
||||||
</title>
|
</title>
|
||||||
|
@ -431,6 +431,7 @@
|
||||||
On the <tm>Solaris</tm> Operating Environment, an agent library is a shared
|
On the <tm>Solaris</tm> Operating Environment, an agent library is a shared
|
||||||
object (<code>.so</code> file).
|
object (<code>.so</code> file).
|
||||||
<p/>
|
<p/>
|
||||||
|
|
||||||
An agent may be started at VM startup by specifying the agent library
|
An agent may be started at VM startup by specifying the agent library
|
||||||
name using a <internallink id="starting">command line option</internallink>.
|
name using a <internallink id="starting">command line option</internallink>.
|
||||||
Some implementations may support a mechanism to <internallink id="onattach">
|
Some implementations may support a mechanism to <internallink id="onattach">
|
||||||
|
@ -438,6 +439,39 @@
|
||||||
The details of how this is initiated are implementation specific.
|
The details of how this is initiated are implementation specific.
|
||||||
</intro>
|
</intro>
|
||||||
|
|
||||||
|
<intro id="entry point" label="Statically Linked Agents (since version 1.2.3)">
|
||||||
|
|
||||||
|
A native JVMTI Agent may be <i>statically linked</i> with the VM.
|
||||||
|
The manner in which the library and VM image are combined is
|
||||||
|
implementation-dependent.
|
||||||
|
An agent L whose image has been combined with the VM is defined as
|
||||||
|
<i>statically linked</i> if and only if the agent exports a function
|
||||||
|
called Agent_OnLoad_L.
|
||||||
|
<p/>
|
||||||
|
If a <i>statically linked</i> agent L exports a function called
|
||||||
|
Agent_OnLoad_L and a function called Agent_OnLoad, the Agent_OnLoad
|
||||||
|
function will be ignored.
|
||||||
|
If an agent L is <i>statically linked</i>, an Agent_OnLoad_L
|
||||||
|
function will be invoked with the same arguments and expected return
|
||||||
|
value as specified for the Agent_OnLoad function.
|
||||||
|
An agent L that is <i>statically linked</i> will prohibit an agent of
|
||||||
|
the same name from being loaded dynamically.
|
||||||
|
<p/>
|
||||||
|
The VM will invoke the Agent_OnUnload_L function of the agent, if such
|
||||||
|
a function is exported, at the same point during startup as it would
|
||||||
|
have called the dynamic entry point Agent_OnUnLoad.
|
||||||
|
If a <i>statically linked</i> agent L exports a function called
|
||||||
|
Agent_OnUnLoad_L and a function called Agent_OnUnLoad, the Agent_OnUnLoad
|
||||||
|
function will be ignored.
|
||||||
|
<p/>
|
||||||
|
If an agent L is <i>statically linked</i>, an Agent_OnAttach_L function
|
||||||
|
will be invoked with the same arguments and expected return value as
|
||||||
|
specified for the Agent_OnAttach function.
|
||||||
|
If a <i>statically linked</i> agent L exports a function called
|
||||||
|
Agent_OnAttach_L and a function called Agent_OnAttach, the Agent_OnAttach
|
||||||
|
function will be ignored.
|
||||||
|
</intro>
|
||||||
|
|
||||||
<intro id="starting" label="Agent Command Line Options">
|
<intro id="starting" label="Agent Command Line Options">
|
||||||
The term "command-line option" is used below to
|
The term "command-line option" is used below to
|
||||||
mean options supplied in the <code>JavaVMInitArgs</code> argument
|
mean options supplied in the <code>JavaVMInitArgs</code> argument
|
||||||
|
@ -463,7 +497,11 @@
|
||||||
<code>-agentlib:foo=opt1,opt2</code> is specified, the VM will attempt to
|
<code>-agentlib:foo=opt1,opt2</code> is specified, the VM will attempt to
|
||||||
load the shared library <code>foo.dll</code> from the system <code>PATH</code>
|
load the shared library <code>foo.dll</code> from the system <code>PATH</code>
|
||||||
under <tm>Windows</tm> or <code>libfoo.so</code> from the
|
under <tm>Windows</tm> or <code>libfoo.so</code> from the
|
||||||
<code>LD_LIBRARY_PATH</code> under the <tm>Solaris</tm> operating environment.
|
<code>LD_LIBRARY_PATH</code> under the <tm>Solaris</tm> operating
|
||||||
|
environment.
|
||||||
|
If the agent library is statically linked into the executable
|
||||||
|
then no actual loading takes place.
|
||||||
|
<p/>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><code>-agentpath:</code><i><path-to-agent></i><code>=</code><i><options></i></dt>
|
<dt><code>-agentpath:</code><i><path-to-agent></i><code>=</code><i><options></i></dt>
|
||||||
<dd>
|
<dd>
|
||||||
|
@ -473,11 +511,20 @@
|
||||||
The <i><options></i> will be passed to the agent on start-up.
|
The <i><options></i> will be passed to the agent on start-up.
|
||||||
For example, if the option
|
For example, if the option
|
||||||
<code>-agentpath:c:\myLibs\foo.dll=opt1,opt2</code> is specified, the VM will attempt to
|
<code>-agentpath:c:\myLibs\foo.dll=opt1,opt2</code> is specified, the VM will attempt to
|
||||||
load the shared library <code>c:\myLibs\foo.dll</code>.
|
load the shared library <code>c:\myLibs\foo.dll</code>. If the agent
|
||||||
|
library is statically linked into the executable
|
||||||
|
then no actual loading takes place.
|
||||||
|
<p/>
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
The start-up routine <internallink id="onload"><code>Agent_OnLoad</code></internallink>
|
For a dynamic shared library agent, the start-up routine
|
||||||
in the library will be invoked.
|
<internallink id="onload"><code>Agent_OnLoad</code></internallink>
|
||||||
|
in the library will be invoked. If the agent library is statically linked
|
||||||
|
into the executable then the system will attempt to invoke the
|
||||||
|
<code>Agent_OnLoad_<agent-lib-name></code> entry point where
|
||||||
|
<agent-lib-name> is the basename of the
|
||||||
|
agent. In the above example <code>-agentpath:c:\myLibs\foo.dll=opt1,opt2</code>,
|
||||||
|
the system will attempt to find and call the <code>Agent_OnLoad_foo</code> start-up routine.
|
||||||
<p/>
|
<p/>
|
||||||
Libraries loaded with <code>-agentlib:</code> or <code>-agentpath:</code>
|
Libraries loaded with <code>-agentlib:</code> or <code>-agentpath:</code>
|
||||||
will be searched for JNI native method implementations to facilitate the
|
will be searched for JNI native method implementations to facilitate the
|
||||||
|
@ -502,11 +549,13 @@
|
||||||
If the agent is started in the <code>OnLoad</code>
|
If the agent is started in the <code>OnLoad</code>
|
||||||
<functionlink id="GetPhase">phase</functionlink> the function
|
<functionlink id="GetPhase">phase</functionlink> the function
|
||||||
<internallink id="onload"><code>Agent_OnLoad</code></internallink>
|
<internallink id="onload"><code>Agent_OnLoad</code></internallink>
|
||||||
will be invoked.
|
or <internallink id="onload"><code>Agent_OnLoad_L</code></internallink>
|
||||||
|
for statically linked agents will be invoked.
|
||||||
If the agent is started in the live
|
If the agent is started in the live
|
||||||
<functionlink id="GetPhase">phase</functionlink> the function
|
<functionlink id="GetPhase">phase</functionlink> the function
|
||||||
<internallink id="onattach"><code>Agent_OnAttach</code></internallink>
|
<internallink id="onattach"><code>Agent_OnAttach</code></internallink>
|
||||||
will be invoked.
|
or <internallink id="onattach"><code>Agent_OnAttach_L</code></internallink>
|
||||||
|
for statically linked agents will be invoked.
|
||||||
Exactly one call to a start-up function is made per agent.
|
Exactly one call to a start-up function is made per agent.
|
||||||
</intro>
|
</intro>
|
||||||
|
|
||||||
|
@ -516,6 +565,11 @@
|
||||||
<example>
|
<example>
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)</example>
|
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)</example>
|
||||||
|
Or for a statically linked agent named 'L':
|
||||||
|
<example>
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Agent_OnLoad_L(JavaVM *vm, char *options, void *reserved)</example>
|
||||||
|
|
||||||
The VM will start the agent by calling this function.
|
The VM will start the agent by calling this function.
|
||||||
It will be called early enough in VM initialization that:
|
It will be called early enough in VM initialization that:
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -531,7 +585,8 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved)</example>
|
||||||
<li>no objects have been created</li>
|
<li>no objects have been created</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p/>
|
<p/>
|
||||||
The VM will call the <code>Agent_OnLoad</code> function with
|
The VM will call the <code>Agent_OnLoad</code> or
|
||||||
|
<code>Agent_OnLoad_<agent-lib-name></code> function with
|
||||||
<i><options></i> as the second argument -
|
<i><options></i> as the second argument -
|
||||||
that is, using the command-line option examples,
|
that is, using the command-line option examples,
|
||||||
<code>"opt1,opt2"</code> will be passed to the <code>char *options</code>
|
<code>"opt1,opt2"</code> will be passed to the <code>char *options</code>
|
||||||
|
@ -540,7 +595,8 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved)</example>
|
||||||
<internallink id="mUTF">modified UTF-8</internallink> string.
|
<internallink id="mUTF">modified UTF-8</internallink> string.
|
||||||
If <i>=<options></i> is not specified,
|
If <i>=<options></i> is not specified,
|
||||||
a zero length string is passed to <code>options</code>.
|
a zero length string is passed to <code>options</code>.
|
||||||
The lifespan of the <code>options</code> string is the <code>Agent_OnLoad</code>
|
The lifespan of the <code>options</code> string is the
|
||||||
|
<code>Agent_OnLoad</code> or <code>Agent_OnLoad_<agent-lib-name></code>
|
||||||
call. If needed beyond this time the string or parts of the string must
|
call. If needed beyond this time the string or parts of the string must
|
||||||
be copied.
|
be copied.
|
||||||
The period between when <code>Agent_OnLoad</code> is called and when it
|
The period between when <code>Agent_OnLoad</code> is called and when it
|
||||||
|
@ -570,7 +626,8 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved)</example>
|
||||||
their functionality.
|
their functionality.
|
||||||
</rationale>
|
</rationale>
|
||||||
<p/>
|
<p/>
|
||||||
The return value from <code>Agent_OnLoad</code> is used to indicate an error.
|
The return value from <code>Agent_OnLoad</code> or
|
||||||
|
<code>Agent_OnLoad_<agent-lib-name></code> is used to indicate an error.
|
||||||
Any value other than zero indicates an error and causes termination of the VM.
|
Any value other than zero indicates an error and causes termination of the VM.
|
||||||
</intro>
|
</intro>
|
||||||
|
|
||||||
|
@ -587,6 +644,11 @@ Agent_OnLoad(JavaVM *vm, char *options, void *reserved)</example>
|
||||||
<example>
|
<example>
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Agent_OnAttach(JavaVM* vm, char *options, void *reserved)</example>
|
Agent_OnAttach(JavaVM* vm, char *options, void *reserved)</example>
|
||||||
|
Or for a statically linked agent named 'L':
|
||||||
|
<example>
|
||||||
|
JNIEXPORT jint JNICALL
|
||||||
|
Agent_OnAttach_L(JavaVM* vm, char *options, void *reserved)</example>
|
||||||
|
|
||||||
<p/>
|
<p/>
|
||||||
The VM will start the agent by calling this function.
|
The VM will start the agent by calling this function.
|
||||||
It will be called in the context of a thread
|
It will be called in the context of a thread
|
||||||
|
@ -596,13 +658,14 @@ Agent_OnAttach(JavaVM* vm, char *options, void *reserved)</example>
|
||||||
</internallink> string.
|
</internallink> string.
|
||||||
If startup options were not provided, a zero length string is passed to
|
If startup options were not provided, a zero length string is passed to
|
||||||
<code>options</code>. The lifespan of the <code>options</code> string is the
|
<code>options</code>. The lifespan of the <code>options</code> string is the
|
||||||
<code>Agent_OnAttach</code> call. If needed beyond this time the string or parts of
|
<code>Agent_OnAttach</code> or <code>Agent_OnAttach_<agent-lib-name></code> call.
|
||||||
the string must be copied.
|
If needed beyond this time the string or parts of the string must be copied.
|
||||||
<p/>
|
<p/>
|
||||||
Note that some <internallink id="capability">capabilities</internallink>
|
Note that some <internallink id="capability">capabilities</internallink>
|
||||||
may not be available in the live phase.
|
may not be available in the live phase.
|
||||||
<p/>
|
<p/>
|
||||||
The <code>Agent_OnAttach</code> function initializes the agent and returns a value
|
The <code>Agent_OnAttach</code> or <code>Agent_OnAttach_<agent-lib-name
|
||||||
|
></code> function initializes the agent and returns a value
|
||||||
to the VM to indicate if an error occurred. Any value other than zero indicates an error.
|
to the VM to indicate if an error occurred. Any value other than zero indicates an error.
|
||||||
An error does not cause the VM to terminate. Instead the VM ignores the error, or takes
|
An error does not cause the VM to terminate. Instead the VM ignores the error, or takes
|
||||||
some implementation specific action -- for example it might print an error to standard error,
|
some implementation specific action -- for example it might print an error to standard error,
|
||||||
|
@ -615,8 +678,14 @@ Agent_OnAttach(JavaVM* vm, char *options, void *reserved)</example>
|
||||||
<example>
|
<example>
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Agent_OnUnload(JavaVM *vm)</example>
|
Agent_OnUnload(JavaVM *vm)</example>
|
||||||
|
Or for a statically linked agent named 'L':
|
||||||
|
<example>
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Agent_OnUnload_L(JavaVM *vm)</example>
|
||||||
|
|
||||||
This function will be called by the VM when the library is about to be unloaded.
|
This function will be called by the VM when the library is about to be unloaded.
|
||||||
The library will be unloaded and this function will be called if some platform specific
|
The library will be unloaded (unless it is statically linked into the
|
||||||
|
executable) and this function will be called if some platform specific
|
||||||
mechanism causes the unload (an unload mechanism is not specified in this document)
|
mechanism causes the unload (an unload mechanism is not specified in this document)
|
||||||
or the library is (in effect) unloaded by the termination of the VM whether through
|
or the library is (in effect) unloaded by the termination of the VM whether through
|
||||||
normal termination or VM failure, including start-up failure.
|
normal termination or VM failure, including start-up failure.
|
||||||
|
@ -625,8 +694,9 @@ Agent_OnUnload(JavaVM *vm)</example>
|
||||||
<eventlink id="VMDeath">VM Death event</eventlink>: for the VM Death event
|
<eventlink id="VMDeath">VM Death event</eventlink>: for the VM Death event
|
||||||
to be sent, the VM must have run at least to the point of initialization and a valid
|
to be sent, the VM must have run at least to the point of initialization and a valid
|
||||||
<jvmti/> environment must exist which has set a callback for VMDeath
|
<jvmti/> environment must exist which has set a callback for VMDeath
|
||||||
and enabled the event
|
and enabled the event.
|
||||||
None of these are required for <code>Agent_OnUnload</code> and this function
|
None of these are required for <code>Agent_OnUnload</code> or
|
||||||
|
<code>Agent_OnUnload_<agent-lib-name></code> and this function
|
||||||
is also called if the library is unloaded for other reasons.
|
is also called if the library is unloaded for other reasons.
|
||||||
In the case that a VM Death event is sent, it will be sent before this
|
In the case that a VM Death event is sent, it will be sent before this
|
||||||
function is called (assuming this function is called due to VM termination).
|
function is called (assuming this function is called due to VM termination).
|
||||||
|
@ -10701,10 +10771,14 @@ myInit() {
|
||||||
<constants id="jvmtiPhase" label="Phases of execution" kind="enum">
|
<constants id="jvmtiPhase" label="Phases of execution" kind="enum">
|
||||||
<constant id="JVMTI_PHASE_ONLOAD" num="1">
|
<constant id="JVMTI_PHASE_ONLOAD" num="1">
|
||||||
<code>OnLoad</code> phase: while in the
|
<code>OnLoad</code> phase: while in the
|
||||||
<internallink id="onload"><code>Agent_OnLoad</code></internallink> function.
|
<internallink id="onload"><code>Agent_OnLoad</code></internallink>
|
||||||
|
or, for statically linked agents, the <internallink id="onload">
|
||||||
|
<code>Agent_OnLoad_<agent-lib-name>
|
||||||
|
</code></internallink> function.
|
||||||
</constant>
|
</constant>
|
||||||
<constant id="JVMTI_PHASE_PRIMORDIAL" num="2">
|
<constant id="JVMTI_PHASE_PRIMORDIAL" num="2">
|
||||||
Primordial phase: between return from <code>Agent_OnLoad</code> and the
|
Primordial phase: between return from <code>Agent_OnLoad</code>
|
||||||
|
or <code>Agent_OnLoad_<agent-lib-name></code> and the
|
||||||
<code>VMStart</code> event.
|
<code>VMStart</code> event.
|
||||||
</constant>
|
</constant>
|
||||||
<constant id="JVMTI_PHASE_START" num="6">
|
<constant id="JVMTI_PHASE_START" num="6">
|
||||||
|
@ -14261,6 +14335,9 @@ typedef void (JNICALL *jvmtiEventVMInit)
|
||||||
<change date="11 October 2012" version="1.2.2">
|
<change date="11 October 2012" version="1.2.2">
|
||||||
Fixed the "HTTP" and "Missing Anchor" errors reported by the LinkCheck tool.
|
Fixed the "HTTP" and "Missing Anchor" errors reported by the LinkCheck tool.
|
||||||
</change>
|
</change>
|
||||||
|
<change date="19 June 2013" version="1.2.3">
|
||||||
|
Added support for statically linked agents.
|
||||||
|
</change>
|
||||||
</changehistory>
|
</changehistory>
|
||||||
|
|
||||||
</specification>
|
</specification>
|
||||||
|
|
|
@ -2191,6 +2191,8 @@ jint JvmtiExport::load_agent_library(AttachOperation* op, outputStream* st) {
|
||||||
char buffer[JVM_MAXPATHLEN];
|
char buffer[JVM_MAXPATHLEN];
|
||||||
void* library = NULL;
|
void* library = NULL;
|
||||||
jint result = JNI_ERR;
|
jint result = JNI_ERR;
|
||||||
|
const char *on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS;
|
||||||
|
size_t num_symbol_entries = ARRAY_SIZE(on_attach_symbols);
|
||||||
|
|
||||||
// get agent name and options
|
// get agent name and options
|
||||||
const char* agent = op->arg(0);
|
const char* agent = op->arg(0);
|
||||||
|
@ -2200,10 +2202,14 @@ jint JvmtiExport::load_agent_library(AttachOperation* op, outputStream* st) {
|
||||||
// The abs paramter should be "true" or "false"
|
// The abs paramter should be "true" or "false"
|
||||||
bool is_absolute_path = (absParam != NULL) && (strcmp(absParam,"true")==0);
|
bool is_absolute_path = (absParam != NULL) && (strcmp(absParam,"true")==0);
|
||||||
|
|
||||||
|
// Initially marked as invalid. It will be set to valid if we can find the agent
|
||||||
|
AgentLibrary *agent_lib = new AgentLibrary(agent, options, is_absolute_path, NULL);
|
||||||
|
|
||||||
// If the path is absolute we attempt to load the library. Otherwise we try to
|
// Check for statically linked in agent. If not found then if the path is
|
||||||
// load it from the standard dll directory.
|
// absolute we attempt to load the library. Otherwise we try to load it
|
||||||
|
// from the standard dll directory.
|
||||||
|
|
||||||
|
if (!os::find_builtin_agent(agent_lib, on_attach_symbols, num_symbol_entries)) {
|
||||||
if (is_absolute_path) {
|
if (is_absolute_path) {
|
||||||
library = os::dll_load(agent, ebuf, sizeof ebuf);
|
library = os::dll_load(agent, ebuf, sizeof ebuf);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2220,23 +2226,24 @@ jint JvmtiExport::load_agent_library(AttachOperation* op, outputStream* st) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (library != NULL) {
|
||||||
|
agent_lib->set_os_lib(library);
|
||||||
|
agent_lib->set_valid();
|
||||||
|
}
|
||||||
|
}
|
||||||
// If the library was loaded then we attempt to invoke the Agent_OnAttach
|
// If the library was loaded then we attempt to invoke the Agent_OnAttach
|
||||||
// function
|
// function
|
||||||
if (library != NULL) {
|
if (agent_lib->valid()) {
|
||||||
|
|
||||||
// Lookup the Agent_OnAttach function
|
// Lookup the Agent_OnAttach function
|
||||||
OnAttachEntry_t on_attach_entry = NULL;
|
OnAttachEntry_t on_attach_entry = NULL;
|
||||||
const char *on_attach_symbols[] = AGENT_ONATTACH_SYMBOLS;
|
on_attach_entry = CAST_TO_FN_PTR(OnAttachEntry_t,
|
||||||
for (uint symbol_index = 0; symbol_index < ARRAY_SIZE(on_attach_symbols); symbol_index++) {
|
os::find_agent_function(agent_lib, false, on_attach_symbols, num_symbol_entries));
|
||||||
on_attach_entry =
|
|
||||||
CAST_TO_FN_PTR(OnAttachEntry_t, os::dll_lookup(library, on_attach_symbols[symbol_index]));
|
|
||||||
if (on_attach_entry != NULL) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (on_attach_entry == NULL) {
|
if (on_attach_entry == NULL) {
|
||||||
// Agent_OnAttach missing - unload library
|
// Agent_OnAttach missing - unload library
|
||||||
|
if (!agent_lib->is_static_lib()) {
|
||||||
os::dll_unload(library);
|
os::dll_unload(library);
|
||||||
|
}
|
||||||
|
delete agent_lib;
|
||||||
} else {
|
} else {
|
||||||
// Invoke the Agent_OnAttach function
|
// Invoke the Agent_OnAttach function
|
||||||
JavaThread* THREAD = JavaThread::current();
|
JavaThread* THREAD = JavaThread::current();
|
||||||
|
@ -2256,7 +2263,9 @@ jint JvmtiExport::load_agent_library(AttachOperation* op, outputStream* st) {
|
||||||
// If OnAttach returns JNI_OK then we add it to the list of
|
// If OnAttach returns JNI_OK then we add it to the list of
|
||||||
// agent libraries so that we can call Agent_OnUnload later.
|
// agent libraries so that we can call Agent_OnUnload later.
|
||||||
if (result == JNI_OK) {
|
if (result == JNI_OK) {
|
||||||
Arguments::add_loaded_agent(agent, (char*)options, is_absolute_path, library);
|
Arguments::add_loaded_agent(agent_lib);
|
||||||
|
} else {
|
||||||
|
delete agent_lib;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agent_OnAttach executed so completion status is JNI_OK
|
// Agent_OnAttach executed so completion status is JNI_OK
|
||||||
|
|
|
@ -118,11 +118,21 @@ class SystemProperty: public CHeapObj<mtInternal> {
|
||||||
// For use by -agentlib, -agentpath and -Xrun
|
// For use by -agentlib, -agentpath and -Xrun
|
||||||
class AgentLibrary : public CHeapObj<mtInternal> {
|
class AgentLibrary : public CHeapObj<mtInternal> {
|
||||||
friend class AgentLibraryList;
|
friend class AgentLibraryList;
|
||||||
|
public:
|
||||||
|
// Is this library valid or not. Don't rely on os_lib == NULL as statically
|
||||||
|
// linked lib could have handle of RTLD_DEFAULT which == 0 on some platforms
|
||||||
|
enum AgentState {
|
||||||
|
agent_invalid = 0,
|
||||||
|
agent_valid = 1
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char* _name;
|
char* _name;
|
||||||
char* _options;
|
char* _options;
|
||||||
void* _os_lib;
|
void* _os_lib;
|
||||||
bool _is_absolute_path;
|
bool _is_absolute_path;
|
||||||
|
bool _is_static_lib;
|
||||||
|
AgentState _state;
|
||||||
AgentLibrary* _next;
|
AgentLibrary* _next;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -133,6 +143,11 @@ class AgentLibrary : public CHeapObj<mtInternal> {
|
||||||
void* os_lib() const { return _os_lib; }
|
void* os_lib() const { return _os_lib; }
|
||||||
void set_os_lib(void* os_lib) { _os_lib = os_lib; }
|
void set_os_lib(void* os_lib) { _os_lib = os_lib; }
|
||||||
AgentLibrary* next() const { return _next; }
|
AgentLibrary* next() const { return _next; }
|
||||||
|
bool is_static_lib() const { return _is_static_lib; }
|
||||||
|
void set_static_lib(bool static_lib) { _is_static_lib = static_lib; }
|
||||||
|
bool valid() { return (_state == agent_valid); }
|
||||||
|
void set_valid() { _state = agent_valid; }
|
||||||
|
void set_invalid() { _state = agent_invalid; }
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) {
|
AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) {
|
||||||
|
@ -147,6 +162,8 @@ class AgentLibrary : public CHeapObj<mtInternal> {
|
||||||
_is_absolute_path = is_absolute_path;
|
_is_absolute_path = is_absolute_path;
|
||||||
_os_lib = os_lib;
|
_os_lib = os_lib;
|
||||||
_next = NULL;
|
_next = NULL;
|
||||||
|
_state = agent_invalid;
|
||||||
|
_is_static_lib = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -276,6 +293,8 @@ class Arguments : AllStatic {
|
||||||
{ _agentList.add(new AgentLibrary(name, options, absolute_path, NULL)); }
|
{ _agentList.add(new AgentLibrary(name, options, absolute_path, NULL)); }
|
||||||
|
|
||||||
// Late-binding agents not started via arguments
|
// Late-binding agents not started via arguments
|
||||||
|
static void add_loaded_agent(AgentLibrary *agentLib)
|
||||||
|
{ _agentList.add(agentLib); }
|
||||||
static void add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib)
|
static void add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib)
|
||||||
{ _agentList.add(new AgentLibrary(name, options, absolute_path, os_lib)); }
|
{ _agentList.add(new AgentLibrary(name, options, absolute_path, os_lib)); }
|
||||||
|
|
||||||
|
|
|
@ -443,6 +443,67 @@ void* os::native_java_library() {
|
||||||
return _native_java_library;
|
return _native_java_library;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Support for finding Agent_On(Un)Load/Attach<_lib_name> if it exists.
|
||||||
|
* If check_lib == true then we are looking for an
|
||||||
|
* Agent_OnLoad_lib_name or Agent_OnAttach_lib_name function to determine if
|
||||||
|
* this library is statically linked into the image.
|
||||||
|
* If check_lib == false then we will look for the appropriate symbol in the
|
||||||
|
* executable if agent_lib->is_static_lib() == true or in the shared library
|
||||||
|
* referenced by 'handle'.
|
||||||
|
*/
|
||||||
|
void* os::find_agent_function(AgentLibrary *agent_lib, bool check_lib,
|
||||||
|
const char *syms[], size_t syms_len) {
|
||||||
|
const char *lib_name;
|
||||||
|
void *handle = agent_lib->os_lib();
|
||||||
|
void *entryName = NULL;
|
||||||
|
char *agent_function_name;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
// If checking then use the agent name otherwise test is_static_lib() to
|
||||||
|
// see how to process this lookup
|
||||||
|
lib_name = ((check_lib || agent_lib->is_static_lib()) ? agent_lib->name() : NULL);
|
||||||
|
for (i = 0; i < syms_len; i++) {
|
||||||
|
agent_function_name = build_agent_function_name(syms[i], lib_name, agent_lib->is_absolute_path());
|
||||||
|
if (agent_function_name == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entryName = dll_lookup(handle, agent_function_name);
|
||||||
|
FREE_C_HEAP_ARRAY(char, agent_function_name, mtThread);
|
||||||
|
if (entryName != NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if the passed in agent is statically linked into the VM image.
|
||||||
|
bool os::find_builtin_agent(AgentLibrary *agent_lib, const char *syms[],
|
||||||
|
size_t syms_len) {
|
||||||
|
void *ret;
|
||||||
|
void *proc_handle;
|
||||||
|
void *save_handle;
|
||||||
|
|
||||||
|
if (agent_lib->name() == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
proc_handle = get_default_process_handle();
|
||||||
|
// Check for Agent_OnLoad/Attach_lib_name function
|
||||||
|
save_handle = agent_lib->os_lib();
|
||||||
|
// We want to look in this process' symbol table.
|
||||||
|
agent_lib->set_os_lib(proc_handle);
|
||||||
|
ret = find_agent_function(agent_lib, true, syms, syms_len);
|
||||||
|
agent_lib->set_os_lib(save_handle);
|
||||||
|
if (ret != NULL) {
|
||||||
|
// Found an entry point like Agent_OnLoad_lib_name so we have a static agent
|
||||||
|
agent_lib->set_os_lib(proc_handle);
|
||||||
|
agent_lib->set_valid();
|
||||||
|
agent_lib->set_static_lib(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------- heap allocation utilities ---------------------
|
// --------------------- heap allocation utilities ---------------------
|
||||||
|
|
||||||
char *os::strdup(const char *str, MEMFLAGS flags) {
|
char *os::strdup(const char *str, MEMFLAGS flags) {
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
# include <setjmp.h>
|
# include <setjmp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class AgentLibrary;
|
||||||
|
|
||||||
// os defines the interface to operating system; this includes traditional
|
// os defines the interface to operating system; this includes traditional
|
||||||
// OS services (time, I/O) as well as other functionality with system-
|
// OS services (time, I/O) as well as other functionality with system-
|
||||||
// dependent code.
|
// dependent code.
|
||||||
|
@ -537,6 +539,17 @@ class os: AllStatic {
|
||||||
// Unload library
|
// Unload library
|
||||||
static void dll_unload(void *lib);
|
static void dll_unload(void *lib);
|
||||||
|
|
||||||
|
// Return the handle of this process
|
||||||
|
static void* get_default_process_handle();
|
||||||
|
|
||||||
|
// Check for static linked agent library
|
||||||
|
static bool find_builtin_agent(AgentLibrary *agent_lib, const char *syms[],
|
||||||
|
size_t syms_len);
|
||||||
|
|
||||||
|
// Find agent entry point
|
||||||
|
static void *find_agent_function(AgentLibrary *agent_lib, bool check_lib,
|
||||||
|
const char *syms[], size_t syms_len);
|
||||||
|
|
||||||
// Print out system information; they are called by fatal error handler.
|
// Print out system information; they are called by fatal error handler.
|
||||||
// Output format may be different on different platforms.
|
// Output format may be different on different platforms.
|
||||||
static void print_os_info(outputStream* st);
|
static void print_os_info(outputStream* st);
|
||||||
|
@ -806,6 +819,11 @@ class os: AllStatic {
|
||||||
// ResumeThread call)
|
// ResumeThread call)
|
||||||
static void pause();
|
static void pause();
|
||||||
|
|
||||||
|
// Builds a platform dependent Agent_OnLoad_<libname> function name
|
||||||
|
// which is used to find statically linked in agents.
|
||||||
|
static char* build_agent_function_name(const char *sym, const char *cname,
|
||||||
|
bool is_absolute_path);
|
||||||
|
|
||||||
class SuspendedThreadTaskContext {
|
class SuspendedThreadTaskContext {
|
||||||
public:
|
public:
|
||||||
SuspendedThreadTaskContext(Thread* thread, void *ucontext) : _thread(thread), _ucontext(ucontext) {}
|
SuspendedThreadTaskContext(Thread* thread, void *ucontext) : _thread(thread), _ucontext(ucontext) {}
|
||||||
|
|
|
@ -3696,15 +3696,18 @@ extern "C" {
|
||||||
// num_symbol_entries must be passed-in since only the caller knows the number of symbols in the array.
|
// num_symbol_entries must be passed-in since only the caller knows the number of symbols in the array.
|
||||||
static OnLoadEntry_t lookup_on_load(AgentLibrary* agent, const char *on_load_symbols[], size_t num_symbol_entries) {
|
static OnLoadEntry_t lookup_on_load(AgentLibrary* agent, const char *on_load_symbols[], size_t num_symbol_entries) {
|
||||||
OnLoadEntry_t on_load_entry = NULL;
|
OnLoadEntry_t on_load_entry = NULL;
|
||||||
void *library = agent->os_lib(); // check if we have looked it up before
|
void *library = NULL;
|
||||||
|
|
||||||
if (library == NULL) {
|
if (!agent->valid()) {
|
||||||
char buffer[JVM_MAXPATHLEN];
|
char buffer[JVM_MAXPATHLEN];
|
||||||
char ebuf[1024];
|
char ebuf[1024];
|
||||||
const char *name = agent->name();
|
const char *name = agent->name();
|
||||||
const char *msg = "Could not find agent library ";
|
const char *msg = "Could not find agent library ";
|
||||||
|
|
||||||
if (agent->is_absolute_path()) {
|
// First check to see if agent is statcally linked into executable
|
||||||
|
if (os::find_builtin_agent(agent, on_load_symbols, num_symbol_entries)) {
|
||||||
|
library = agent->os_lib();
|
||||||
|
} else if (agent->is_absolute_path()) {
|
||||||
library = os::dll_load(name, ebuf, sizeof ebuf);
|
library = os::dll_load(name, ebuf, sizeof ebuf);
|
||||||
if (library == NULL) {
|
if (library == NULL) {
|
||||||
const char *sub_msg = " in absolute path, with error: ";
|
const char *sub_msg = " in absolute path, with error: ";
|
||||||
|
@ -3738,13 +3741,15 @@ static OnLoadEntry_t lookup_on_load(AgentLibrary* agent, const char *on_load_sym
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
agent->set_os_lib(library);
|
agent->set_os_lib(library);
|
||||||
|
agent->set_valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the OnLoad function.
|
// Find the OnLoad function.
|
||||||
for (size_t symbol_index = 0; symbol_index < num_symbol_entries; symbol_index++) {
|
on_load_entry =
|
||||||
on_load_entry = CAST_TO_FN_PTR(OnLoadEntry_t, os::dll_lookup(library, on_load_symbols[symbol_index]));
|
CAST_TO_FN_PTR(OnLoadEntry_t, os::find_agent_function(agent,
|
||||||
if (on_load_entry != NULL) break;
|
false,
|
||||||
}
|
on_load_symbols,
|
||||||
|
num_symbol_entries));
|
||||||
return on_load_entry;
|
return on_load_entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3819,13 +3824,16 @@ extern "C" {
|
||||||
void Threads::shutdown_vm_agents() {
|
void Threads::shutdown_vm_agents() {
|
||||||
// Send any Agent_OnUnload notifications
|
// Send any Agent_OnUnload notifications
|
||||||
const char *on_unload_symbols[] = AGENT_ONUNLOAD_SYMBOLS;
|
const char *on_unload_symbols[] = AGENT_ONUNLOAD_SYMBOLS;
|
||||||
|
size_t num_symbol_entries = ARRAY_SIZE(on_unload_symbols);
|
||||||
extern struct JavaVM_ main_vm;
|
extern struct JavaVM_ main_vm;
|
||||||
for (AgentLibrary* agent = Arguments::agents(); agent != NULL; agent = agent->next()) {
|
for (AgentLibrary* agent = Arguments::agents(); agent != NULL; agent = agent->next()) {
|
||||||
|
|
||||||
// Find the Agent_OnUnload function.
|
// Find the Agent_OnUnload function.
|
||||||
for (uint symbol_index = 0; symbol_index < ARRAY_SIZE(on_unload_symbols); symbol_index++) {
|
|
||||||
Agent_OnUnload_t unload_entry = CAST_TO_FN_PTR(Agent_OnUnload_t,
|
Agent_OnUnload_t unload_entry = CAST_TO_FN_PTR(Agent_OnUnload_t,
|
||||||
os::dll_lookup(agent->os_lib(), on_unload_symbols[symbol_index]));
|
os::find_agent_function(agent,
|
||||||
|
false,
|
||||||
|
on_unload_symbols,
|
||||||
|
num_symbol_entries));
|
||||||
|
|
||||||
// Invoke the Agent_OnUnload function
|
// Invoke the Agent_OnUnload function
|
||||||
if (unload_entry != NULL) {
|
if (unload_entry != NULL) {
|
||||||
|
@ -3833,8 +3841,6 @@ void Threads::shutdown_vm_agents() {
|
||||||
ThreadToNativeFromVM ttn(thread);
|
ThreadToNativeFromVM ttn(thread);
|
||||||
HandleMark hm(thread);
|
HandleMark hm(thread);
|
||||||
(*unload_entry)(&main_vm);
|
(*unload_entry)(&main_vm);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue