blob: 4bb73d939b59524e6022869ccfc54a393aa9a512 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Gregory Shimansky
*/
/*
* JVMTI thread API
*/
#define LOG_DOMAIN "jvmti.thread"
#include "cxxlog.h"
#include "jvmti_utils.h"
#include "jni_utils.h"
#include "vm_threads.h"
#include "thread_generic.h"
#include "ti_thread.h"
#include "jthread.h"
#include "thread_manager.h"
#include "object_handles.h"
#include "platform_lowlevel.h"
#include "mon_enter_exit.h"
#include "interpreter_exports.h"
#include "environment.h"
#include "suspend_checker.h"
#include "stack_iterator.h"
//#include "Class.h" // FIXME: this is for Class::heap_base and Class::heap_end
#define MAX_JVMTI_ENV_NUMBER 10
#define jvmti_test_jenv (p_TLS_vmthread->jni_env)
/*
* Get Thread State
*
* Get the state of a thread.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetThreadState(jvmtiEnv* env,
jthread thread,
jint* thread_state_ptr)
{
TRACE2("jvmti.thread", "GetThreadState called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (NULL != thread)
{
if (!is_valid_thread_object(thread)) {
return JVMTI_ERROR_INVALID_THREAD;
}
}
else
thread = jthread_self();
if (thread_state_ptr == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
IDATA UNUSED status = jthread_get_jvmti_state(thread, thread_state_ptr);
assert(status == TM_ERROR_NONE);
return JVMTI_ERROR_NONE;
}
/*
* Get All Threads
*
* 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 JVMTI
* environment, which typically is all threads attached to the VM.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetAllThreads(jvmtiEnv* env,
jint* threads_count_ptr,
jthread** threads_ptr)
{
jthread_iterator_t iterator;
int i,java_thread_count;
jthread* java_threads;
jvmtiError err;
TRACE2("jvmti.thread", "GetAllThreads called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (threads_count_ptr == NULL || threads_ptr == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
//jthread_get_all_threads(threads_ptr, threads_count_ptr);
iterator=jthread_iterator_create();
java_thread_count = (jint)jthread_iterator_size(iterator);
//allocate memory
err=jvmtiAllocate(env,java_thread_count*sizeof(jthread),(unsigned char**)&java_threads);
if (err != JVMTI_ERROR_NONE){
jthread_iterator_release(&iterator);
return err;
}
for (i=0;i<java_thread_count;i++) {
java_threads[i]= oh_copy_to_local_handle(jthread_iterator_next(&iterator));
assert(java_threads[i]);
}
*threads_count_ptr = java_thread_count;
*threads_ptr = java_threads;
jthread_iterator_release(&iterator);
return JVMTI_ERROR_NONE;
}
/*
* Suspend 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
*/
jvmtiError JNICALL
jvmtiSuspendThread(jvmtiEnv* env,
jthread thread)
{
TRACE2("jvmti.thread", "SuspendThread called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jvmtiCapabilities capa;
jvmtiError err = env -> GetCapabilities(&capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_suspend == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (NULL != thread)
{
if (!is_valid_thread_object(thread))
return JVMTI_ERROR_INVALID_THREAD;
}
else
thread = jthread_self();
jint state;
err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE){
return err;
}
// check error condition: JVMTI_ERROR_THREAD_NOT_ALIVE
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0)
return JVMTI_ERROR_THREAD_NOT_ALIVE;
if (state & JVMTI_THREAD_STATE_SUSPENDED)
return JVMTI_ERROR_THREAD_SUSPENDED;
return (jvmtiError)jthread_suspend(thread);
}
/*
* Suspend Thread List
*
* 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 are not suspended.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiSuspendThreadList(jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results)
{
TRACE2("jvmti.thread", "SuspendThreadList called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jvmtiCapabilities capa;
jvmtiError err = env -> GetCapabilities(&capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_suspend == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (request_count < 0){
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
if (request_list == NULL || results == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
for (int i = 0; i < request_count; i++){
results[i] = jvmtiSuspendThread(env, request_list[i]);
}
return JVMTI_ERROR_NONE;
}
/*
* Resume Thread
*
* Resume a suspended thread. Any threads currently suspended
* through a JVMTI suspend function (eg. SuspendThread) or
* java.lang.Thread.suspend() will resume execution; all other
* threads are unaffected.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiResumeThread(jvmtiEnv* env,
jthread thread)
{
TRACE2("jvmti.thread", "ResumeThread called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jvmtiCapabilities capa;
jvmtiError err = jvmtiGetCapabilities(env, &capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_suspend == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (!is_valid_thread_object(thread)){
return JVMTI_ERROR_INVALID_THREAD;
}
if (false){ // TBD
return JVMTI_ERROR_INVALID_TYPESTATE;
}
if (NULL == thread)
return JVMTI_ERROR_INVALID_THREAD;
jint state;
err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE)
return err;
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0)
return JVMTI_ERROR_THREAD_NOT_ALIVE;
if ((state & JVMTI_THREAD_STATE_SUSPENDED) == 0)
return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
jthread_resume(thread);
return JVMTI_ERROR_NONE;
}
/*
* Resume Thread List
*
* Resume the request_count threads specified in the request_list
* array. Any thread suspended through a JVMTI suspend function
* (eg. SuspendThreadList) or java.lang.Thread.suspend() will
* resume execution.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiResumeThreadList(jvmtiEnv* env,
jint request_count,
const jthread* request_list,
jvmtiError* results)
{
TRACE2("jvmti.thread", "ResumeThreadList called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jvmtiCapabilities capa;
jvmtiError err = env -> GetCapabilities(&capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_suspend == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (request_count < 0){
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
if (request_list == NULL || results == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
for (int i = 0; i < request_count; i++){
results[i] = jvmtiResumeThread(env, request_list[i]);
}
return JVMTI_ERROR_NONE;
}
/*
* Stop Thread
*
* 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
*/
jvmtiError JNICALL
jvmtiStopThread(jvmtiEnv* env,
jthread thread,
jobject exception)
{
TRACE2("jvmti.thread", "StopThread called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jint state;
jvmtiCapabilities capa;
jvmtiError err = env -> GetCapabilities(&capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_signal_thread == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (!is_valid_thread_object(thread)){
return JVMTI_ERROR_INVALID_THREAD;
}
err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE){
return err;
}
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0){
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
if (! is_valid_throwable_object(exception))
return JVMTI_ERROR_INVALID_OBJECT;
if (TM_ERROR_NONE != jthread_exception_stop(thread, exception))
return JVMTI_ERROR_INTERNAL;
// force exit from wait
jthread_interrupt(thread);
return JVMTI_ERROR_NONE;
}
/*
* Interrupt Thread
*
* Interrupt the specified thread (similar to
* java.lang.Thread.interrupt).
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiInterruptThread(jvmtiEnv* env,
jthread thread)
{
TRACE2("jvmti.thread", "InterruptThread called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jvmtiCapabilities capa;
jvmtiError err = env -> GetCapabilities(&capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_signal_thread == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (!is_valid_thread_object(thread)){
return JVMTI_ERROR_INVALID_THREAD;
}
jint thread_state;
IDATA UNUSED status = jthread_get_jvmti_state(thread, &thread_state);
assert(status == TM_ERROR_NONE);
if (! (JVMTI_THREAD_STATE_ALIVE & thread_state))
return JVMTI_ERROR_THREAD_NOT_ALIVE;
return (jvmtiError)jthread_interrupt(thread);
}
/*
* Get Thread Info
*
* Get thread information. The fields of the jvmtiThreadInfo
* structure are filled in with details of the specified thread.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetThreadInfo(jvmtiEnv* env,
jthread thread,
jvmtiThreadInfo* info_ptr)
{
TRACE2("jvmti.thread", "GetThreadInfo called");
DebugUtilsTI *ti = VM_Global_State::loader_env->TI;
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (info_ptr == NULL)
return JVMTI_ERROR_NULL_POINTER;
if (NULL != thread) {
if (!is_valid_thread_object(thread)) {
return JVMTI_ERROR_INVALID_THREAD;
}
} else {
thread = jthread_self();
assert(thread != NULL);
}
jclass cl = GetObjectClass(jvmti_test_jenv, thread);
jfieldID id = jvmti_test_jenv->GetFieldID(cl, "name", "Ljava/lang/String;");
assert(id != NULL); // field must exist in kernel class
jstring name = jvmti_test_jenv->GetObjectField(thread, id);
info_ptr->name = (char*)jvmti_test_jenv->GetStringUTFChars(name, false);
id = jvmti_test_jenv->GetFieldID(cl, "priority", "I");
assert(id != NULL); // field must exist in kernel class
info_ptr->priority = jvmti_test_jenv->GetIntField(thread, id);
id = jvmti_test_jenv->GetFieldID(cl, "daemon","Z");
assert(id != NULL); // field must exist in kernel class
info_ptr->is_daemon = jvmti_test_jenv->GetBooleanField(thread, id);
id = jvmti_test_jenv->GetFieldID(cl, "group","Ljava/lang/ThreadGroup;");
assert(id != NULL); // field must exist in kernel class
info_ptr->thread_group = jvmti_test_jenv->GetObjectField(thread, id);
id = jvmti_test_jenv->GetFieldID(cl, "contextClassLoader","Ljava/lang/ClassLoader;");
assert(id != NULL); // field must exist in kernel class
info_ptr->context_class_loader = jvmti_test_jenv->GetObjectField(thread, id);
return JVMTI_ERROR_NONE;
}
/*
* Get Owned Monitor Info
*
* Get information about the monitors owned by the specified thread.
*
* OPTIONAL Functionality
*/
jvmtiError JNICALL
jvmtiGetOwnedMonitorInfo(jvmtiEnv* env,
jthread thread,
jint* owned_monitor_count_ptr,
jobject** owned_monitors_ptr)
{
TRACE2("jvmti.thread", "GetOwnedMonitorInfo called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jvmtiCapabilities capa;
jvmtiError err = env -> GetCapabilities(&capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_get_owned_monitor_info == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (NULL != thread)
{
if (!is_valid_thread_object(thread))
return JVMTI_ERROR_INVALID_THREAD;
}
else
thread = jthread_self();
if (owned_monitor_count_ptr == NULL || owned_monitors_ptr == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
jint state;
err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE){
return err;
}
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0){
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
bool thread_suspended = false;
// Gregory -
// There is a race condition. If target thread is not suspended,
// it may exit some of its owned monitors which leads to incorrect
// references in the array.
vm_thread_t vm_thread = jthread_get_vm_thread_ptr_safe(thread);
// Check that this thread is not current
if (vm_thread != p_TLS_vmthread)
{
IDATA UNREF status = hythread_suspend_other((hythread_t)vm_thread);
assert(TM_ERROR_NONE == status);
thread_suspended = true;
}
IDATA UNUSED status = jthread_get_owned_monitors(thread, owned_monitor_count_ptr, owned_monitors_ptr);
assert(status == TM_ERROR_NONE);
if (thread_suspended)
hythread_resume((hythread_t)vm_thread);
return JVMTI_ERROR_NONE;
}
/*
* Get Current Contended Monitor
*
* Get the object, if any, whose monitor the specified thread is
* waiting to enter or waiting to regain through java.lang.Object.wait.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetCurrentContendedMonitor(jvmtiEnv* env,
jthread thread,
jobject* monitor_ptr)
{
TRACE2("jvmti.thread", "GetCurrentContendedMonitor called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
jvmtiCapabilities capa;
jvmtiError err = env -> GetCapabilities(&capa);
if (err != JVMTI_ERROR_NONE){
return err;
}
if (capa.can_get_current_contended_monitor == 0){
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}
if (monitor_ptr == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
if (NULL == thread)
thread = jthread_self();
jint state;
err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE){
return err;
}
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0){
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
IDATA status = jthread_get_contended_monitor(thread, monitor_ptr);
return (jvmtiError)status;
}
/*
* Run Agent Thread
*
* Starts the execution of an agent thread. with the specified
* native function.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiRunAgentThread(jvmtiEnv* env,
jthread thread,
jvmtiStartFunction proc,
const void* arg,
jint priority)
{
JNIEnv * jni_env;
TRACE2("jvmti.thread", "RunAgentThread called");
DebugUtilsTI *ti = VM_Global_State::loader_env->TI;
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY){
return JVMTI_ERROR_INVALID_PRIORITY;
}
if (!is_valid_thread_object(thread)){
return JVMTI_ERROR_INVALID_THREAD;
}
if (proc == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
// Set daemon flag for the thread
jclass thread_class = GetObjectClass(jvmti_test_jenv, thread);
assert(thread_class);
jfieldID is_daemon = jvmti_test_jenv->GetFieldID(thread_class, "daemon", "Z");
assert(is_daemon);
jvmti_test_jenv->SetBooleanField(thread, is_daemon, JNI_TRUE);
jni_env = jthread_get_JNI_env(jthread_self());
// Run new thread
jthread_start_proc_data attrs = {0};
attrs.priority = priority;
attrs.daemon = JNI_TRUE;
attrs.jvmti_env = env;
attrs.proc = proc;
attrs.arg = arg;
jthread_create_with_function(jni_env, thread, &attrs);
return JVMTI_ERROR_NONE;
}
/*
* Set Thread Local Storage
*
* The VM stores a pointer value associated with each
* environment-thread pair. This pointer value is called
* thread-local storage. This value is NULL unless set with this
* function. Agents can allocate memory in which they store thread
* specific information. By setting thread-local storage it can
* then be accessed with GetThreadLocalStorage.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiSetThreadLocalStorage(jvmtiEnv* env,
jthread thread,
const void* data)
{
TRACE2("jvmti.thread", "SetThreadLocalStorage called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_START, JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (NULL != thread)
{
if (!is_valid_thread_object(thread)){
return JVMTI_ERROR_INVALID_THREAD;
}
}
else
thread = jthread_self();
jint state;
jvmtiError err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE){
return err;
}
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0){
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
JVMTILocalStorage* aa = NULL;
JVMTILocalStorage* lstg = jthread_get_jvmti_local_storage(thread);
if (lstg -> env == NULL) {
if (lstg -> data == NULL) {
// we have no records stored;
// so, we put our first record into vm_thread -> jvmti_local_storage
lstg -> env = (data == NULL) ? NULL : env;
lstg -> data = (void *)data;
return JVMTI_ERROR_NONE;
} else {
// we have more than one record stored;
// so, they are stored in array which is pointed at by
// vm_thread -> jvmti_local_storage -> data
aa = (JVMTILocalStorage*)lstg -> data;
}
} else {
// we have just one record stored;
// so, it's stored in vm_thread -> jvmti_local_storage
if (lstg -> env == env) {
// override data in this record
lstg -> data = (void *)data;
return JVMTI_ERROR_NONE;
} else if (data != NULL){
// we have just one record stored and we have to add another one;
// so, array is created and record is copied there
aa = (JVMTILocalStorage*)STD_MALLOC(sizeof(JVMTILocalStorage)*
MAX_JVMTI_ENV_NUMBER);
for (int i = 0; i < MAX_JVMTI_ENV_NUMBER; i++){
aa[0].env = NULL;
aa[0].data = NULL;
}
aa[0].env = lstg -> env;
aa[0].data = lstg -> data;
lstg -> env = NULL;
lstg -> data = (void *)aa;
}
}
// array look up for existing env or for free record
int ii = -1;
for (int i = 0; i < MAX_JVMTI_ENV_NUMBER; i++){
if (aa[i].env == env){
ii = i;
break;
} else if (aa[i].env == NULL && ii < 0){
ii = i;
}
}
assert(ii > -1); // ii == -1 => array is full
aa[ii].env = (data == NULL) ? NULL : env;
aa[ii].data = (void *)data;
return JVMTI_ERROR_NONE;
}
/*
* Get Thread Local Storage
*
* Called by the agent to get the value of the JVMTI thread-local
* storage.
*
* REQUIRED Functionality
*/
jvmtiError JNICALL
jvmtiGetThreadLocalStorage(jvmtiEnv* env,
jthread thread,
void** data_ptr)
{
TRACE2("jvmti.thread", "GetThreadLocalStorage called");
SuspendEnabledChecker sec;
/*
* Check given env & current phase.
*/
jvmtiPhase phases[] = {JVMTI_PHASE_START, JVMTI_PHASE_LIVE};
CHECK_EVERYTHING();
if (data_ptr == NULL){
return JVMTI_ERROR_NULL_POINTER;
}
if (NULL != thread)
{
if (!is_valid_thread_object(thread)){
return JVMTI_ERROR_INVALID_THREAD;
}
}
else
thread = jthread_self();
jint state;
jvmtiError err = jvmtiGetThreadState(env, thread, &state);
if (err != JVMTI_ERROR_NONE){
return err;
}
if ((state & JVMTI_THREAD_STATE_ALIVE) == 0){
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
*data_ptr = NULL;
//if (!vm_thread)
// return JVMTI_ERROR_THREAD_NOT_ALIVE; // non-existent thread
JVMTILocalStorage* lstg = jthread_get_jvmti_local_storage(thread);
if (lstg -> env == NULL) {
if (lstg -> data != NULL) {
// we have more than one record stored;
// so, they are stored in array which is pointed at by
// vm_thread -> jvmti_local_storage -> data
JVMTILocalStorage* aa = (JVMTILocalStorage* )lstg -> data;
for (int i = 0; i < MAX_JVMTI_ENV_NUMBER; i++){
if (aa[i].env == env) {
*data_ptr = aa[i].data;
break;
}
}
}
} else {
// we have just one record stored;
// so, it's stored in vm_thread -> jvmti_local_storage
if (lstg -> env == env) {
*data_ptr = lstg -> data;
}
}
return JVMTI_ERROR_NONE;
}