Revision 0.1
This document describes VM debug interface which extends JVMTI to support the debugging of mixed managed and native code, namely Native Code Access Interface - NCAI.
The following cannot be done without knowing VM internals:
The following can be done behind the VM:
Processing of breakpoints/watchpoints/signals can be done behind the VM, but if VM use the same methods to implement java breakpoints/watchpoints over jitted code we get a conflict of interests, so we need common approach.
NCAI features can be accessed via an interface pointer just like it can be done with JNI and JVMTI interface pointers. It is used to call it environment pointer, or briefly environment. A NCAI environment can be obtained via the following JVMTI extension function:
jvmtiError JNICALL GetExtensionEnv
(jvmtiEnv* jvmti_env, void** ncai_env_ptr, jint version);
where version is a NCAI version number like NCAI_VERSION_1_0.
The function is provided by a standard JVMTI extension mechanism where it
is identified as org.apache.harmony.vm.GetExtensionEnv
. For
example:
static const char* const GET_EXTENTION_ENV_FUNC =
"org.apache.harmony.vm.GetExtensionEnv";
jint extCount = 0;
jvmtiExtensionFunctionInfo* extensions = 0;
jvmtiError err = jvmti->GetExtensionFunctions(&extCount, &extensions);
jvmtiExtensionFunction
func = 0;
for (jint i = 0; i < extCount; i++) {
if (strcmp(extensions[i].id, GET_EXTENTION_ENV_FUNC) == 0) {
func = extensions[i].func;
}
jvmti->Deallocate(extensions[i].id);
jvmti->Deallocate(extensions[i].short_description);
for (jint j = 0; j < extensions[i].param_count; j++) {
jvmti->Deallocate(extensions[i].params[j].name);
}
jvmti->Deallocate(extensions[i].params);
jvmti->Deallocate(extensions[i].errors);
}
ncaiEnv* ncai = 0;
if (func != NULL) {
err = func(jvmti, &ncai, NCAI_VERSION_1_0);
}
struct _ncai;
typedef _ncai* ncaiEnv;
Interface pointer to structure containing the NCAI function table just like JNI and JVMTI do.
typedef enum {...} ncaiError;
An error code which every NCAI function returns.
struct _ncaiModule;
typedef _ncaiModule* ncaiModule;
Opaque reference to VM internal data structure which represents a loadable module.
struct ncaiModuleInfo;
struct ncaiSegmentInfo;
Structures containing module information. See the GetModuleInfo function.
typedef enum {} ncaiModuleKind;
An identifier for a module type. See the GetModuleInfo function.
typedef enum {} ncaiSegmentKind;
An identifier for a segment type. See the GetModuleInfo function.
struct _ncaiThread;
typedef _ncaiThread* ncaiThread;
Opaque reference to VM internal data structure which represents a thread attached to the VM process.
struct ncaiThreadInfo;
Structure containing thread information. See the GetThreadInfo function.
typedef enum {} ncaiThreadKind;
An identifier for a thread type. See the GetThreadInfo function.
struct ncaiFrameInfo;
Structure containing frame information. See the GetStackTrace function.
struct ncaiRegisterInfo;
Structure containing register information. See the GetRegisterInfo function.
struct ncaiSignalInfo;
Structure containing signal information. See the GetSignalInfo function.
struct ncaiCapabilities;
Capabilities of a NCAI environment. TBD.
struct ncaiEventCallbacks;
Structure containing the list of event callback functions.
typedef enum {} ncaiEventMode;
typedef enum {} ncaiEventKind;
Identifiers for event mode and type. See the SetEventNotificationMode function.
typedef enum {} ncaiWatchpointMode;
An identifier for the Watchpoint event mode. See the SetWatchpoint function.
typedef enum {} ncaiStepMode;
An identifier for the Step event mode. See the SetStepMode function.
ncaiError JNICALL GetAllLoadedModules
(ncaiEnv* env, jint* count_ptr, ncaiModule** modules_ptr);
Get a list of all modules which are loaded to the VM process. All the returned module pointers remain valid at least till the next function call.
Parameters:
Errors:
ncaiError JNICALL GetModuleInfo
(ncaiEnv* env, ncaiModule module, ncaiModuleInfo* info_ptr);
Get the module information. Fills the following structure:
typedef enum {
NCAI_MODULE _JNI_LIBRARY,
NCAI_MODULE _VM_INTERNAL,
NCAI_MODULE _OTHER
} ncaiModuleKind;
typedef struct {
ncaiModuleKind kind;
char* name;
char* filename;
ncaiSegmentInfo* segments;
size_t segment_count;
} ncaiModuleInfo;
The kind field determines the module kind from the point of view of VM. The segments field will be set on return with a pointer to array of the following structures:
typedef enum {
NCAI_SEGMENT_UNKNOWN,
NCAI_SEGMENT_CODE,
NCAI_SEGMENT_DATA
} ncaiSegmentKind;
typedef struct {
ncaiSegmentKind kind;
void* base_address;
size_t size;
} ncaiSegmentInfo;
Parameters:
Errors:
ncaiError JNICALL GetModuleClassLoader
(ncaiEnv* env, ncaiModule module, jobject* classloader_ptr);
Get the JNI library module class loader. Returns NULL if the module was loaded by the bootstrap class loader. If the module is not of NCAI_MODULE _JNI_LIBRARY kind the function returns an error.
Parameters:
Errors:
ncaiError JNICALL IsMethodCompiled
(ncaiEnv* env, jmethodID method, jboolean* is_compiled_ptr);
Indicates if the java method is actually in compiled form.
Parameters:
Errors:
ncaiError JNICALL GetMethodLocation
(ncaiEnv* env, jmethodID method, void** address_ptr, size_t* size_ptr);
Get a native address where compiled method is loaded and the size of compiled code. This is a convenience method corresponding to the CompiledMethodLoad event callback of JVMTI. So, the values returning in address_ptr and size_ptr should be the same as code_addr and code_size parameters of the callback.
Parameters:
Errors:
ncaiError JNICALL FindJavaMethod
(ncaiEnv* env, void* address, jmethodID* method_ptr);
Finds a compiled java method which corresponds to the given native address.
Parameters:
Errors:
ncaiError JNICALL GetBytecodeLocation
(ncaiEnv* env, void* address, jmethodID method, jlocation* location_ptr);
Get a bytecode location in compiled java method which corresponds to the given native address.
Parameters:
Errors:
ncaiError JNICALL GetNativeLocation
(ncaiEnv* env, jmethodID method, jlocation location, void** address_ptr);
Get a native address which corresponds to the given bytecode location in compiled method. This is a convenience method corresponding to the CompiledMethodLoad event callback of JVMTI. So, the value returning in address_ptr should be the same as start_address corresponding to the location in map parameter of the callback.
Parameters:
Errors:
ncaiError JNICALL GetAllThreads
(ncaiEnv* env, jint* count_ptr, ncaiThread** threads_ptr);
Get a list of all known threads. All the returned thread pointers remain valid at least till the next function call.
Parameters:
Errors:
ncaiError JNICALL GetThreadInfo
(ncaiEnv* env, ncaiThread thread, ncaiThreadInfo* info_ptr);
Get the thread information. Fills the following structure:
typedef enum {
NCAI_THREAD_JAVA,
NCAI_THREAD_VM_INTERNAL,
NCAI_THREAD_OTHER
} ncaiThreadKind;
typedef struct {
ncaiThreadKind kind;
char* name;
} ncaiThreadInfo;
Parameters:
Errors:
ncaiError JNICALL GetThreadHandle
(ncaiEnv* env, jthread thread, ncaiThread* thread_ptr);
Get a thread handle for the given java thread object (0, if such object does not exist).
Parameters:
Errors:
ncaiError JNICALL GetThreadObject
(ncaiEnv* env, ncaiThread thread, jthread* thread_ptr);
Get a java thread object for the given thread handle (0, if such object does not exist).
Parameters:
Errors:
ncaiError JNICALL SuspendThread
(ncaiEnv* env, ncaiThread thread);
Suspend the given thread. This is really a hard suspend which means one should take care about possible deadlock if some thread will try to enter a monitor acquired by the suspended thread.
Parameters:
Errors:
ncaiError JNICALL ResumeThread
(ncaiEnv* env, ncaiThread thread);
Resume the given thread.
Parameters:
Errors:
ncaiError JNICALL TerminateThread
(ncaiEnv* env, ncaiThread thread);
Terminate the given thread. Not all acquired locks and owned JVMTI monitors could be released, so care must be taken to prevent a possible deadlock.
Parameters:
Errors:
ncaiError JNICALL GetThreadState
(ncaiEnv* env, jthread thread, jint* state_ptr);
Get a state for the given thread. TBD.
ncaiError JNICALL GetFrameCount
(ncaiEnv* env, ncaiThread thread, jint* count_ptr);
Get the total number of frames in the given thread. The thread needs to be either suspended or the current thread in breakpoint or step callback.
Parameters:
Errors:
ncaiError JNICALL GetStackTrace
(ncaiEnv* env, ncaiThread thread, jint depth, ncaiFrameInfo* frame_buffer, jint* count_ptr);
Get the stack frame information for the given thread. The depth specifies capacity of the frame_buffer. The thread needs to be either suspended or the current thread in breakpoint or step callback.
typedef struct {
jint java_frame_depth;
void* pc_address;
void* return_address;
void* frame_address;
void* stack_address;
} ncaiFrameInfo;
Parameters:
Errors:
ncaiError JNICALL GetRegisterCount
(ncaiEnv* env, jint* count_ptr);
Get a number of native registers.
Parameters:
Errors:
ncaiError JNICALL GetRegisterlInfo
(ncaiEnv* env, jint reg_number, ncaiRegisterInfo* info_ptr);
Get the register information. Fills the following structure:
typedef struct {
char* name;
jint size;
} ncaiRegisterInfo;
Field name is the register name and size is the register size in bits.
Parameters:
Errors:
ncaiError JNICALL GetRegisterValue
(ncaiEnv* env, ncaiThread thread, jint reg_number, void* buf);
Get the register value for the given thread. The thread needs to be either suspended or the current thread in breakpoint or step callback.
Parameters:
Errors:
ncaiError JNICALL SetRegisterValue
(ncaiEnv* env, ncaiThread thread, jint reg_number, void* buf);
Set the register value for the given thread. The thread needs to be either suspended or the current thread in breakpoint or step callback.
Parameters:
Errors:
The following functions should hide/preserve any instrumentation of a native code done by the VM
ncaiError JNICALL ReadMemory
(ncaiEnv* env, void* addr, size_t size, void* buf);
Read the memory block.
Parameters:
Errors:
ncaiError JNICALL WriteMemory
(ncaiEnv* env, void* addr, size_t size, void* buf);
Write the memory block.
Parameters:
Errors:
ncaiError JNICALL GetSignalCount
(ncaiEnv* env, jint* count_ptr);
Get a number of signals supported by the platform.
Parameters:
Errors:
ncaiError JNICALL GetSignalInfo
(ncaiEnv* env, jint signal, ncaiSignalInfo* info_ptr);
Get the signal information. Fills the following structure:
typedef struct {
char* name;
} ncaiSignalInfo;
Parameters:
Errors:
ncaiError JNICALL GetJvmtiEnv
(ncaiEnv* env, jvmtiEnv** jvmti_env_ptr);
Get JVMTI environment which NCAI environment extends.
Parameters:
Errors:
ncaiError JNICALL GetVersion
(ncaiEnv* env, jint* version_ptr);
Get the NCAI version number. The version number consists of major, minor and micro parts just like the JVMTI version identifier.
Parameters:
Errors:
ncaiError JNICALL GetErrorName
(ncaiEnv* env, ncaiError err, const char** name_ptr);
Get symbolic representation for the given error code.
Parameters:
Errors:
typedef struct {
// ...
} ncaiCapabilities;
ncaiError JNICALL GetPotentialCapabilities
(ncaiEnv* env, ncaiCapabilities* caps_ptr);
Get capabilities which the NCAI environment can potentially possess.
Parameters:
Errors:
ncaiError JNICALL GetCapabilities
(ncaiEnv* env, ncaiCapabilities* caps_ptr);
Get currently possessed capabilities.
Parameters:
Errors:
ncaiError JNICALL AddCapabilities
(ncaiEnv* env, ncaiCapabilities* caps_ptr);
Add the specified capabilities.
Parameters:
Errors:
ncaiError JNICALL RelinquishCapabilities
(ncaiEnv* env, ncaiCapabilities* caps_ptr);
Relinquish the specified capabilities.
Parameters:
Errors:
ncaiError JNICALL GetEventCallbacks
(ncaiEnv* env, ncaiEventCallbacks* callbacks, size_t size);
Get the event callbacks.
Parameters:
Errors:
ncaiError JNICALL SetEventCallbacks
(ncaiEnv* env, ncaiEventCallbacks* callbacks, size_t size);
Set the event callbacks.
Parameters:
Errors:
ncaiError JNICALL SetEventNotificationMode
(ncaiEnv* env, ncaiEventMode mode, ncaiEventKind event, ncaiThread thread);
Set the event notification mode for the given thread. If thread is NULL, the mode is set for all the known threads. The ncaiEventMode and ncaiEventKind are the following:
typedef enum {
NCAI_ENABLE = 1,
NCAI_DISABLE = 0
} ncaiEventMode;
typedef enum {
NCAI_EVENT_THREAD_START = 1,
NCAI_EVENT_THREAD_END,
NCAI_EVENT_BREAKPOINT,
NCAI_EVENT_STEP,
NCAI_EVENT_WATCHPOINT,
NCAI_EVENT_METHOD_ENTRY,
NCAI_EVENT_METHOD_EXIT,
NCAI_EVENT_FRAME_POP,
NCAI_EVENT_SIGNAL,
NCAI_EVENT_EXCEPTION,
NCAI_EVENT_MODULE_LOAD,
NCAI_EVENT_MODULE_UNLOAD,
NCAI_EVENT_CONSOLE_INPUT,
NCAI_EVENT_CONSOLE_OUTPUT,
NCAI_EVENT_DEBUG_OUTPUT
} ncaiEventKind;
Parameters:
Errors:
ncaiError JNICALL SetBreakpoint
(ncaiEnv* env, void* code_addr);
Set a breakpoint at the given address code_addr.
Parameters:
Errors:
ncaiError JNICALL ClearBreakpoint
(ncaiEnv* env, void* code_addr);
Clear a breakpoint at the given address code_addr.
Parameters:
Errors:
ncaiError JNICALL SetWatchpoint
(ncaiEnv* env, void* data_addr, size_t len, ncaiWatchpointMode mode);
Set a watchpoint starting at the given address data_addr for len bytes.
typedef enum {
NCAI_WATCHPOINT_READ,
NCAI_WATCHPOINT_WRITE,
NCAI_WATCHPOINT_ACCESS
} ncaiWatchpointMode;
Parameters:
Errors:
ncaiError JNICALL ClearWatchpoint
(ncaiEnv* env, void* data_addr);
Clear a watchpoint starting at the given address data_addr.
Parameters:
Errors:
ncaiError JNICALL SetStepMode
(ncaiEnv* env, ncaiThread thread, ncaiStepMode mode);
Set the step mode for the given thread. If thread is NULL, the mode is set for all the known threads.
typedef enum {
NCAI_STEP_OFF,
NCAI_STEP_INTO,
NCAI_STEP_OVER,
NCAI_STEP_OUT
} ncaiStepMode;
NCAI_STEP_OFF disables generating step events for the given thread, NCAI_STEP_INTO enables generating step event for each CPU instruction, NCAI_STEP_OVER enables generating step event for each CPU instruction skipping function calls, NCAI_STEP_OUT disables generating step event untill the function returns.
Parameters:
Errors:
ncaiError JNICALL NotifyFramePop
(ncaiEnv* env, ncaiThread thread, void* frame_address);
Set a trigger to notify when the frame of the given address is popped from the given thread stack.
Parameters:
Errors:
The following callbacks are called from within an appropriate application thread and are not queued by the VM.
void JNICALL ThreadStart
(ncaiEnv* env, ncaiThread thread);
This callback is invoked when the thread is about to enter its initial method.
void JNICALL ThreadEnd
(ncaiEnv* env, ncaiThread thread);
This callback is invoked just after the thread exits its initial method.
void JNICALL Step
(ncaiEnv* env, ncaiThread thread, void* addr);
This callback is invoked when the native thread specified in SetStepMode request reaches a new native instruction address according to the specified step mode.
void JNICALL Breakpoint
(ncaiEnv* env, ncaiThread thread, void* addr);
This callback is invoked when the native thread specified in SetBreakpoint request reaches the specified native instruction address.
void JNICALL Watchpoint
(ncaiEnv* env, ncaiThread thread, void* code_addr, void* data_addr);
This callback is invoked when the native thread specified in SetWatchpoint request accesses the specified native data address according to the specified watchpoint mode.
void JNICALL Signal
(ncaiEnv* env, ncaiThread thread, void* addr, jint signal, jboolean is_internal, jboolean* is_handled);
This callback is invoked when OS notifies the VM process about a raised signal. is_internal indicates whether the VM raises the signal for internal use. is_handled is in/out flag which indicates whether the VM will handle the signal or not. The actual set of possible signals depends on platform.
void JNICALL Exception
(ncaiEnv* env, ncaiThread thread, void* addr, void* exception);
This callback is invoked when C++ exception is thrown. TBD.
void JNICALL ModuleLoad
(ncaiEnv* env, ncaiThread thread, ncaiModule module);
This callback is invoked when the VM process loads an executable module.
void JNICALL ModuleUnload
(ncaiEnv* env, ncaiThread thread, ncaiModule module);
This callback is invoked when the VM process is about to unload an executable module.
void JNICALL MethodEntry
(ncaiEnv* env, ncaiThread thread, void* addr);
This callback is invoked when the thread is about to enter a method.
void JNICALL MethodExit
(ncaiEnv* env, ncaiThread thread, void* addr);
This callback is invoked when the thread is about to exit a method.
void JNICALL FramePop
(ncaiEnv* env, ncaiThread thread, void* addr);
This callback is invoked when the thread is about to exit a frame specified in NotifyFramePop request.
void JNICALL ConsoleInput
(ncaiEnv* env, char** message);
This callback is invoked when the VM process tries to read data from standard input. TBD.
void JNICALL ConsoleOutput
(ncaiEnv* env, char* message);
This callback is invoked when the VM process is about to write a message to standard output. TBD.
void JNICALL DebugMessage
(ncaiEnv* env, char* message);
This callback is invoked when the VM process sends a debug message. TBD.
The following is a list of possible error codes which can be returned by NCAI functions. Universal errors can be returned by any function and so they are not specified in the above function error lists. On the contrary function specific errors can be returned only by the functions which include them in the function errors specification.
NCAI_ERROR_NONE
Indicates a successful completion of the function.
NCAI_ERROR_NULL_POINTER
A pointer argument is equal to 0 which is not acceptable for the function.
NCAI_ERROR_OUT_OF_MEMORY
Not enough allocatable memory available to process the function.
NCAI_ERROR_ACCESS_DENIED
Access is denied.
NCAI_ERROR_UNATTACHED_THREAD
The calling thread is not attached to the VM.
NCAI_ERROR_INVALID_ENVIRONMENT
The NCAI environment is not valid.
NCAI_ERROR_INTERNAL
An unexpected internal error.
NCAI_ERROR_ILLEGAL_ARGUMENT
Illegal value of argument.
NCAI_ERROR_NOT_AVAILABLE
Function is not implemented.
NCAI_ERROR_INVALID_MODULE
The module specified by argument is not a valid ncaiModule.
NCAI_ERROR_INVALID_METHOD
The method specified by argument is not a valid jmethodID.
NCAI_ERROR_INVALID_LOCATION
The location specified by argument is not a valid location.
NCAI_ERROR_INVALID_ADDRESS
The address specified by argument is not valid.
NCAI_ERROR_INVALID_THREAD
The thread specified by argument is not a valid ncaiThread.
NCAI_ERROR_THREAD_SUSPENDED
The thread specified by argument is suspended.
NCAI_ERROR_THREAD_NOT_SUSPENDED
The thread specified by argument is not suspended.
NCAI_ERROR_THREAD_NOT_ALIVE
The thread specified by argument is not alive.
NCAI_ERROR_INTERPRETER_USED
VM was started in interpreter mode.
NCAI_ERROR_NOT_COMPILED
The method is not compiled yet.
NCAI_ERROR_DUPLICATE
The designated breakpoint is already set.
NCAI_ERROR_NOT_FOUND
The designated breakpoint is not found.
The following table reflects our view on implementation priorities.
Function Name | Priority | Comments | Capability |
---|---|---|---|
Modules | |||
GetAllLoadedModules
| |||
GetModuleInfo
| |||
GetModuleClassLoader
| |||
Methods | |||
IsMethodCompiled
| |||
GetMethodLocation
| |||
FindJavaMethod
| |||
GetBytecodeLocation
| |||
GetNativeLocation
| |||
Threads | |||
GetAllThreads
| |||
GetThreadInfo
| |||
GetThreadHandle
| |||
GetThreadObject
| |||
SuspendThread
| |||
ResumeThread
| |||
TerminateThread
| |||
GetThreadState
| |||
Frames | |||
GetFrameCount
| |||
GetStackTrace
| |||
Registers | |||
GetRegisterCount
| |||
GetRegisterInfo
| |||
GetRegisterValue
| |||
SetRegisterValue
| |||
Memory | |||
ReadMemory
| |||
WriteMemory
| |||
Signals | |||
GetSignalCount
| |||
GetSignalInfo
| |||
Miscellaneous | |||
GetJvmtiEnv
| |||
GetVersion
| |||
GetErrorName
| |||
Capabilities | |||
GetPotentialCapabilities
| |||
AddCapabilities
| |||
RelinquishCapabilities
| |||
GetCapabilities
| |||
Event Management | |||
GetEventCallbacks
| |||
SetEventCallbacks
| |||
SetEventNotificationMode
| |||
SetBreakpoint
| |||
ClearBreakpoint
| |||
SetWatchpoint
| |||
ClearWatchpoint
| |||
SetStepMode
| |||
NotifyFramePop
| |||
Event Callbacks | |||
ThreadStart
| |||
ThreadEnd
| |||
Step
| |||
Breakpoint
| |||
Watchpoint
| |||
Signal
| |||
Exception
| |||
ModuleLoad
| |||
ModuleUnload
| |||
MethodEntry
| |||
MethodExit
| |||
FramePop
| |||
ConsoleInput
| |||
ConsoleOutput
| |||
DebugMessage
|