JVMTM Tool Interface
Version 1.1
|
What is the JVM Tool Interface?
The JVMTM Tool Interface (JVM TI)
is a programming interface used by development and monitoring tools.
It provides both a way to inspect the state and
to control the execution of applications running in the
JavaTM virtual machine (VM).
JVM TI is intended to provide a VM interface for the full breadth of tools
that need access to VM state, including but not limited to: profiling,
debugging, monitoring, thread analysis, and coverage analysis tools.
JVM TI may not be available in all implementations of the JavaTM virtual
machine.
JVM TI is a two-way interface.
A client of JVM TI, hereafter called an agent,
can be notified of
interesting occurrences through events.
JVM TI
can query and control the application through many
functions,
either in response to events or
independent of them.
Agents run in the same process with and communicate directly with
the virtual machine executing
the application being examined. This communication is
through a native interface (JVM TI). The native in-process interface allows
maximal control with minimal intrusion on the part of a tool.
Typically, agents are relatively compact. They can be controlled
by a separate process which implements the bulk of a tool's
function without interfering with the target application's normal execution.
Architecture
Tools can be written directly to JVM TI or indirectly
through higher level interfaces.
The Java Platform Debugger Architecture includes JVM TI, but also
contains higher-level, out-of-process debugger interfaces. The higher-level
interfaces are more appropriate than JVM TI for many tools.
For more information on the Java Platform Debugger Architecture,
see the
Java
Platform Debugger Architecture website.
Writing Agents
Agents can be written in any native language that supports C
language calling conventions and C or C++
definitions.
The function, event, data type, and constant definitions needed for
using JVM TI are defined in the include file jvmti.h.
To use these definitions add the J2SETM include directory
to your include path and add
#include <jvmti.h>
to your source code.
Deploying Agents
An agent is deployed in a platform specific manner but is typically the
platform equivalent of a dynamic library. On the WindowsTM operating
system, for example, an agent library is a "Dynamic Linked Library" (DLL).
On the SolarisTM Operating Environment, an agent library is a shared
object (.so file).
An agent may be started at VM startup by specifying the agent library
name using a command line option.
Some implementations may support a mechanism to
start agents in the live phase.
The details of how this is initiated are implementation specific.
Agent Command Line Options
The term "command-line option" is used below to
mean options supplied in the JavaVMInitArgs argument
to the JNI_CreateJavaVM function of the JNI
Invocation API.
One of the two following
command-line options is used on VM startup to
properly load and run agents.
These arguments identify the library containing
the agent as well as an options
string to be passed in at startup.
-
-agentlib:<agent-lib-name>=<options>
-
The name following
-agentlib: is the name of the
library to load. Lookup of the library, both its full name and location,
proceeds in a platform-specific manner.
Typically, the <agent-lib-name> is expanded to an
operating system specific file name.
The <options> will be passed to the agent on start-up.
For example, if the option
-agentlib:foo=opt1,opt2 is specified, the VM will attempt to
load the shared library foo.dll from the system PATH
under WindowsTM or libfoo.so from the
LD_LIBRARY_PATH under the SolarisTM operating environment.
-
-agentpath:<path-to-agent>=<options>
-
The path following
-agentpath: is the absolute path from which
to load the library.
No library name expansion will occur.
The <options> will be passed to the agent on start-up.
For example, if the option
-agentpath:c:\myLibs\foo.dll=opt1,opt2 is specified, the VM will attempt to
load the shared library c:\myLibs\foo.dll.
The start-up routine Agent_OnLoad
in the library will be invoked.
Libraries loaded with -agentlib: or -agentpath:
will be searched for JNI native method implementations to facilitate the
use of Java programming language code in tools, as is needed for
bytecode instrumentation.
The agent libraries will be searched after all other libraries have been
searched (agents wishing to override or intercept the native method
implementations of non-agent methods can use the
NativeMethodBind event).
These switches do the above and nothing more - they do not change the
state of the VM or JVM TI. No command line options are needed
to enable JVM TI
or aspects of JVM TI, this is handled programmatically
by the use of
capabilities.
Agent Start-Up
The VM starts each agent by invoking a start-up function.
If the agent is started in the OnLoad
phase the function
Agent_OnLoad
will be invoked.
If the agent is started in the live
phase the function
Agent_OnAttach
will be invoked.
Exactly one call to a start-up function is made per agent.
Agent Start-Up (OnLoad phase)
If an agent is started during the OnLoad phase then its
agent library must export a start-up function with the following prototype:
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
The VM will start the agent by calling this function.
It will be called early enough in VM initialization that:
-
system properties
may be set before they have been used in the start-up of the VM
- the full set of
capabilities
is still available (note that capabilities that configure the VM
may only be available at this time--see the
Capability function section)
- no bytecodes have executed
- no classes have been loaded
- no objects have been created
The VM will call the Agent_OnLoad function with
<options> as the second argument -
that is, using the command-line option examples,
"opt1,opt2" will be passed to the char *options
argument of Agent_OnLoad.
The options argument is encoded as a
modified UTF-8 string.
If =<options> is not specified,
a zero length string is passed to options.
The lifespan of the options string is the Agent_OnLoad
call. If needed beyond this time the string or parts of the string must
be copied.
The period between when Agent_OnLoad is called and when it
returns is called the OnLoad phase.
Since the VM is not initialized during the OnLoad
phase,
the set of allowed operations
inside Agent_OnLoad is restricted (see the function descriptions for the
functionality available at this time).
The agent can safely process the options and set
event callbacks with SetEventCallbacks. Once
the VM initialization event is received
(that is, the VMInit
callback is invoked), the agent
can complete its initialization.
Rationale:
Early startup is required so that agents can set the desired capabilities,
many of which must be set before the VM is initialized.
In JVMDI, the -Xdebug command-line option provided
very coarse-grain control of capabilities.
JVMPI implementations use various tricks to provide a single "JVMPI on" switch.
No reasonable command-line
option could provide the fine-grain of control required to balance needed capabilities vs
performance impact.
Early startup is also needed so that agents can control the execution
environment - modifying the file system and system properties to install
their functionality.
The return value from Agent_OnLoad is used to indicate an error.
Any value other than zero indicates an error and causes termination of the VM.
Agent Start-Up (Live phase)
A VM may support a mechanism that allows agents to be started in the VM during the live
phase. The details of how this is supported,
are implementation specific. For example, a tool may use some platform specific mechanism,
or implementation specific API, to attach to the running VM, and request it start a given
agent.
If an agent is started during the live phase then its agent library
must export a start-up function
with the following prototype:
JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char *options, void *reserved)
The VM will start the agent by calling this function.
It will be called in the context of a thread
that is attached to the VM. The first argument <vm> is the Java VM.
The <options> argument is the startup options provided to the agent.
<options> is encoded as a modified UTF-8
string.
If startup options were not provided, a zero length string is passed to
options. The lifespan of the options string is the
Agent_OnAttach call. If needed beyond this time the string or parts of
the string must be copied.
Note that some capabilities
may not be available in the live phase.
The Agent_OnAttach 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.
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,
or record the error in a system log.
Agent Shutdown
The library may optionally export a
shutdown function with the following prototype:
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm)
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
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
normal termination or VM failure, including start-up failure.
Uncontrolled shutdown is, of couse, an exception to this rule.
Note the distinction between this function and the
VM Death event: for the VM Death event
to be sent, the VM must have run at least to the point of initialization and a valid
JVM TI environment must exist which has set a callback for VMDeath
and enabled the event
None of these are required for Agent_OnUnload and this function
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
function is called (assuming this function is called due to VM termination).
This function can be used to clean-up resources allocated by the agent.
Since the command-line cannot always be accessed or modified, for example in embedded VMs
or simply VMs launched deep within scripts, a JAVA_TOOL_OPTIONS variable is
provided so that agents may be launched in these cases.
Platforms which support environment variables or other named strings, may support the
JAVA_TOOL_OPTIONS variable. This variable will be broken into options at white-space
boundaries. White-space characters include space, tab, carriage-return, new-line,
vertical-tab, and form-feed. Sequences of white-space characters are considered
equivalent to a single white-space character. No white-space is included in the options
unless quoted. Quoting is as follows:
- All characters enclosed between a pair of single quote marks (''), except a single
quote, are quoted.
- Double quote characters have no special meaning inside a pair of single quote marks.
- All characters enclosed between a pair of double quote marks (""), except a double
quote, are quoted.
- Single quote characters have no special meaning inside a pair of double quote marks.
- A quoted part can start or end anywhere in the variable.
- White-space characters have no special meaning when quoted -- they are included in
the option like any other character and do not mark white-space boundaries.
- The pair of quote marks is not included in the option.
JNI_CreateJavaVM (in the JNI Invocation API) will prepend these options to the options supplied
in its JavaVMInitArgs argument. Platforms may disable this feature in cases where security is
a concern; for example, the Reference Implementation disables this feature on Unix systems when
the effective user or group ID differs from the real ID.
This feature is intended to support the initialization of tools -- specifically including the
launching of native or Java programming language agents. Multiple tools may wish to use this
feature, so the variable should not be overwritten, instead, options should be appended to
the variable. Note that since the variable is processed at the time of the JNI Invocation
API create VM call, options processed by a launcher (e.g., VM selection options) will not be handled.
Environments
The JVM TI specification supports the use of multiple simultaneous
JVM TI agents.
Each agent has its own JVM TI environment.
That is, the JVM TI state is
separate for each agent - changes to one environment do not affect the
others. The state of a JVM TI
environment includes:
Although their JVM TI state
is separate, agents inspect and modify the shared state
of the VM, they also share the native environment in which they execute.
As such, an agent can perturb the results of other agents or cause them
to fail. It is the responsibility of the agent writer to specify the level
of compatibility with other agents. JVM TI implementations are not capable
of preventing destructive interactions between agents. Techniques to reduce
the likelihood of these occurrences are beyond the scope of this document.
An agent creates a JVM TI environment
by passing a JVM TI version
as the interface ID to the JNI Invocation API function
GetEnv.
See Accessing JVM TI Functions
for more details on the creation and use of
JVM TI environments.
Typically, JVM TI environments are created by calling GetEnv from
Agent_OnLoad.
Bytecode Instrumentation
This interface does not include some events that one might expect in an interface with
profiling support. Some examples include object allocation events and full speed
method enter and exit events. The interface instead provides support for
bytecode instrumentation, the ability to alter the Java virtual machine
bytecode instructions which comprise the target program. Typically, these alterations
are to add "events" to the code of a method - for example, to add, at the beginning of a method,
a call to MyProfiler.methodEntered().
Since the changes are purely additive, they do not modify application
state or behavior.
Because the inserted agent code is standard bytecodes, the VM can run at full speed,
optimizing not only the target program but also the instrumentation. If the
instrumentation does not involve switching from bytecode execution, no expensive
state transitions are needed. The result is high performance events.
This approach also provides complete control to the agent: instrumentation can be
restricted to "interesting" portions of the code (e.g., the end user's code) and
can be conditional. Instrumentation can run entirely in Java programming language
code or can call into the native agent. Instrumentation can simply maintain
counters or can statistically sample events.
Instrumentation can be inserted in one of three ways:
-
Static Instrumentation: The class file is instrumented before it
is loaded into the VM - for example, by creating a duplicate directory of
*.class files which have been modified to add the instrumentation.
This method is extremely awkward and, in general, an agent cannot know
the origin of the class files which will be loaded.
-
Load-Time Instrumentation: When a class file is loaded by the VM, the raw
bytes of the class file are sent for instrumentation to the agent.
The
ClassFileLoadHook
event, triggered by the class load,
provides this functionality. This mechanism provides efficient
and complete access to one-time instrumentation.
-
Dynamic Instrumentation: A class which is already loaded (and possibly
even running) is modified. This optional feature is provided by the
ClassFileLoadHook event, triggered by calling the
RetransformClasses function.
Classes can be modified multiple times and can be returned to their
original state.
The mechanism allows instrumentation which changes during the
course of execution.
The class modification functionality provided in this interface
is intended to provide a mechanism for instrumentation
(the ClassFileLoadHook event
and the RetransformClasses function)
and, during development, for fix-and-continue debugging
(the RedefineClasses function).
Care must be taken to avoid perturbing dependencies, especially when
instrumenting core classes. For example, an approach to getting notification
of every object allocation is to instrument the constructor on
Object. Assuming that the constructor is initially
empty, the constructor could be changed to:
public Object() {
MyProfiler.allocationTracker(this);
}
However, if this change was made using the
ClassFileLoadHook
event then this might impact a typical VM as follows:
the first created object will call the constructor causing a class load of
MyProfiler; which will then cause
object creation, and since MyProfiler isn't loaded yet,
infinite recursion; resulting in a stack overflow. A refinement of this
would be to delay invoking the tracking method until a safe time. For
example, trackAllocations could be set in the
handler for the VMInit event.
static boolean trackAllocations = false;
public Object() {
if (trackAllocations) {
MyProfiler.allocationTracker(this);
}
}
The SetNativeMethodPrefix allows native methods
to be instrumented by the use of wrapper methods.
Modified UTF-8 String Encoding
JVM TI uses modified UTF-8 to encode character strings.
This is the same encoding used by JNI.
Modified UTF-8 differs
from standard UTF-8 in the representation of supplementary characters
and of the null character. See the
Modified UTF-8 Strings
section of the JNI specification for details.
Specification Context
Since this interface provides access to the state of applications running in the
Java virtual machine;
terminology refers to the Java platform and not the native
platform (unless stated otherwise). For example:
- "thread" means Java programming language thread.
- "stack frame" means Java virtual machine stack frame.
- "class" means Java programming language class.
- "heap" means Java virtual machine heap.
- "monitor" means Java programming language object monitor.
Sun, Sun Microsystems, the Sun logo, Java, and JVM
are trademarks or registered trademarks of Sun
Microsystems, Inc. in the U.S. and other countries.
Functions
Accessing Functions
Native code accesses JVM TI features
by calling JVM TI functions.
Access to JVM TI functions is by use of an interface pointer
in the same manner as
Java
Native Interface (JNI) functions are accessed.
The JVM TI interface pointer is called the
environment pointer.
An environment pointer is a pointer to an environment and has
the type jvmtiEnv*.
An environment has information about its JVM TI connection.
The first value in the environment is a pointer to the function table.
The function table is an array of pointers to JVM TI functions.
Every function pointer is at a predefined offset inside the
array.
When used from the C language:
double indirection is used to access the functions;
the environment pointer provides context and is the first
parameter of each function call; for example:
jvmtiEnv *jvmti;
...
jvmtiError err = (*jvmti)->GetLoadedClasses(jvmti, &class_count, &classes);
When used from the C++ language:
functions are accessed as member functions of jvmtiEnv;
the environment pointer is not passed to the function call; for example:
jvmtiEnv *jvmti;
...
jvmtiError err = jvmti->GetLoadedClasses(&class_count, &classes);
Unless otherwise stated, all examples and declarations in this
specification use the C language.
A JVM TI environment can be obtained through the JNI Invocation API
GetEnv function:
jvmtiEnv *jvmti;
...
(*jvm)->GetEnv(jvm, &jvmti, JVMTI_VERSION_1_0);
Each call to GetEnv
creates a new JVM TI connection and thus
a new JVM TI environment.
The version argument of GetEnv must be
a JVM TI version.
The returned environment may have a different version than the
requested version but the returned environment must be compatible.
GetEnv will return JNI_EVERSION if a
compatible version is not available, if JVM TI is not supported or
JVM TI is not supported in the current VM configuration.
Other interfaces may be added for creating JVM TI environments
in specific contexts.
Each environment has its own state (for example,
desired events,
event handling functions, and
capabilities).
An environment is released with
DisposeEnvironment.
Thus, unlike JNI which has one environment per thread, JVM TI environments work
across threads and are created dynamically.
Function Return Values
JVM TI functions always return an
error code via the
jvmtiError function return value.
Some functions can return additional
values through pointers provided by the calling function.
In some cases, JVM TI functions allocate memory that your program must
explicitly deallocate. This is indicated in the individual JVM TI
function descriptions. Empty lists, arrays, sequences, etc are
returned as NULL.
In the event that the JVM TI function encounters
an error (any return value other than JVMTI_ERROR_NONE) the values
of memory referenced by argument pointers is undefined, but no memory
will have been allocated and no global references will have been allocated.
If the error occurs because of invalid input, no action will have occurred.
Managing JNI Object References
JVM TI functions identify objects with JNI references
(jobject and jclass)
and their derivatives
(jthread and jthreadGroup).
References passed to
JVM TI functions can be either global or local, but they must be
strong references. All references returned by JVM TI functions are
local references--these local references are created
during the JVM TI call.
Local references are a resource that must be managed (see the
JNI Documentation).
When threads return from native code all local references
are freed. Note that some threads, including typical
agent threads, will never return from native code.
A thread is ensured the ability to create sixteen local
references without the need for any explicit management.
For threads executing a limited number of JVM TI calls before
returning from native code
(for example, threads processing events),
it may be determined that no explicit management
is needed.
However, long running agent threads will need explicit
local reference management--usually with the JNI functions
PushLocalFrame and PopLocalFrame.
Conversely, to preserve references beyond the
return from native code, they must be converted to global references.
These rules do not apply to jmethodID and jfieldID
as they are not jobjects.
Prerequisite State for Calling Functions
Unless the function explicitly states that the agent must bring
a thread or the VM to a particular state (for example, suspended),
the JVM TI implementation is responsible for bringing the VM to a
safe and consistent state for performing the function.
Exceptions and Functions
JVM TI functions never throw exceptions; error conditions are
communicated via the
function return value.
Any existing exception state is preserved across a call to a
JVM TI function.
See the
Java Exceptions
section of the JNI specification for information on handling exceptions.
Function Index
Memory Management
Memory Management functions:
These functions provide for the allocation and deallocation of
memory used by JVM TI functionality and can be used to provide
working memory for agents.
Memory managed by JVM TI is not compatible with other memory
allocation libraries and mechanisms.
Allocate
jvmtiError
Allocate(jvmtiEnv* env,
jlong size,
unsigned char** mem_ptr)
Allocate an area of memory through the JVM TI allocator.
The allocated
memory should be freed with Deallocate.
|
Name
|
Type
|
Description
|
size | jlong |
The number of bytes to allocate.
Rationale:
jlong is used for compatibility with JVMDI.
|
mem_ptr | unsigned char** |
On return, a pointer to the beginning of the allocated memory.
If size is zero, NULL is returned.
Agent passes a pointer to a unsigned char*. On return, the unsigned char* points to a newly allocated array of size size. The array should be freed with Deallocate. |
Deallocate
jvmtiError
Deallocate(jvmtiEnv* env,
unsigned char* mem)
Deallocate mem using the JVM TI allocator.
This function should
be used to deallocate any memory allocated and returned
by a JVM TI function
(including memory allocated with Allocate).
All allocated memory must be deallocated
or the memory cannot be reclaimed.
|
Name
|
Type
|
Description
|
mem |
unsigned char
* |
A pointer to the beginning of the allocated memory.
Please ignore "On return, the elements are set."
Agent passes an array of unsigned char. The incoming values of the elements of the array are ignored. On return, the elements are set.
If
mem
is
NULL, the call is ignored.
|
Thread
Thread functions:
Thread function types:
Thread types:
Thread flags and constants:
Get Thread State
jvmtiError
GetThreadState(jvmtiEnv* env,
jthread thread,
jint* thread_state_ptr)
Get the state of a thread. The state of the thread is represented by the
answers to the hierarchical set of questions below:
The answers are represented by the following bit vector.
|
Constant
|
Value
|
Description
|
JVMTI_THREAD_STATE_ALIVE | 0x0001 |
Thread is alive. Zero if thread is new (not started) or terminated.
|
JVMTI_THREAD_STATE_TERMINATED | 0x0002 |
Thread has completed execution.
|
JVMTI_THREAD_STATE_RUNNABLE | 0x0004 |
Thread is runnable.
|
JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | 0x0400 |
Thread is waiting to enter a synchronization block/method or,
after an Object.wait(), waiting to re-enter a
synchronization block/method.
|
JVMTI_THREAD_STATE_WAITING | 0x0080 |
Thread is waiting.
|
JVMTI_THREAD_STATE_WAITING_INDEFINITELY | 0x0010 |
Thread is waiting without a timeout.
For example, Object.wait().
|
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | 0x0020 |
Thread is waiting with a maximum time to wait specified.
For example, Object.wait(long).
|
JVMTI_THREAD_STATE_SLEEPING | 0x0040 |
Thread is sleeping -- Thread.sleep(long).
|
JVMTI_THREAD_STATE_IN_OBJECT_WAIT | 0x0100 |
Thread is waiting on an object monitor -- Object.wait.
|
JVMTI_THREAD_STATE_PARKED | 0x0200 |
Thread is parked, for example: LockSupport.park,
LockSupport.parkUtil and LockSupport.parkNanos.
|
JVMTI_THREAD_STATE_SUSPENDED | 0x100000 |
Thread suspended.
java.lang.Thread.suspend()
or a JVM TI suspend function
(such as SuspendThread)
has been called on the thread. If this bit
is set, the other bits refer to the thread state before suspension.
|
JVMTI_THREAD_STATE_INTERRUPTED | 0x200000 |
Thread has been interrupted.
|
JVMTI_THREAD_STATE_IN_NATIVE | 0x400000 |
Thread is in native code--that is, a native method is running
which has not called back into the VM or Java programming
language code.
This flag is not set when running VM compiled Java programming
language code nor is it set when running VM code or
VM support code. Native VM interface functions, such as JNI and
JVM TI functions, may be implemented as VM code.
|
JVMTI_THREAD_STATE_VENDOR_1 | 0x10000000 |
Defined by VM vendor.
|
JVMTI_THREAD_STATE_VENDOR_2 | 0x20000000 |
Defined by VM vendor.
|
JVMTI_THREAD_STATE_VENDOR_3 | 0x40000000 |
Defined by VM vendor.
|
The following definitions are used to convert JVM TI thread state
to java.lang.Thread.State style states.
|
Constant
|
Value
|
Description
|
JVMTI_JAVA_LANG_THREAD_STATE_MASK | JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT |
Mask the state with this before comparison
|
JVMTI_JAVA_LANG_THREAD_STATE_NEW | 0 |
java.lang.Thread.State.NEW
|
JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_TERMINATED |
java.lang.Thread.State.TERMINATED
|
JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE |
java.lang.Thread.State.RUNNABLE
|
JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER |
java.lang.Thread.State.BLOCKED
|
JVMTI_JAVA_LANG_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY |
java.lang.Thread.State.WAITING
|
JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT |
java.lang.Thread.State.TIMED_WAITING
|
Rules
There can be no more than one answer to a question, although there can be no
answer (because the answer is unknown, does not apply, or none of the answers is
correct). An answer is set only when the enclosing answers match.
That is, no more than one of
-
JVMTI_THREAD_STATE_RUNNABLE
-
JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER
-
JVMTI_THREAD_STATE_WAITING
can be set (a J2SETM compliant implementation will always set
one of these if JVMTI_THREAD_STATE_ALIVE is set).
And if any of these are set, the enclosing answer
JVMTI_THREAD_STATE_ALIVE is set.
No more than one of
-
JVMTI_THREAD_STATE_WAITING_INDEFINITELY
-
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
can be set (a J2SETM compliant implementation will always set
one of these if JVMTI_THREAD_STATE_WAITING is set).
And if either is set, the enclosing answers
JVMTI_THREAD_STATE_ALIVE and
JVMTI_THREAD_STATE_WAITING are set.
No more than one of
-
JVMTI_THREAD_STATE_IN_OBJECT_WAIT
-
JVMTI_THREAD_STATE_PARKED
-
JVMTI_THREAD_STATE_SLEEPING
can be set. And if any of these is set, the enclosing answers
JVMTI_THREAD_STATE_ALIVE and
JVMTI_THREAD_STATE_WAITING are set.
Also, if JVMTI_THREAD_STATE_SLEEPING is set,
then JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT is set.
If a state A is implemented using the mechanism of
state B then it is state A which
is returned by this function.
For example, if Thread.sleep(long)
is implemented using Object.wait(long)
then it is still JVMTI_THREAD_STATE_SLEEPING
which is returned.
More than one of
-
JVMTI_THREAD_STATE_SUSPENDED
-
JVMTI_THREAD_STATE_INTERRUPTED
-
JVMTI_THREAD_STATE_IN_NATIVE
can be set, but if any is set,
JVMTI_THREAD_STATE_ALIVE is set.
And finally,
JVMTI_THREAD_STATE_TERMINATED cannot be set unless
JVMTI_THREAD_STATE_ALIVE is not set.
The thread state representation is designed for extension in future versions
of the specification; thread state values should be used accordingly, that is
they should not be used as ordinals.
Most queries can be made by testing a single bit, if use in a switch statement is desired,
the state bits should be masked with the interesting bits.
All bits not defined above are reserved for future use.
A VM, compliant to the current specification, must set reserved bits to zero.
An agent should ignore reserved bits --
they should not be assumed to be zero and thus should not be included in comparisons.
Examples
Note that the values below exclude reserved and vendor bits.
The state of a thread blocked at a synchronized-statement would be:
JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER
The state of a thread which hasn't started yet would be:
0
The state of a thread at a Object.wait(3000) would be:
JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_WAITING +
JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT +
JVMTI_THREAD_STATE_MONITOR_WAITING
The state of a thread suspended while runnable would be:
JVMTI_THREAD_STATE_ALIVE + JVMTI_THREAD_STATE_RUNNABLE + JVMTI_THREAD_STATE_SUSPENDED
Testing the State
In most cases, the thread state can be determined by testing the one bit corresponding
to that question. For example, the code to test if a thread is sleeping:
jint state;
jvmtiError err;
err = (*jvmti)->GetThreadState(jvmti, thread, &state);
if (err == JVMTI_ERROR_NONE) {
if (state & JVMTI_THREAD_STATE_SLEEPING) { ...
For waiting (that is, in Object.wait, parked, or sleeping) it would be:
if (state & JVMTI_THREAD_STATE_WAITING) { ...
For some states, more than one bit will need to be tested as is the case
when testing if a thread has not yet been started:
if ((state & (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_TERMINATED)) == 0) { ...
To distinguish timed from untimed Object.wait:
if (state & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) {
if (state & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) {
printf("in Object.wait(long timeout)\n");
} else {
printf("in Object.wait()\n");
}
}
Relationship to java.lang.Thread.State
The thread state represented by java.lang.Thread.State
returned from java.lang.Thread.getState() is a subset of the
information returned from this function.
The corresponding java.lang.Thread.State can be determined
by using the provided conversion masks.
For example, this returns the name of the java.lang.Thread.State thread state:
err = (*jvmti)->GetThreadState(jvmti, thread, &state);
abortOnError(err);
switch (state & JVMTI_JAVA_LANG_THREAD_STATE_MASK) {
case JVMTI_JAVA_LANG_THREAD_STATE_NEW:
return "NEW";
case JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED:
return "TERMINATED";
case JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE:
return "RUNNABLE";
case JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED:
return "BLOCKED";
case JVMTI_JAVA_LANG_THREAD_STATE_WAITING:
return "WAITING";
case JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING:
return "TIMED_WAITING";
}
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to query.
If
thread
is
NULL, the current thread is used.
|
thread_state_ptr | jint* |
On return, points to state flags,
as defined by the Thread State Flags.
Agent passes a pointer to a jint. On return, the jint has been set. |
Get Current Thread
jvmtiError
GetCurrentThread(jvmtiEnv* env,
jthread* thread_ptr)
Get the current thread.
The current thread is the Java programming language thread which has called the function.
Note that most JVM TI functions that take a thread
as an argument will accept NULL to mean
the current thread.
|
Name
|
Type
|
Description
|
thread_ptr | jthread* |
On return, points to the current thread.
Agent passes a pointer to a jthread. On return, the jthread has been set. The object returned by thread_ptr is a JNI local reference and must be managed.
|
Get All Threads
jvmtiError
GetAllThreads(jvmtiEnv* env,
jint* threads_count_ptr,
jthread** threads_ptr)
Get all live threads.
The threads are Java programming language threads;
that is, threads that are attached to the VM.
A thread is live if java.lang.Thread.isAlive()
would return true, that is, the thread has
been started and has not yet died.
The universe of threads is determined by the context of the JVM TI
environment, which typically is all threads attached to the VM.
Note that this includes JVM TI agent threads
(see RunAgentThread).
|
Name
|
Type
|
Description
|
threads_count_ptr | jint* |
On return, points to the number of running threads.
Agent passes a pointer to a jint. On return, the jint has been set. |
threads_ptr | jthread** |
On return, points to an array of references, one
for each running thread.
Agent passes a pointer to a jthread*. On return, the jthread* points to a newly allocated array of size *threads_count_ptr. The array should be freed with Deallocate. The objects returned by threads_ptr are JNI local references and must be managed.
|
Suspend Thread
jvmtiError
SuspendThread(jvmtiEnv* env,
jthread thread)
Suspend the specified thread. If the calling thread is specified,
this function will not return until some other thread calls
ResumeThread.
If the thread is currently suspended, this function
does nothing and returns an error.
Optional Functionality: might not be implemented for all
virtual machines.
The following capability
(as returned by
GetCapabilities)
must be true to use this
function.
|
|
Capability
|
Effect
|
can_suspend |
Can suspend and resume threads
|
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to suspend.
If
thread
is
NULL, the current thread is used.
|
Suspend Thread List
jvmtiError
SuspendThreadList(jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results)
Suspend the request_count
threads specified in the
request_list array.
Threads may be resumed with
ResumeThreadList or
ResumeThread.
If the calling thread is specified in the
request_list array, this function will
not return until some other thread resumes it.
Errors encountered in the suspension of a thread
are returned in the results
array, not in the return value of this function.
Threads that are currently suspended do not change state.
Optional Functionality: might not be implemented for all
virtual machines.
The following capability
(as returned by
GetCapabilities)
must be true to use this
function.
|
|
Capability
|
Effect
|
can_suspend |
Can suspend and resume threads
|
|
Name
|
Type
|
Description
|
request_count | jint |
The number of threads to suspend.
|
request_list | const jthread* |
The list of threads to suspend.
Agent passes in an array of request_count elements of jthread. |
results | jvmtiError* |
An agent supplied array of
request_count elements.
On return, filled with the error code for
the suspend of the corresponding thread.
The error code will be
JVMTI_ERROR_NONE
if the thread was suspended by this call.
Possible error codes are those specified
for SuspendThread.
Agent passes an array large enough to hold request_count elements of jvmtiError. The incoming values of the elements of the array are ignored. On return, the elements are set. |
Resume Thread
jvmtiError
ResumeThread(jvmtiEnv* env,
jthread thread)
Resume a suspended thread.
Any threads currently suspended through
a JVM TI suspend function (eg.
SuspendThread)
or java.lang.Thread.suspend()
will resume execution;
all other threads are unaffected.
Optional Functionality: might not be implemented for all
virtual machines.
The following capability
(as returned by
GetCapabilities)
must be true to use this
function.
|
|
Capability
|
Effect
|
can_suspend |
Can suspend and resume threads
|
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to resume.
|
Resume Thread List
jvmtiError
ResumeThreadList(jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results)
Resume the request_count
threads specified in the
request_list array.
Any thread suspended through
a JVM TI suspend function (eg.
SuspendThreadList)
or java.lang.Thread.suspend()
will resume execution.
Optional Functionality: might not be implemented for all
virtual machines.
The following capability
(as returned by
GetCapabilities)
must be true to use this
function.
|
|
Capability
|
Effect
|
can_suspend |
Can suspend and resume threads
|
|
Name
|
Type
|
Description
|
request_count | jint |
The number of threads to resume.
|
request_list | const jthread* |
The threads to resume.
Agent passes in an array of request_count elements of jthread. |
results | jvmtiError* |
An agent supplied array of
request_count elements.
On return, filled with the error code for
the resume of the corresponding thread.
The error code will be
JVMTI_ERROR_NONE
if the thread was suspended by this call.
Possible error codes are those specified
for ResumeThread.
Agent passes an array large enough to hold request_count elements of jvmtiError. The incoming values of the elements of the array are ignored. On return, the elements are set. |
Stop Thread
jvmtiError
StopThread(jvmtiEnv* env,
jthread thread,
jobject exception)
Send the specified asynchronous exception to the specified thread
(similar to java.lang.Thread.stop).
Normally, this function is used to kill the specified thread with an
instance of the exception ThreadDeath.
Optional Functionality: might not be implemented for all
virtual machines.
The following capability
(as returned by
GetCapabilities)
must be true to use this
function.
|
|
Capability
|
Effect
|
can_signal_thread |
Can send stop or interrupt to threads
|
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to stop.
|
exception | jobject |
The asynchronous exception object.
|
Interrupt Thread
jvmtiError
InterruptThread(jvmtiEnv* env,
jthread thread)
Interrupt the specified thread
(similar to java.lang.Thread.interrupt).
Optional Functionality: might not be implemented for all
virtual machines.
The following capability
(as returned by
GetCapabilities)
must be true to use this
function.
|
|
Capability
|
Effect
|
can_signal_thread |
Can send stop or interrupt to threads
|
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to interrupt.
|
Get Thread Info
typedef struct {
char* name;
jint priority;
jboolean is_daemon;
jthreadGroup thread_group;
jobject context_class_loader;
} jvmtiThreadInfo;
jvmtiError
GetThreadInfo(jvmtiEnv* env,
jthread thread,
jvmtiThreadInfo* info_ptr)
Get thread information. The fields of the jvmtiThreadInfo structure
are filled in with details of the specified thread.
|
Field
|
Type
|
Description
|
name | char* |
The thread name, encoded as a
modified UTF-8 string.
|
priority | jint |
The thread priority. See the thread priority constants:
jvmtiThreadPriority.
|
is_daemon | jboolean |
Is this a daemon thread?
|
thread_group | jthreadGroup |
The thread group to which this thread belongs.
NULL if the thread has died.
|
context_class_loader | jobject |
The context class loader associated with this thread.
|
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to query.
If
thread
is
NULL, the current thread is used.
|
info_ptr | jvmtiThreadInfo* |
On return, filled with information describing the specified thread.
For JDK 1.1 implementations that don't
recognize context class loaders,
the context_class_loader field will be NULL.
Agent passes a pointer to a jvmtiThreadInfo. On return, the jvmtiThreadInfo has been set. The pointer returned in the field name of jvmtiThreadInfo is a newly allocated array. The array should be freed with Deallocate. The object returned in the field thread_group of jvmtiThreadInfo is a JNI local reference and must be managed.
The object returned in the field context_class_loader of jvmtiThreadInfo is a JNI local reference and must be managed.
|
Get Owned Monitor Info
jvmtiError
GetOwnedMonitorInfo(jvmtiEnv* env,
jthread thread,
jint* owned_monitor_count_ptr,
jobject** owned_monitors_ptr)
Get information about the monitors owned by the
specified thread.
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to query.
If
thread
is
NULL, the current thread is used.
|
owned_monitor_count_ptr | jint* |
The number of monitors returned.
Agent passes a pointer to a jint. On return, the jint has been set. |
owned_monitors_ptr | jobject** |
The array of owned monitors.
Agent passes a pointer to a jobject*. On return, the jobject* points to a newly allocated array of size *owned_monitor_count_ptr. The array should be freed with Deallocate. The objects returned by owned_monitors_ptr are JNI local references and must be managed.
|
Get Owned Monitor Stack Depth Info
typedef struct {
jobject monitor;
jint stack_depth;
} jvmtiMonitorStackDepthInfo;
jvmtiError
GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
jthread thread,
jint* monitor_info_count_ptr,
jvmtiMonitorStackDepthInfo** monitor_info_ptr)
Get information about the monitors owned by the
specified thread and the depth of the stack frame which locked them.
|
Field
|
Type
|
Description
|
monitor | jobject |
The owned monitor.
|
stack_depth | jint |
The stack depth. Corresponds to the stack depth used in the
Stack Frame functions.
That is, zero is the current frame, one is the frame which
called the current frame. And it is negative one if the
implementation cannot determine the stack depth (e.g., for
monitors acquired by JNI MonitorEnter).
|
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to query.
If
thread
is
NULL, the current thread is used.
|
monitor_info_count_ptr | jint* |
The number of monitors returned.
Agent passes a pointer to a jint. On return, the jint has been set. |
monitor_info_ptr |
jvmtiMonitorStackDepthInfo
** |
The array of owned monitor depth information.
Agent passes a pointer to a jvmtiMonitorStackDepthInfo*. On return, the jvmtiMonitorStackDepthInfo* points to a newly allocated array of size *owned_monitor_depth_count_ptr. The array should be freed with Deallocate. The objects returned in the field monitor of jvmtiMonitorStackDepthInfo are JNI local references and must be managed.
|
Get Current Contended Monitor
jvmtiError
GetCurrentContendedMonitor(jvmtiEnv* env,
jthread thread,
jobject* monitor_ptr)
Get the object, if any, whose monitor the specified thread is waiting to
enter or waiting to regain through java.lang.Object.wait.
|
Name
|
Type
|
Description
|
thread | jthread |
The thread to query.
If
thread
is
NULL, the current thread is used.
|
monitor_ptr | jobject* |
On return, filled with the current contended monitor, or
NULL if there is none.
Agent passes a pointer to a jobject. On return, the jobject has been set. The object returned by monitor_ptr is a JNI local reference and must be managed.
|