Path: blob/master/test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetLocalWithoutSuspendTest.cpp
41153 views
/*1* Copyright (c) 2020 SAP SE. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223#include <stdio.h>24#include <string.h>25#include "jvmti.h"26#include "jni.h"272829//30// Please also read the @comment in GetLocalWithoutSuspendTest.java31//3233extern "C" {3435////////////////////////////////////////////////////36// BEGIN: Shared Variables37// The following variables are shared between agent and target thread3839// glws_monitor is used to synchronize access to shared variables.40static jrawMonitorID glws_monitor;4142// Target thread for agent operations.43static jthread target_thread = NULL;4445// Depth of the frame for GetLocalObject() call by the agent thread. It is set by the target thread.46// -1 is the signal to shut down.47static int depth_for_get_local = 0;4849enum TestState {50Initial,5152TargetInNative, // The agent waits for the target thread to reach53// the native method notifyAgentToGetLocal. Then it54// reads depth_for_get_local and changes the state55// to AgentInGetLocal. After that it56// calls GetLocalObject().5758AgentInGetLocal, // The target thread waits for the agent to call59// GetLocalObject(). When this state is reached it60// resets the state to Initial and returns from61// native after a short spin wait racing the agent62// thread doing the unsafe stack walk.6364ShutDown,6566Terminated67};6869// Current test state. It is used to synchronize agent and target thread execution.70static TestState test_state;7172// END: Shared Variables73////////////////////////////////////////////////////747576// Dummy counter used in spin wait. It is declared volatile to prevent the compiler77// from eliminating the whole spin loop.78static volatile int dummy_counter = 0;7980// Makes a string of the argument (which is not macro-expanded)81#define STR(a) #a8283// Makes a string of the macro expansion of a84#define XSTR(a) STR(a)8586#define AT_LINE " ERROR at line " XSTR(__LINE__)8788static jvmtiEnv* jvmti = NULL;8990static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);9192static char* GetErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode) {93char *errMsg;94jvmtiError result = jvmti->GetErrorName(errCode, &errMsg);95return result == JVMTI_ERROR_NONE ? errMsg : NULL;96}9798static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) {99char* errMsg = GetErrorMessage(jvmti, errCode);100101if (errMsg != NULL) {102fprintf(stderr, "AGENT: %s: %s (%d)\n", message, errMsg, errCode);103jvmti->Deallocate((unsigned char *)errMsg);104} else {105fprintf(stderr, "AGENT: %s (%d)\n", message, errCode);106}107}108109static void monitor_enter(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {110jvmtiError err = jvmti->RawMonitorEnter(glws_monitor);111if (err != JVMTI_ERROR_NONE) {112ShowErrorMessage(jvmti, err, loc);113env->FatalError(loc);114}115}116117static void monitor_exit(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {118jvmtiError err = jvmti->RawMonitorExit(glws_monitor);119if (err != JVMTI_ERROR_NONE) {120ShowErrorMessage(jvmti, err, loc);121env->FatalError(loc);122}123}124125static void monitor_wait(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {126jvmtiError err = jvmti->RawMonitorWait(glws_monitor, 0);127if (err != JVMTI_ERROR_NONE) {128ShowErrorMessage(jvmti, err, loc);129env->FatalError(loc);130}131}132133static void monitor_notify(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {134jvmtiError err = jvmti->RawMonitorNotify(glws_monitor);135if (err != JVMTI_ERROR_NONE) {136ShowErrorMessage(jvmti, err, loc);137env->FatalError(loc);138}139}140141static void monitor_destroy(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {142jvmtiError err = jvmti->DestroyRawMonitor(glws_monitor);143if (err != JVMTI_ERROR_NONE) {144ShowErrorMessage(jvmti, err, loc);145env->FatalError(loc);146}147}148149// Perform GetLocalObject() at the requested depth while target thread is running.150// Note that the JVMTI spec does not require to suspend the target thread.151void test_GetLocalObject(jvmtiEnv* jvmti, JNIEnv* env, int depth) {152jvmtiError err;153jobject obj;154char* errMsg;155156printf("AGENT: calling GetLocalObject() with depth %d\n", depth);157err = jvmti->GetLocalObject(target_thread, depth, 0, &obj);158errMsg = GetErrorMessage(jvmti, err);159printf("AGENT: GetLocalObject() result code %s (%d)\n", errMsg != NULL ? errMsg : "N/A", err);160if (errMsg != NULL) {161jvmti->Deallocate((unsigned char *)errMsg);162}163fflush(stdout);164165// If the target thread wins the race we can get errors because we166// don't find a frame at the given depth or we find a non-java frame167// there (e.g. native frame). This is expected.168// JVMTI_ERROR_INVALID_SLOT can occur also because the target thread is169// running and the GetLocalObject() call might coincidentally refer to the170// frame of a static method without parameters.171if (err != JVMTI_ERROR_NONE &&172err != JVMTI_ERROR_NO_MORE_FRAMES &&173err != JVMTI_ERROR_OPAQUE_FRAME &&174err != JVMTI_ERROR_INVALID_SLOT) {175ShowErrorMessage(jvmti, err, "AgentThreadLoop: error in JVMTI GetLocalObject");176env->FatalError("AgentThreadLoop: error in JVMTI GetLocalObject\n");177}178}179180// Function holding the main loop for the test agent thread.181//182// The agent does the following in each loop iteration:183//184// - Wait for the target thread either to start a new test iteration or to185// signal shutdown.186//187// Shutdown is signalled by setting test_state to ShutDown. The agent reacts188// to it by changing test_state to Terminated and then exits.189//190// In the case of a new test iteration the target thread builds a deep call191// stack and then calls the native method notifyAgentToGetLocal(). While in192// native code its stack is walkable. It sets the shared variable test_state193// to TargetInNative and then uses the glws_monitor to send the194// notification to the agent thread.195//196// - Read the shared variable depth_for_get_local which was set by the target197// thread before sending the notification.198//199// - Set test_state to AgentInGetLocal and notify the target thread.200//201// - Perform the JVMTI GetLocal call at depth_for_get_local racing the target202// thread returning from the native call making its stack not walkable. The VM203// will crash if this happens while the stack is walked to find the frame for204// the GetLocal operation. The deeper the frame the more likely the crash205// because the stack walk takes longer.206//207JNIEXPORT void JNICALL208AgentThreadLoop(jvmtiEnv * jvmti, JNIEnv* env, void * arg) {209jvmtiError err;210jvmtiThreadInfo thread_info;211212// Wait until target_thread is set by target thread.213monitor_enter(jvmti, env, AT_LINE);214while (target_thread == NULL) {215monitor_wait(jvmti, env, AT_LINE);216}217monitor_exit(jvmti, env, AT_LINE);218219err = jvmti->GetThreadInfo(target_thread, &thread_info);220if (err != JVMTI_ERROR_NONE) {221ShowErrorMessage(jvmti, err, "AgentThreadLoop: error in JVMTI GetThreadInfo");222env->FatalError("AgentThreadLoop: error in JVMTI GetThreadInfo\n");223}224225printf("AGENT: AgentThreadLoop thread started. Polling thread '%s' for local variables\n",226thread_info.name);227jvmti->Deallocate((unsigned char *) thread_info.name);228229do {230int depth;231232monitor_enter(jvmti, env, AT_LINE);233234// Wait for java part to build large stack and then become stack walk235// save by calling the native method notifyAgentToGetLocal or to signal236// shutdown.237while (test_state != TargetInNative) {238if (test_state == ShutDown) {239test_state = Terminated;240monitor_notify(jvmti, env, AT_LINE);241monitor_exit(jvmti, env, AT_LINE);242return;243}244monitor_wait(jvmti, env, AT_LINE);245}246depth = depth_for_get_local;247248// Notify target thread that this thread is about to query the local value.249test_state = AgentInGetLocal;250monitor_notify(jvmti, env, AT_LINE);251252monitor_exit(jvmti, env, AT_LINE);253254// Now get the local object from the target thread's stack.255test_GetLocalObject(jvmti, env, depth);256} while (true);257258printf("AGENT: AgentThreadLoop thread: exiting\n");259}260261// Called by target thread after building a large stack.262// By calling this native method, the thread's stack becomes walkable.263// It notifies the agent to do the GetLocalObject() call and then races264// it to make its stack not walkable by returning from the native call.265JNIEXPORT void JNICALL266Java_GetLocalWithoutSuspendTest_notifyAgentToGetLocal(JNIEnv *env, jclass cls, jint depth, jint waitCycles) {267monitor_enter(jvmti, env, AT_LINE);268269// Set depth_for_get_local and notify agent that the target thread is ready for the GetLocalObject() call270depth_for_get_local = depth;271test_state = TargetInNative;272273monitor_notify(jvmti, env, AT_LINE);274275// Wait for agent thread to read depth_for_get_local and do the GetLocalObject() call276while (test_state != AgentInGetLocal) {277monitor_wait(jvmti, env, AT_LINE);278}279280// Reset state to Initial281test_state = Initial;282283monitor_exit(jvmti, env, AT_LINE);284285// Wait a little until agent thread is in unsafe stack walk.286// This needs to be a spin wait or sleep because we cannot get a notification287// from there.288while (--waitCycles > 0) {289dummy_counter++;290}291}292293// Called by target thread to signal shutdown. The target thread waits for the294// agent's acknowledge by changing test_state to Terminated.295JNIEXPORT void JNICALL296Java_GetLocalWithoutSuspendTest_shutDown(JNIEnv *env, jclass cls) {297monitor_enter(jvmti, env, AT_LINE);298299// Notify agent thread to shut down300test_state = ShutDown;301monitor_notify(jvmti, env, AT_LINE);302303// Wait for agent to terminate304while (test_state != Terminated) {305monitor_wait(jvmti, env, AT_LINE);306}307308monitor_exit(jvmti, env, AT_LINE);309310// Destroy glws_monitor311monitor_destroy(jvmti, env, AT_LINE);312}313314// Called by target thread to provide agent with its thread object315JNIEXPORT void JNICALL316Java_GetLocalWithoutSuspendTest_setTargetThread(JNIEnv *env, jclass cls, jthread target) {317monitor_enter(jvmti, env, AT_LINE);318319target_thread = env->NewGlobalRef(target);320321monitor_notify(jvmti, env, AT_LINE);322323monitor_exit(jvmti, env, AT_LINE);324}325326void JNICALL VMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thr) {327jvmtiError err;328jobject agent_thread_name;329jclass thread_clas;330jmethodID thread_ctro;331jthread agent_thread;332333printf("AGENT: VM init event\n");334printf("AGENT: Start new thread that performs GetLocalObject calls on a running target thread\n");335336agent_thread_name = env->NewStringUTF("GetLocalWithoutSuspendTest Agent Thread");337if (agent_thread_name == NULL) {338env->FatalError("VMInit: NewStringUTF failed\n");339}340341thread_clas = env->FindClass("java/lang/Thread");342if (agent_thread_name == NULL) {343env->FatalError("VMInit: java.lang.Thread class not found\n");344}345346thread_ctro = env->GetMethodID(thread_clas, "<init>", "(Ljava/lang/String;)V");347if (thread_ctro == NULL) {348env->FatalError("VMInit: failed to get ID for the Thread ctor\n");349}350351agent_thread = (jthread) env->NewObject(thread_clas, thread_ctro, agent_thread_name);352if (agent_thread == NULL) {353env->FatalError("VMInit: Failed to allocate thread object\n");354}355356err = jvmti->RunAgentThread(agent_thread, &AgentThreadLoop, NULL,357JVMTI_THREAD_NORM_PRIORITY);358if (err != JVMTI_ERROR_NONE) {359ShowErrorMessage(jvmti, err, "VMInit: failed to start GetLocalWithoutSuspendTest thread");360return;361}362}363364JNIEXPORT jint JNICALL365Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {366printf("AGENT: Agent_OnLoad started.\n");367return Agent_Initialize(jvm, options, reserved);368}369370JNIEXPORT jint JNICALL371Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {372printf("AGENT: Agent_OnAttach started.");373return Agent_Initialize(jvm, options, reserved);374}375376JNIEXPORT jint JNICALL377JNI_OnLoad(JavaVM *jvm, void *reserved) {378jint res;379JNIEnv *env;380381printf("AGENT: JNI_OnLoad started.");382res = jvm->GetEnv((void **) &env, JNI_VERSION_9);383if (res != JNI_OK || env == NULL) {384fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res);385return JNI_ERR;386}387388return JNI_VERSION_9;389}390391static392jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {393jint res;394jvmtiError err;395jvmtiCapabilities caps;396jvmtiEventCallbacks callbacks;397398printf("AGENT: Agent_Initialize started\n");399400memset(&caps, 0, sizeof(caps));401memset(&callbacks, 0, sizeof(callbacks));402403res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_9);404if (res != JNI_OK || jvmti == NULL) {405fprintf(stderr, "Error: GetEnv(JVMTI_VERSION_9) call failed(%d)!\n", res);406return JNI_ERR;407}408409caps.can_access_local_variables = 1;410411err = jvmti->AddCapabilities(&caps);412if (err != JVMTI_ERROR_NONE) {413ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI AddCapabilities");414return JNI_ERR;415}416417err = jvmti->GetCapabilities(&caps);418if (err != JVMTI_ERROR_NONE) {419ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI GetCapabilities");420return JNI_ERR;421}422423if (!caps.can_access_local_variables) {424fprintf(stderr, "Warning: Access to local variables is not implemented\n");425return JNI_ERR;426}427428callbacks.VMInit = &VMInit;429err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));430if (err != JVMTI_ERROR_NONE) {431ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI SetEventCallbacks");432return JNI_ERR;433}434435err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);436if (err != JVMTI_ERROR_NONE) {437ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI SetEventNotificationMode");438return JNI_ERR;439}440441err = jvmti->CreateRawMonitor("GetLocalWithoutSuspend Test Monitor", &glws_monitor);442443printf("AGENT: Agent_Initialize finished\n");444return JNI_OK;445}446447}448449450