1 | /* |
2 | * Copyright (c) 2016, 2018, 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/jfrEventClass.hpp" |
33 | #include "logging/log.hpp" |
34 | #include "memory/resourceArea.hpp" |
35 | #include "prims/jvmtiExport.hpp" |
36 | #include "runtime/interfaceSupport.inline.hpp" |
37 | #include "runtime/thread.inline.hpp" |
38 | #include "utilities/exceptions.hpp" |
39 | |
40 | static const size_t ERROR_MSG_BUFFER_SIZE = 256; |
41 | static JfrJvmtiAgent* agent = NULL; |
42 | static jvmtiEnv* jfr_jvmti_env = NULL; |
43 | |
44 | static void check_jvmti_error(jvmtiEnv* jvmti, jvmtiError errnum, const char* str) { |
45 | if (errnum != JVMTI_ERROR_NONE) { |
46 | char* errnum_str = NULL; |
47 | jvmti->GetErrorName(errnum, &errnum_str); |
48 | log_error(jfr, system)("ERROR: JfrJvmtiAgent: " INT32_FORMAT " (%s): %s\n" , |
49 | errnum, |
50 | NULL == errnum_str ? "Unknown" : errnum_str, |
51 | NULL == str ? "" : str); |
52 | } |
53 | } |
54 | |
55 | static jvmtiError set_event_notification_mode(jvmtiEventMode mode, |
56 | jvmtiEvent event, |
57 | jthread event_thread, |
58 | ...) { |
59 | if (jfr_jvmti_env == NULL) { |
60 | return JVMTI_ERROR_NONE; |
61 | } |
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; |
65 | } |
66 | |
67 | static jvmtiError 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 | static JavaThread* current_java_thread() { |
72 | Thread* this_thread = Thread::current(); |
73 | assert(this_thread != NULL && this_thread->is_Java_thread(), "invariant" ); |
74 | return static_cast<JavaThread*>(this_thread); |
75 | } |
76 | |
77 | // jvmti event callbacks require C linkage |
78 | extern "C" void JNICALL jfr_on_class_file_load_hook(jvmtiEnv *jvmti_env, |
79 | JNIEnv* jni_env, |
80 | jclass class_being_redefined, |
81 | jobject loader, |
82 | const char* name, |
83 | jobject protection_domain, |
84 | jint class_data_len, |
85 | const unsigned char* class_data, |
86 | jint* new_class_data_len, |
87 | unsigned char** new_class_data) { |
88 | if (class_being_redefined == NULL) { |
89 | return; |
90 | } |
91 | JavaThread* jt = JavaThread::thread_from_jni_environment(jni_env); |
92 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt));; |
93 | ThreadInVMfromNative tvmfn(jt); |
94 | JfrUpcalls::on_retransform(JfrTraceId::get(class_being_redefined), |
95 | class_being_redefined, |
96 | class_data_len, |
97 | class_data, |
98 | new_class_data_len, |
99 | new_class_data, |
100 | jt); |
101 | } |
102 | |
103 | // caller needs ResourceMark |
104 | static jclass* create_classes_array(jint classes_count, TRAPS) { |
105 | assert(classes_count > 0, "invariant" ); |
106 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); |
107 | ThreadInVMfromNative tvmfn((JavaThread*)THREAD); |
108 | jclass* const classes = NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jclass, classes_count); |
109 | if (NULL == classes) { |
110 | char error_buffer[ERROR_MSG_BUFFER_SIZE]; |
111 | jio_snprintf(error_buffer, ERROR_MSG_BUFFER_SIZE, |
112 | "Thread local allocation (native) of " SIZE_FORMAT " bytes failed " |
113 | "in retransform classes" , sizeof(jclass) * classes_count); |
114 | log_error(jfr, system)("%s" , error_buffer); |
115 | JfrJavaSupport::throw_out_of_memory_error(error_buffer, CHECK_NULL); |
116 | } |
117 | return classes; |
118 | } |
119 | |
120 | static void log_and_throw(TRAPS) { |
121 | if (!HAS_PENDING_EXCEPTION) { |
122 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); |
123 | ThreadInVMfromNative tvmfn((JavaThread*)THREAD); |
124 | log_error(jfr, system)("JfrJvmtiAgent::retransformClasses failed" ); |
125 | JfrJavaSupport::throw_class_format_error("JfrJvmtiAgent::retransformClasses failed" , THREAD); |
126 | } |
127 | } |
128 | |
129 | static void check_exception_and_log(JNIEnv* env, TRAPS) { |
130 | assert(env != NULL, "invariant" ); |
131 | if (env->ExceptionOccurred()) { |
132 | // array index out of bound |
133 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); |
134 | ThreadInVMfromNative tvmfn((JavaThread*)THREAD); |
135 | log_error(jfr, system)("GetObjectArrayElement threw an exception" ); |
136 | return; |
137 | } |
138 | } |
139 | |
140 | void JfrJvmtiAgent::retransform_classes(JNIEnv* env, jobjectArray classes_array, TRAPS) { |
141 | assert(env != NULL, "invariant" ); |
142 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(THREAD)); |
143 | if (classes_array == NULL) { |
144 | return; |
145 | } |
146 | const jint classes_count = env->GetArrayLength(classes_array); |
147 | if (classes_count <= 0) { |
148 | return; |
149 | } |
150 | ResourceMark rm(THREAD); |
151 | jclass* const classes = create_classes_array(classes_count, CHECK); |
152 | assert(classes != NULL, "invariant" ); |
153 | for (jint i = 0; i < classes_count; i++) { |
154 | jclass clz = (jclass)env->GetObjectArrayElement(classes_array, i); |
155 | check_exception_and_log(env, THREAD); |
156 | |
157 | // inspecting the oop/klass requires a thread transition |
158 | { |
159 | ThreadInVMfromNative transition((JavaThread*)THREAD); |
160 | if (JdkJfrEvent::is_a(clz)) { |
161 | // should have been tagged already |
162 | assert(JdkJfrEvent::is_subklass(clz), "invariant" ); |
163 | } else { |
164 | // outside the event hierarchy |
165 | JdkJfrEvent::tag_as_host(clz); |
166 | } |
167 | } |
168 | |
169 | classes[i] = clz; |
170 | } |
171 | if (jfr_jvmti_env->RetransformClasses(classes_count, classes) != JVMTI_ERROR_NONE) { |
172 | log_and_throw(THREAD); |
173 | } |
174 | } |
175 | |
176 | static jvmtiError register_callbacks(JavaThread* jt) { |
177 | assert(jfr_jvmti_env != NULL, "invariant" ); |
178 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); |
179 | jvmtiEventCallbacks callbacks; |
180 | /* Set callbacks */ |
181 | memset(&callbacks, 0, sizeof(callbacks)); |
182 | callbacks.ClassFileLoadHook = jfr_on_class_file_load_hook; |
183 | const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); |
184 | check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks" ); |
185 | return jvmti_ret_code; |
186 | } |
187 | |
188 | static jvmtiError register_capabilities(JavaThread* jt) { |
189 | assert(jfr_jvmti_env != NULL, "invariant" ); |
190 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); |
191 | jvmtiCapabilities capabilities; |
192 | /* Add JVMTI capabilities */ |
193 | (void)memset(&capabilities, 0, sizeof(capabilities)); |
194 | capabilities.can_retransform_classes = 1; |
195 | capabilities.can_retransform_any_class = 1; |
196 | const jvmtiError jvmti_ret_code = jfr_jvmti_env->AddCapabilities(&capabilities); |
197 | check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "Add Capabilities" ); |
198 | return jvmti_ret_code; |
199 | } |
200 | |
201 | static jint create_jvmti_env(JavaThread* jt) { |
202 | assert(jfr_jvmti_env == NULL, "invariant" ); |
203 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(jt)); |
204 | extern struct JavaVM_ main_vm; |
205 | JavaVM* vm = &main_vm; |
206 | return vm->GetEnv((void **)&jfr_jvmti_env, JVMTI_VERSION); |
207 | } |
208 | |
209 | static jvmtiError unregister_callbacks(JavaThread* jt) { |
210 | if (jfr_jvmti_env == NULL) { |
211 | return JVMTI_ERROR_NONE; |
212 | } |
213 | jvmtiEventCallbacks callbacks; |
214 | /* Set empty callbacks */ |
215 | memset(&callbacks, 0, sizeof(callbacks)); |
216 | const jvmtiError jvmti_ret_code = jfr_jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); |
217 | check_jvmti_error(jfr_jvmti_env, jvmti_ret_code, "SetEventCallbacks" ); |
218 | return jvmti_ret_code; |
219 | } |
220 | |
221 | JfrJvmtiAgent::JfrJvmtiAgent() {} |
222 | |
223 | JfrJvmtiAgent::~JfrJvmtiAgent() { |
224 | JavaThread* jt = current_java_thread(); |
225 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); |
226 | ThreadToNativeFromVM transition(jt); |
227 | update_class_file_load_hook_event(JVMTI_DISABLE); |
228 | unregister_callbacks(jt); |
229 | if (jfr_jvmti_env != NULL) { |
230 | jfr_jvmti_env->DisposeEnvironment(); |
231 | jfr_jvmti_env = NULL; |
232 | } |
233 | agent = NULL; |
234 | } |
235 | |
236 | static bool initialize() { |
237 | JavaThread* const jt = current_java_thread(); |
238 | assert(jt != NULL, "invariant" ); |
239 | assert(jt->thread_state() == _thread_in_vm, "invariant" ); |
240 | DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(jt)); |
241 | ThreadToNativeFromVM transition(jt); |
242 | if (create_jvmti_env(jt) != JNI_OK) { |
243 | assert(jfr_jvmti_env == NULL, "invariant" ); |
244 | return false; |
245 | } |
246 | assert(jfr_jvmti_env != NULL, "invariant" ); |
247 | if (register_capabilities(jt) != JVMTI_ERROR_NONE) { |
248 | return false; |
249 | } |
250 | if (register_callbacks(jt) != JVMTI_ERROR_NONE) { |
251 | return false; |
252 | } |
253 | if (update_class_file_load_hook_event(JVMTI_ENABLE) != JVMTI_ERROR_NONE) { |
254 | return false; |
255 | } |
256 | return true; |
257 | } |
258 | |
259 | bool JfrJvmtiAgent::create() { |
260 | assert(jfr_jvmti_env == NULL, "invariant" ); |
261 | agent = new JfrJvmtiAgent(); |
262 | if (agent == NULL) { |
263 | return false; |
264 | } |
265 | if (!initialize()) { |
266 | delete agent; |
267 | agent = NULL; |
268 | return false; |
269 | } |
270 | return true; |
271 | } |
272 | |
273 | void JfrJvmtiAgent::destroy() { |
274 | if (agent != NULL) { |
275 | delete agent; |
276 | agent = NULL; |
277 | } |
278 | } |
279 | |
280 | |