Path: blob/master/src/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.cpp
41149 views
/*1* Copyright (c) 2016, 2020, Oracle and/or its affiliates. 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*22*/2324#include "precompiled.hpp"25#include "jvm.h"26#include "jfr/instrumentation/jfrJvmtiAgent.hpp"27#include "jfr/jni/jfrJavaSupport.hpp"28#include "jfr/jni/jfrUpcalls.hpp"29#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"30#include "jfr/recorder/service/jfrOptionSet.hpp"31#include "jfr/support/jfrJdkJfrEvent.hpp"32#include "logging/log.hpp"33#include "memory/resourceArea.hpp"34#include "prims/jvmtiEnvBase.hpp"35#include "prims/jvmtiExport.hpp"36#include "prims/jvmtiUtil.hpp"37#include "runtime/interfaceSupport.inline.hpp"38#include "runtime/thread.inline.hpp"39#include "utilities/exceptions.hpp"4041static const size_t ERROR_MSG_BUFFER_SIZE = 256;42static JfrJvmtiAgent* agent = NULL;43static jvmtiEnv* jfr_jvmti_env = NULL;4445static void check_jvmti_error(jvmtiEnv* jvmti, jvmtiError errnum, const char* str) {46if (errnum != JVMTI_ERROR_NONE) {47char* errnum_str = NULL;48jvmti->GetErrorName(errnum, &errnum_str);49log_error(jfr, system)("ERROR: JfrJvmtiAgent: " INT32_FORMAT " (%s): %s\n",50errnum,51NULL == errnum_str ? "Unknown" : errnum_str,52NULL == str ? "" : str);53}54}5556static bool set_event_notification_mode(jvmtiEventMode mode,57jvmtiEvent event,58jthread event_thread,59...) {60assert(jfr_jvmti_env != NULL, "invariant");61const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventNotificationMode(mode, event, event_thread);62check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventNotificationMode");63return jvmti_ret_code == JVMTI_ERROR_NONE;64}6566static bool update_class_file_load_hook_event(jvmtiEventMode mode) {67return set_event_notification_mode(mode, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);68}6970// jvmti event callbacks require C linkage71extern "C" void JNICALL jfr_on_class_file_load_hook(jvmtiEnv *jvmti_env,72JNIEnv* jni_env,73jclass class_being_redefined,74jobject loader,75const char* name,76jobject protection_domain,77jint class_data_len,78const unsigned char* class_data,79jint* new_class_data_len,80unsigned char** new_class_data) {81if (class_being_redefined == NULL) {82return;83}84JavaThread* jt = JavaThread::thread_from_jni_environment(jni_env);85DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));;86ThreadInVMfromNative tvmfn(jt);87JfrUpcalls::on_retransform(JfrTraceId::load_raw(class_being_redefined),88class_being_redefined,89class_data_len,90class_data,91new_class_data_len,92new_class_data,93jt);94}9596// caller needs ResourceMark97static jclass* create_classes_array(jint classes_count, TRAPS) {98assert(classes_count > 0, "invariant");99DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));100ThreadInVMfromNative tvmfn(THREAD);101jclass* const classes = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jclass, classes_count);102if (NULL == classes) {103char error_buffer[ERROR_MSG_BUFFER_SIZE];104jio_snprintf(error_buffer, ERROR_MSG_BUFFER_SIZE,105"Thread local allocation (native) of " SIZE_FORMAT " bytes failed "106"in retransform classes", sizeof(jclass) * classes_count);107log_error(jfr, system)("%s", error_buffer);108JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK_NULL);109}110return classes;111}112113// caller needs ResourceMark114static void log_and_throw(jvmtiError error, TRAPS) {115if (!HAS_PENDING_EXCEPTION) {116DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));117ThreadInVMfromNative tvmfn(THREAD);118const char base_error_msg[] = "JfrJvmtiAgent::retransformClasses failed: ";119size_t length = sizeof base_error_msg; // includes terminating null120const char* const jvmti_error_name = JvmtiUtil::error_name(error);121assert(jvmti_error_name != NULL, "invariant");122length += strlen(jvmti_error_name);123char* error_msg = NEW_RESOURCE_ARRAY(char, length);124jio_snprintf(error_msg, length, "%s%s", base_error_msg, jvmti_error_name);125if (JVMTI_ERROR_INVALID_CLASS_FORMAT == error) {126JfrJavaSupport::throw_class_format_error(error_msg, THREAD);127} else {128JfrJavaSupport::throw_runtime_exception(error_msg, THREAD);129}130}131}132133static void check_exception_and_log(JNIEnv* env, TRAPS) {134assert(env != NULL, "invariant");135if (env->ExceptionOccurred()) {136// array index out of bound137DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));138ThreadInVMfromNative tvmfn(THREAD);139log_error(jfr, system)("GetObjectArrayElement threw an exception");140return;141}142}143144static bool is_valid_jvmti_phase() {145return JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE;146}147148void JfrJvmtiAgent::retransform_classes(JNIEnv* env, jobjectArray classes_array, TRAPS) {149assert(env != NULL, "invariant");150assert(classes_array != NULL, "invariant");151assert(is_valid_jvmti_phase(), "invariant");152DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));153const jint classes_count = env->GetArrayLength(classes_array);154if (classes_count <= 0) {155return;156}157ResourceMark rm(THREAD);158jclass* const classes = create_classes_array(classes_count, CHECK);159assert(classes != NULL, "invariant");160for (jint i = 0; i < classes_count; i++) {161jclass clz = (jclass)env->GetObjectArrayElement(classes_array, i);162check_exception_and_log(env, THREAD);163classes[i] = clz;164}165{166// inspecting the oop/klass requires a thread transition167ThreadInVMfromNative transition(THREAD);168for (jint i = 0; i < classes_count; ++i) {169jclass clz = classes[i];170if (!JdkJfrEvent::is_a(clz)) {171// outside the event hierarchy172JdkJfrEvent::tag_as_host(clz);173}174}175}176DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD));177const jvmtiError result = jfr_jvmti_env->RetransformClasses(classes_count, classes);178if (result != JVMTI_ERROR_NONE) {179log_and_throw(result, THREAD);180}181}182183static bool register_callbacks(JavaThread* jt) {184assert(jfr_jvmti_env != NULL, "invariant");185DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));186jvmtiEventCallbacks callbacks;187/* Set callbacks */188memset(&callbacks, 0, sizeof(callbacks));189callbacks.ClassFileLoadHook = jfr_on_class_file_load_hook;190const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));191check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks");192return jvmti_ret_code == JVMTI_ERROR_NONE;193}194195static bool register_capabilities(JavaThread* jt) {196assert(jfr_jvmti_env != NULL, "invariant");197DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));198jvmtiCapabilities capabilities;199/* Add JVMTI capabilities */200(void)memset(&capabilities, 0, sizeof(capabilities));201capabilities.can_retransform_classes = 1;202capabilities.can_retransform_any_class = 1;203const jvmtiError jvmti_ret_code = jfr_jvmti_env->AddCapabilities(&capabilities);204check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "Add Capabilities");205return jvmti_ret_code == JVMTI_ERROR_NONE;206}207208static jint create_jvmti_env(JavaThread* jt) {209assert(jfr_jvmti_env == NULL, "invariant");210DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));211extern struct JavaVM_ main_vm;212JavaVM* vm = &main_vm;213return vm->GetEnv((void **)&jfr_jvmti_env, JVMTI_VERSION);214}215216static bool unregister_callbacks(JavaThread* jt) {217assert(jfr_jvmti_env != NULL, "invariant");218jvmtiEventCallbacks callbacks;219/* Set empty callbacks */220memset(&callbacks, 0, sizeof(callbacks));221const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));222check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks");223return jvmti_ret_code == JVMTI_ERROR_NONE;224}225226JfrJvmtiAgent::JfrJvmtiAgent() {}227228JfrJvmtiAgent::~JfrJvmtiAgent() {229JavaThread* jt = JavaThread::current();230DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));231if (jfr_jvmti_env != NULL) {232ThreadToNativeFromVM transition(jt);233update_class_file_load_hook_event(JVMTI_DISABLE);234unregister_callbacks(jt);235jfr_jvmti_env->DisposeEnvironment();236jfr_jvmti_env = NULL;237}238}239240static bool initialize(JavaThread* jt) {241assert(jt != NULL, "invariant");242DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt));243ThreadToNativeFromVM transition(jt);244if (create_jvmti_env(jt) != JNI_OK) {245assert(jfr_jvmti_env == NULL, "invariant");246return false;247}248assert(jfr_jvmti_env != NULL, "invariant");249if (!register_capabilities(jt)) {250return false;251}252if (!register_callbacks(jt)) {253return false;254}255return update_class_file_load_hook_event(JVMTI_ENABLE);256}257258static void log_and_throw_illegal_state_exception(TRAPS) {259DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));260const char* const illegal_state_msg = "An attempt was made to start JFR too early in the VM initialization sequence.";261log_error(jfr, system)(illegal_state_msg);262log_error(jfr, system)("JFR uses JVMTI RetransformClasses and requires the JVMTI state to have entered JVMTI_PHASE_LIVE.");263log_error(jfr, system)("Please initialize JFR in response to event JVMTI_EVENT_VM_INIT instead of JVMTI_EVENT_VM_START.");264JfrJavaSupport::throw_illegal_state_exception(illegal_state_msg, THREAD);265}266267bool JfrJvmtiAgent::create() {268assert(agent == NULL, "invariant");269JavaThread* const jt = JavaThread::current();270if (!is_valid_jvmti_phase()) {271log_and_throw_illegal_state_exception(jt);272return false;273}274agent = new JfrJvmtiAgent();275if (agent == NULL) {276return false;277}278if (!initialize(jt)) {279delete agent;280agent = NULL;281return false;282}283return true;284}285286void JfrJvmtiAgent::destroy() {287if (agent != NULL) {288delete agent;289agent = NULL;290}291}292293294