src/share/vm/jfr/instrumentation/jfrJvmtiAgent.cpp

Fri, 12 Jun 2020 02:59:56 +0100

author
jbachorik
date
Fri, 12 Jun 2020 02:59:56 +0100
changeset 9925
30fb8c8cceb9
parent 9858
b985cbb00e68
child 9986
85e682d8ab91
permissions
-rw-r--r--

8233197: Invert JvmtiExport::post_vm_initialized() and Jfr:on_vm_start() start-up order for correct option parsing
8246703: [TESTBUG] Add test for JDK-8233197
Reviewed-by: aph, adinn, neugens

apetushkov@9858 1 /*
jbachorik@9925 2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
apetushkov@9858 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
apetushkov@9858 4 *
apetushkov@9858 5 * This code is free software; you can redistribute it and/or modify it
apetushkov@9858 6 * under the terms of the GNU General Public License version 2 only, as
apetushkov@9858 7 * published by the Free Software Foundation.
apetushkov@9858 8 *
apetushkov@9858 9 * This code is distributed in the hope that it will be useful, but WITHOUT
apetushkov@9858 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
apetushkov@9858 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
apetushkov@9858 12 * version 2 for more details (a copy is included in the LICENSE file that
apetushkov@9858 13 * accompanied this code).
apetushkov@9858 14 *
apetushkov@9858 15 * You should have received a copy of the GNU General Public License version
apetushkov@9858 16 * 2 along with this work; if not, write to the Free Software Foundation,
apetushkov@9858 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
apetushkov@9858 18 *
apetushkov@9858 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
apetushkov@9858 20 * or visit www.oracle.com if you need additional information or have any
apetushkov@9858 21 * questions.
apetushkov@9858 22 *
apetushkov@9858 23 */
apetushkov@9858 24
apetushkov@9858 25 #include "precompiled.hpp"
apetushkov@9858 26 #include "jvm.h"
apetushkov@9858 27 #include "jfr/instrumentation/jfrJvmtiAgent.hpp"
apetushkov@9858 28 #include "jfr/jni/jfrJavaSupport.hpp"
apetushkov@9858 29 #include "jfr/jni/jfrUpcalls.hpp"
apetushkov@9858 30 #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
apetushkov@9858 31 #include "jfr/recorder/service/jfrOptionSet.hpp"
apetushkov@9858 32 #include "jfr/support/jfrEventClass.hpp"
apetushkov@9858 33 #include "memory/resourceArea.hpp"
jbachorik@9925 34 #include "prims/jvmtiEnvBase.hpp"
apetushkov@9858 35 #include "prims/jvmtiExport.hpp"
jbachorik@9925 36 #include "prims/jvmtiUtil.hpp"
apetushkov@9858 37 #include "runtime/interfaceSupport.hpp"
apetushkov@9858 38 #include "runtime/thread.inline.hpp"
apetushkov@9858 39 #include "utilities/exceptions.hpp"
apetushkov@9858 40
apetushkov@9858 41 static const size_t ERROR_MSG_BUFFER_SIZE = 256;
apetushkov@9858 42 static JfrJvmtiAgent* agent = NULL;
apetushkov@9858 43 static jvmtiEnv* jfr_jvmti_env = NULL;
apetushkov@9858 44
apetushkov@9858 45 static void check_jvmti_error(jvmtiEnv* jvmti, jvmtiError errnum, const char* str) {
apetushkov@9858 46 if (errnum != JVMTI_ERROR_NONE) {
apetushkov@9858 47 char* errnum_str = NULL;
apetushkov@9858 48 jvmti->GetErrorName(errnum, &errnum_str);
apetushkov@9858 49 if (true) tty->print_cr("ERROR: JfrJvmtiAgent: " INT32_FORMAT " (%s): %s\n",
apetushkov@9858 50 errnum,
apetushkov@9858 51 NULL == errnum_str ? "Unknown" : errnum_str,
apetushkov@9858 52 NULL == str ? "" : str);
apetushkov@9858 53 }
apetushkov@9858 54 }
apetushkov@9858 55
jbachorik@9925 56 static bool set_event_notification_mode(jvmtiEventMode mode,
jbachorik@9925 57 jvmtiEvent event,
jbachorik@9925 58 jthread event_thread,
jbachorik@9925 59 ...) {
jbachorik@9925 60 assert(jfr_jvmti_env != NULL, "invariant");
apetushkov@9858 61 const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventNotificationMode(mode, event, event_thread);
apetushkov@9858 62 check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventNotificationMode");
jbachorik@9925 63 return jvmti_ret_code == JVMTI_ERROR_NONE;
apetushkov@9858 64 }
apetushkov@9858 65
jbachorik@9925 66 static bool update_class_file_load_hook_event(jvmtiEventMode mode) {
apetushkov@9858 67 return set_event_notification_mode(mode, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
apetushkov@9858 68 }
apetushkov@9858 69
apetushkov@9858 70 static JavaThread* current_java_thread() {
apetushkov@9858 71 Thread* this_thread = Thread::current();
apetushkov@9858 72 assert(this_thread != NULL && this_thread->is_Java_thread(), "invariant");
apetushkov@9858 73 return static_cast<JavaThread*>(this_thread);
apetushkov@9858 74 }
apetushkov@9858 75
apetushkov@9858 76 // jvmti event callbacks require C linkage
apetushkov@9858 77 extern "C" void JNICALL jfr_on_class_file_load_hook(jvmtiEnv *jvmti_env,
apetushkov@9858 78 JNIEnv* jni_env,
apetushkov@9858 79 jclass class_being_redefined,
apetushkov@9858 80 jobject loader,
apetushkov@9858 81 const char* name,
apetushkov@9858 82 jobject protection_domain,
apetushkov@9858 83 jint class_data_len,
apetushkov@9858 84 const unsigned char* class_data,
apetushkov@9858 85 jint* new_class_data_len,
apetushkov@9858 86 unsigned char** new_class_data) {
apetushkov@9858 87 if (class_being_redefined == NULL) {
apetushkov@9858 88 return;
apetushkov@9858 89 }
apetushkov@9858 90 JavaThread* jt = JavaThread::thread_from_jni_environment(jni_env);
apetushkov@9858 91 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));;
apetushkov@9858 92 ThreadInVMfromNative tvmfn(jt);
apetushkov@9858 93 JfrUpcalls::on_retransform(JfrTraceId::get(class_being_redefined),
apetushkov@9858 94 class_being_redefined,
apetushkov@9858 95 class_data_len,
apetushkov@9858 96 class_data,
apetushkov@9858 97 new_class_data_len,
apetushkov@9858 98 new_class_data,
apetushkov@9858 99 jt);
apetushkov@9858 100 }
apetushkov@9858 101
apetushkov@9858 102 // caller needs ResourceMark
apetushkov@9858 103 static jclass* create_classes_array(jint classes_count, TRAPS) {
apetushkov@9858 104 assert(classes_count > 0, "invariant");
apetushkov@9858 105 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
apetushkov@9858 106 ThreadInVMfromNative tvmfn((JavaThread*)THREAD);
apetushkov@9858 107 jclass* const classes = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jclass, classes_count);
apetushkov@9858 108 if (NULL == classes) {
apetushkov@9858 109 char error_buffer[ERROR_MSG_BUFFER_SIZE];
apetushkov@9858 110 jio_snprintf(error_buffer, ERROR_MSG_BUFFER_SIZE,
apetushkov@9858 111 "Thread local allocation (native) of " SIZE_FORMAT " bytes failed "
apetushkov@9858 112 "in retransform classes", sizeof(jclass) * classes_count);
apetushkov@9858 113 if (true) tty->print_cr("%s", error_buffer);
apetushkov@9858 114 JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK_NULL);
apetushkov@9858 115 }
apetushkov@9858 116 return classes;
apetushkov@9858 117 }
apetushkov@9858 118
jbachorik@9925 119 // caller needs ResourceMark
jbachorik@9925 120 static void log_and_throw(jvmtiError error, TRAPS) {
apetushkov@9858 121 if (!HAS_PENDING_EXCEPTION) {
apetushkov@9858 122 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
apetushkov@9858 123 ThreadInVMfromNative tvmfn((JavaThread*)THREAD);
jbachorik@9925 124 const char base_error_msg[] = "JfrJvmtiAgent::retransformClasses failed: ";
jbachorik@9925 125 size_t length = sizeof base_error_msg; // includes terminating null
jbachorik@9925 126 const char* const jvmti_error_name = JvmtiUtil::error_name(error);
jbachorik@9925 127 assert(jvmti_error_name != NULL, "invariant");
jbachorik@9925 128 length += strlen(jvmti_error_name);
jbachorik@9925 129 char* error_msg = NEW_RESOURCE_ARRAY(char, length);
jbachorik@9925 130 jio_snprintf(error_msg, length, "%s%s", base_error_msg, jvmti_error_name);
jbachorik@9925 131 if (JVMTI_ERROR_INVALID_CLASS_FORMAT == error) {
jbachorik@9925 132 JfrJavaSupport::throw_class_format_error(error_msg, THREAD);
jbachorik@9925 133 } else {
jbachorik@9925 134 JfrJavaSupport::throw_runtime_exception(error_msg, THREAD);
jbachorik@9925 135 }
apetushkov@9858 136 }
apetushkov@9858 137 }
apetushkov@9858 138
apetushkov@9858 139 static void check_exception_and_log(JNIEnv* env, TRAPS) {
apetushkov@9858 140 assert(env != NULL, "invariant");
apetushkov@9858 141 if (env->ExceptionOccurred()) {
apetushkov@9858 142 // array index out of bound
apetushkov@9858 143 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
apetushkov@9858 144 ThreadInVMfromNative tvmfn((JavaThread*)THREAD);
apetushkov@9858 145 if (true) tty->print_cr("GetObjectArrayElement threw an exception");
apetushkov@9858 146 return;
apetushkov@9858 147 }
apetushkov@9858 148 }
apetushkov@9858 149
jbachorik@9925 150 static bool is_valid_jvmti_phase() {
jbachorik@9925 151 return JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE;
jbachorik@9925 152 }
jbachorik@9925 153
apetushkov@9858 154 void JfrJvmtiAgent::retransform_classes(JNIEnv* env, jobjectArray classes_array, TRAPS) {
apetushkov@9858 155 assert(env != NULL, "invariant");
jbachorik@9925 156 assert(classes_array != NULL, "invariant");
jbachorik@9925 157 assert(is_valid_jvmti_phase(), "invariant");
apetushkov@9858 158 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
apetushkov@9858 159 const jint classes_count = env->GetArrayLength(classes_array);
apetushkov@9858 160 if (classes_count <= 0) {
apetushkov@9858 161 return;
apetushkov@9858 162 }
apetushkov@9858 163 ResourceMark rm(THREAD);
apetushkov@9858 164 jclass* const classes = create_classes_array(classes_count, CHECK);
apetushkov@9858 165 assert(classes != NULL, "invariant");
apetushkov@9858 166 for (jint i = 0; i < classes_count; i++) {
apetushkov@9858 167 jclass clz = (jclass)env->GetObjectArrayElement(classes_array, i);
apetushkov@9858 168 check_exception_and_log(env, THREAD);
jbachorik@9925 169 classes[i] = clz;
jbachorik@9925 170 }
jbachorik@9925 171 {
apetushkov@9858 172 // inspecting the oop/klass requires a thread transition
jbachorik@9925 173 ThreadInVMfromNative transition((JavaThread*)THREAD);
jbachorik@9925 174 for (jint i = 0; i < classes_count; ++i) {
jbachorik@9925 175 jclass clz = classes[i];
jbachorik@9925 176 if (!JdkJfrEvent::is_a(clz)) {
apetushkov@9858 177 // outside the event hierarchy
apetushkov@9858 178 JdkJfrEvent::tag_as_host(clz);
apetushkov@9858 179 }
apetushkov@9858 180 }
apetushkov@9858 181 }
jbachorik@9925 182 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));
jbachorik@9925 183 const jvmtiError result = jfr_jvmti_env->RetransformClasses(classes_count, classes);
jbachorik@9925 184 if (result != JVMTI_ERROR_NONE) {
jbachorik@9925 185 log_and_throw(result, THREAD);
apetushkov@9858 186 }
apetushkov@9858 187 }
apetushkov@9858 188
jbachorik@9925 189 static bool register_callbacks(JavaThread* jt) {
apetushkov@9858 190 assert(jfr_jvmti_env != NULL, "invariant");
apetushkov@9858 191 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
apetushkov@9858 192 jvmtiEventCallbacks callbacks;
apetushkov@9858 193 /* Set callbacks */
apetushkov@9858 194 memset(&callbacks, 0, sizeof(callbacks));
apetushkov@9858 195 callbacks.ClassFileLoadHook = jfr_on_class_file_load_hook;
apetushkov@9858 196 const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
apetushkov@9858 197 check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks");
jbachorik@9925 198 return jvmti_ret_code == JVMTI_ERROR_NONE;
apetushkov@9858 199 }
apetushkov@9858 200
jbachorik@9925 201 static bool register_capabilities(JavaThread* jt) {
apetushkov@9858 202 assert(jfr_jvmti_env != NULL, "invariant");
apetushkov@9858 203 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
apetushkov@9858 204 jvmtiCapabilities capabilities;
apetushkov@9858 205 /* Add JVMTI capabilities */
apetushkov@9858 206 (void)memset(&capabilities, 0, sizeof(capabilities));
apetushkov@9858 207 capabilities.can_retransform_classes = 1;
apetushkov@9858 208 capabilities.can_retransform_any_class = 1;
apetushkov@9858 209 const jvmtiError jvmti_ret_code = jfr_jvmti_env->AddCapabilities(&capabilities);
apetushkov@9858 210 check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "Add Capabilities");
jbachorik@9925 211 return jvmti_ret_code == JVMTI_ERROR_NONE;
apetushkov@9858 212 }
apetushkov@9858 213
apetushkov@9858 214 static jint create_jvmti_env(JavaThread* jt) {
apetushkov@9858 215 assert(jfr_jvmti_env == NULL, "invariant");
apetushkov@9858 216 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));
apetushkov@9858 217 extern struct JavaVM_ main_vm;
apetushkov@9858 218 JavaVM* vm = &main_vm;
apetushkov@9858 219 return vm->GetEnv((void **)&jfr_jvmti_env, JVMTI_VERSION);
apetushkov@9858 220 }
apetushkov@9858 221
jbachorik@9925 222 static bool unregister_callbacks(JavaThread* jt) {
jbachorik@9925 223 assert(jfr_jvmti_env != NULL, "invariant");
apetushkov@9858 224 jvmtiEventCallbacks callbacks;
apetushkov@9858 225 /* Set empty callbacks */
apetushkov@9858 226 memset(&callbacks, 0, sizeof(callbacks));
apetushkov@9858 227 const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
apetushkov@9858 228 check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks");
jbachorik@9925 229 return jvmti_ret_code == JVMTI_ERROR_NONE;
apetushkov@9858 230 }
apetushkov@9858 231
apetushkov@9858 232 JfrJvmtiAgent::JfrJvmtiAgent() {}
apetushkov@9858 233
apetushkov@9858 234 JfrJvmtiAgent::~JfrJvmtiAgent() {
apetushkov@9858 235 JavaThread* jt = current_java_thread();
apetushkov@9858 236 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
apetushkov@9858 237 if (jfr_jvmti_env != NULL) {
jbachorik@9925 238 ThreadToNativeFromVM transition(jt);
jbachorik@9925 239 update_class_file_load_hook_event(JVMTI_DISABLE);
jbachorik@9925 240 unregister_callbacks(jt);
apetushkov@9858 241 jfr_jvmti_env->DisposeEnvironment();
apetushkov@9858 242 jfr_jvmti_env = NULL;
apetushkov@9858 243 }
apetushkov@9858 244 }
apetushkov@9858 245
jbachorik@9925 246 static bool initialize(JavaThread* jt) {
apetushkov@9858 247 assert(jt != NULL, "invariant");
apetushkov@9858 248 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));
apetushkov@9858 249 ThreadToNativeFromVM transition(jt);
apetushkov@9858 250 if (create_jvmti_env(jt) != JNI_OK) {
apetushkov@9858 251 assert(jfr_jvmti_env == NULL, "invariant");
apetushkov@9858 252 return false;
apetushkov@9858 253 }
apetushkov@9858 254 assert(jfr_jvmti_env != NULL, "invariant");
jbachorik@9925 255 if (!register_capabilities(jt)) {
apetushkov@9858 256 return false;
apetushkov@9858 257 }
jbachorik@9925 258 if (!register_callbacks(jt)) {
apetushkov@9858 259 return false;
apetushkov@9858 260 }
jbachorik@9925 261 return update_class_file_load_hook_event(JVMTI_ENABLE);
jbachorik@9925 262 }
jbachorik@9925 263
jbachorik@9925 264 static void log_and_throw_illegal_state_exception(TRAPS) {
jbachorik@9925 265 DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
jbachorik@9925 266 const char* const illegal_state_msg = "An attempt was made to start JFR too early in the VM initialization sequence.";
jbachorik@9925 267 if (true) {
jbachorik@9925 268 tty->print_cr("%s\n", illegal_state_msg);
jbachorik@9925 269 tty->print_cr("JFR uses JVMTI RetransformClasses and requires the JVMTI state to have entered JVMTI_PHASE_LIVE.\n");
jbachorik@9925 270 tty->print_cr("Please initialize JFR in response to event JVMTI_EVENT_VM_INIT instead of JVMTI_EVENT_VM_START.\n");
apetushkov@9858 271 }
jbachorik@9925 272 JfrJavaSupport::throw_illegal_state_exception(illegal_state_msg, THREAD);
apetushkov@9858 273 }
apetushkov@9858 274
apetushkov@9858 275 bool JfrJvmtiAgent::create() {
jbachorik@9925 276 assert(agent == NULL, "invariant");
jbachorik@9925 277 JavaThread* const jt = current_java_thread();
jbachorik@9925 278 if (!is_valid_jvmti_phase()) {
jbachorik@9925 279 log_and_throw_illegal_state_exception(jt);
jbachorik@9925 280 return false;
jbachorik@9925 281 }
apetushkov@9858 282 agent = new JfrJvmtiAgent();
apetushkov@9858 283 if (agent == NULL) {
apetushkov@9858 284 return false;
apetushkov@9858 285 }
jbachorik@9925 286 if (!initialize(jt)) {
apetushkov@9858 287 delete agent;
apetushkov@9858 288 agent = NULL;
apetushkov@9858 289 return false;
apetushkov@9858 290 }
apetushkov@9858 291 return true;
apetushkov@9858 292 }
apetushkov@9858 293
apetushkov@9858 294 void JfrJvmtiAgent::destroy() {
apetushkov@9858 295 if (agent != NULL) {
apetushkov@9858 296 delete agent;
apetushkov@9858 297 agent = NULL;
apetushkov@9858 298 }
apetushkov@9858 299 }

mercurial