# HG changeset patch # User sspitsyn # Date 1554942543 25200 # Node ID 96230a5ef2ec1b6508f9851b22f854e0353cfb18 # Parent 30aca1e755bf1d9703f01d8015e7185496a4492b 8222072: JVMTI GenerateEvents() sends CompiledMethodLoad events to wrong jvmtiEnv Summary: Fix GenerateEvents() to send CompiledMethodLoad events to requesting agent only Reviewed-by: jcbeyler, amenkov diff -r 30aca1e755bf -r 96230a5ef2ec src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp --- a/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp Thu Apr 11 01:21:11 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiCodeBlobEvents.cpp Wed Apr 10 17:29:03 2019 -0700 @@ -236,7 +236,7 @@ // Don't hold the lock over the notify or jmethodID creation MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); current->get_and_cache_jmethod_id(); - JvmtiExport::post_compiled_method_load(current); + JvmtiExport::post_compiled_method_load(env, current); } return JVMTI_ERROR_NONE; } diff -r 30aca1e755bf -r 96230a5ef2ec src/hotspot/share/prims/jvmtiExport.cpp --- a/src/hotspot/share/prims/jvmtiExport.cpp Thu Apr 11 01:21:11 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiExport.cpp Wed Apr 10 17:29:03 2019 -0700 @@ -2170,61 +2170,37 @@ JvmtiEnvIterator it; for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { - if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_LOAD)) { - if (env->phase() == JVMTI_PHASE_PRIMORDIAL) { - continue; - } - EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, - ("[%s] class compile method load event sent %s.%s ", - JvmtiTrace::safe_get_thread_name(thread), - (nm->method() == NULL) ? "NULL" : nm->method()->klass_name()->as_C_string(), - (nm->method() == NULL) ? "NULL" : nm->method()->name()->as_C_string())); - ResourceMark rm(thread); - HandleMark hm(thread); - - // Add inlining information - jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm); - // Pass inlining information through the void pointer - JvmtiCompiledMethodLoadEventMark jem(thread, nm, inlinerecord); - JvmtiJavaThreadEventTransition jet(thread); - jvmtiEventCompiledMethodLoad callback = env->callbacks()->CompiledMethodLoad; - if (callback != NULL) { - (*callback)(env->jvmti_external(), jem.jni_methodID(), - jem.code_size(), jem.code_data(), jem.map_length(), - jem.map(), jem.compile_info()); - } - } + post_compiled_method_load(env, nm); } } - // post a COMPILED_METHOD_LOAD event for a given environment -void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, const jmethodID method, const jint length, - const void *code_begin, const jint map_length, - const jvmtiAddrLocationMap* map) -{ - if (env->phase() <= JVMTI_PHASE_PRIMORDIAL) { +void JvmtiExport::post_compiled_method_load(JvmtiEnv* env, nmethod *nm) { + if (env->phase() == JVMTI_PHASE_PRIMORDIAL || !env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_LOAD)) { + return; + } + jvmtiEventCompiledMethodLoad callback = env->callbacks()->CompiledMethodLoad; + if (callback == NULL) { return; } JavaThread* thread = JavaThread::current(); - EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, - ("[%s] method compile load event triggered (by GenerateEvents)", - JvmtiTrace::safe_get_thread_name(thread))); - if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_LOAD)) { - - EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, - ("[%s] class compile method load event sent (by GenerateEvents), jmethodID=" PTR_FORMAT, - JvmtiTrace::safe_get_thread_name(thread), p2i(method))); - - JvmtiEventMark jem(thread); - JvmtiJavaThreadEventTransition jet(thread); - jvmtiEventCompiledMethodLoad callback = env->callbacks()->CompiledMethodLoad; - if (callback != NULL) { - (*callback)(env->jvmti_external(), method, - length, code_begin, map_length, - map, NULL); - } - } + + EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_LOAD, + ("[%s] method compile load event sent %s.%s ", + JvmtiTrace::safe_get_thread_name(thread), + (nm->method() == NULL) ? "NULL" : nm->method()->klass_name()->as_C_string(), + (nm->method() == NULL) ? "NULL" : nm->method()->name()->as_C_string())); + ResourceMark rm(thread); + HandleMark hm(thread); + + // Add inlining information + jvmtiCompiledMethodLoadInlineRecord* inlinerecord = create_inline_record(nm); + // Pass inlining information through the void pointer + JvmtiCompiledMethodLoadEventMark jem(thread, nm, inlinerecord); + JvmtiJavaThreadEventTransition jet(thread); + (*callback)(env->jvmti_external(), jem.jni_methodID(), + jem.code_size(), jem.code_data(), jem.map_length(), + jem.map(), jem.compile_info()); } void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) { diff -r 30aca1e755bf -r 96230a5ef2ec src/hotspot/share/prims/jvmtiExport.hpp --- a/src/hotspot/share/prims/jvmtiExport.hpp Thu Apr 11 01:21:11 2019 +0200 +++ b/src/hotspot/share/prims/jvmtiExport.hpp Wed Apr 10 17:29:03 2019 -0700 @@ -165,9 +165,7 @@ // DynamicCodeGenerated events for a given environment. friend class JvmtiCodeBlobEvents; - static void post_compiled_method_load(JvmtiEnv* env, const jmethodID method, const jint length, - const void *code_begin, const jint map_length, - const jvmtiAddrLocationMap* map) NOT_JVMTI_RETURN; + static void post_compiled_method_load(JvmtiEnv* env, nmethod *nm) NOT_JVMTI_RETURN; static void post_dynamic_code_generated(JvmtiEnv* env, const char *name, const void *code_begin, const void *code_end) NOT_JVMTI_RETURN; diff -r 30aca1e755bf -r 96230a5ef2ec test/hotspot/jtreg/serviceability/jvmti/GenerateEvents/MyPackage/GenerateEventsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/jvmti/GenerateEvents/MyPackage/GenerateEventsTest.java Wed Apr 10 17:29:03 2019 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8222072 + * @summary Send CompiledMethodLoad events only to the environment requested it with GenerateEvents + * @compile GenerateEventsTest.java + * @run main/othervm/native -agentlib:GenerateEvents1 -agentlib:GenerateEvents2 MyPackage.GenerateEventsTest + */ + +package MyPackage; + +public class GenerateEventsTest { + static native void agent1GenerateEvents(); + static native void agent2SetThread(Thread thread); + static native boolean agent1FailStatus(); + static native boolean agent2FailStatus(); + + public static void main(String[] args) { + agent2SetThread(Thread.currentThread()); + agent1GenerateEvents(); // Re-generate CompiledMethodLoad events + if (agent1FailStatus()|| agent2FailStatus()) { + throw new RuntimeException("GenerateEventsTest failed!"); + } + System.out.println("GenerateEventsTest passed!"); + } +} diff -r 30aca1e755bf -r 96230a5ef2ec test/hotspot/jtreg/serviceability/jvmti/GenerateEvents/libGenerateEvents1.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/jvmti/GenerateEvents/libGenerateEvents1.cpp Wed Apr 10 17:29:03 2019 -0700 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include "jvmti.h" + +extern "C" { + +#define AGENT_NAME "agent1" + +static JavaVM *java_vm = NULL; +static jthread exp_thread = NULL; +static jvmtiEnv *jvmti1 = NULL; +static jint agent1_event_count = 0; +static bool fail_status = false; + +static void +check_jvmti_status(JNIEnv* env, jvmtiError err, const char* msg) { + if (err != JVMTI_ERROR_NONE) { + printf("check_jvmti_status: JVMTI function returned error: %d\n", err); + fail_status = true; + env->FatalError(msg); + } +} + +static void JNICALL +CompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, + jint code_size, const void* code_addr, + jint map_length, const jvmtiAddrLocationMap* map, + const void* compile_info) { + JNIEnv* env = NULL; + jthread thread = NULL; + char* name = NULL; + char* sign = NULL; + jvmtiError err; + + // Posted on JavaThread's, so it is legal to obtain JNIEnv* + if (java_vm->GetEnv((void **) (&env), JNI_VERSION_9) != JNI_OK) { + printf("CompiledMethodLoad: failed to obtain JNIEnv*\n"); + fail_status = true; + return; + } + + jvmti->GetCurrentThread(&thread); + if (!env->IsSameObject(thread, exp_thread)) { + return; // skip events from unexpected threads + } + agent1_event_count++; + + err = jvmti->GetMethodName(method, &name, &sign, NULL); + check_jvmti_status(env, err, "CompiledMethodLoad: Error in JVMTI GetMethodName"); + + printf("%s: CompiledMethodLoad: %s%s\n", AGENT_NAME, name, sign); + fflush(0); +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + jvmtiEventCallbacks callbacks; + jvmtiCapabilities caps; + jvmtiError err; + + java_vm = jvm; + if (jvm->GetEnv((void **) (&jvmti1), JVMTI_VERSION) != JNI_OK) { + printf("Agent_OnLoad: Error in GetEnv in obtaining jvmtiEnv*\n"); + fail_status = true; + return JNI_ERR; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.CompiledMethodLoad = &CompiledMethodLoad; + + err = jvmti1->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks)); + if (err != JVMTI_ERROR_NONE) { + printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err); + fail_status = true; + return JNI_ERR; + } + + memset(&caps, 0, sizeof(caps)); + caps.can_generate_compiled_method_load_events = 1; + + err = jvmti1->AddCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err); + fail_status = true; + return JNI_ERR; + } + return JNI_OK; +} + +JNIEXPORT void JNICALL +Java_MyPackage_GenerateEventsTest_agent1GenerateEvents(JNIEnv *env, jclass cls) { + jthread thread = NULL; + jvmtiError err; + + err = jvmti1->GetCurrentThread(&thread); + check_jvmti_status(env, err, "generateEvents1: Error in JVMTI GetCurrentThread"); + + exp_thread = (jthread)env->NewGlobalRef(thread); + + err = jvmti1->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); + check_jvmti_status(env, err, "generateEvents1: Error in JVMTI SetEventNotificationMode: JVMTI_ENABLE"); + + err = jvmti1->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD); + check_jvmti_status(env, err, "generateEvents1: Error in JVMTI GenerateEvents"); + + err = jvmti1->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); + check_jvmti_status(env, err, "generateEvents1: Error in JVMTI SetEventNotificationMode: JVMTI_DISABLE"); +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_GenerateEventsTest_agent1FailStatus(JNIEnv *env, jclass cls) { + return fail_status; +} + +} // extern "C" diff -r 30aca1e755bf -r 96230a5ef2ec test/hotspot/jtreg/serviceability/jvmti/GenerateEvents/libGenerateEvents2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/serviceability/jvmti/GenerateEvents/libGenerateEvents2.cpp Wed Apr 10 17:29:03 2019 -0700 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include "jvmti.h" + +extern "C" { + +#define AGENT_NAME "agent2" + +static JavaVM *java_vm = NULL; +static jthread exp_thread = NULL; +static jvmtiEnv *jvmti2 = NULL; +static jint agent2_event_count = 0; +static bool fail_status = false; + +static void +check_jvmti_status(JNIEnv* env, jvmtiError err, const char* msg) { + if (err != JVMTI_ERROR_NONE) { + printf("check_jvmti_status: JVMTI function returned error: %d\n", err); + fail_status = true; + env->FatalError(msg); + } +} + +static void JNICALL +CompiledMethodLoad(jvmtiEnv* jvmti, jmethodID method, + jint code_size, const void* code_addr, + jint map_length, const jvmtiAddrLocationMap* map, + const void* compile_info) { + JNIEnv* env = NULL; + jthread thread = NULL; + char* name = NULL; + char* sign = NULL; + jvmtiError err; + + // Posted on JavaThread's, so it is legal to obtain JNIEnv* + if (java_vm->GetEnv((void **) (&env), JNI_VERSION_9) != JNI_OK) { + fail_status = true; + return; + } + + err = jvmti->GetCurrentThread(&thread); + check_jvmti_status(env, err, "CompiledMethodLoad: Error in JVMTI GetCurrentThread"); + if (!env->IsSameObject(thread, exp_thread)) { + return; // skip events from unexpected threads + } + agent2_event_count++; + + err = jvmti->GetMethodName(method, &name, &sign, NULL); + check_jvmti_status(env, err, "CompiledMethodLoad: Error in JVMTI GetMethodName"); + + printf("%s: CompiledMethodLoad: %s%s\n", AGENT_NAME, name, sign); + fflush(0); +} + +JNIEXPORT jint JNICALL +Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { + jvmtiEventCallbacks callbacks; + jvmtiCapabilities caps; + jvmtiError err; + + java_vm = jvm; + if (jvm->GetEnv((void **) (&jvmti2), JVMTI_VERSION_9) != JNI_OK) { + return JNI_ERR; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.CompiledMethodLoad = &CompiledMethodLoad; + + err = jvmti2->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks)); + if (err != JVMTI_ERROR_NONE) { + printf("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err); + fail_status = true; + return JNI_ERR; + } + + memset(&caps, 0, sizeof(caps)); + caps.can_generate_compiled_method_load_events = 1; + + err = jvmti2->AddCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + printf("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err); + fail_status = true; + return JNI_ERR; + } + return JNI_OK; +} + +JNIEXPORT void JNICALL +Java_MyPackage_GenerateEventsTest_agent2SetThread(JNIEnv *env, jclass cls, jthread thread) { + jvmtiError err; + + exp_thread = (jthread)env->NewGlobalRef(thread); + + err = jvmti2->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); + check_jvmti_status(env, err, "setThread2: Error in JVMTI SetEventNotificationMode: JVMTI_ENABLE"); +} + +JNIEXPORT jboolean JNICALL +Java_MyPackage_GenerateEventsTest_agent2FailStatus(JNIEnv *env, jclass cls) { + jvmtiError err; + + err = jvmti2->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); + check_jvmti_status(env, err, "check2: Error in JVMTI SetEventNotificationMode: JVMTI_DISABLE"); + + printf("\n"); + if (agent2_event_count == 0) { + printf("check2: Zero events in agent2 as expected\n"); + } else { + fail_status = true; + printf("check2: Unexpected non-zero event count in agent2: %d\n", agent2_event_count); + } + printf("\n"); + fflush(0); + + return fail_status; +} + +} // extern "C"