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