Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/jfr/instrumentation/jfrJvmtiAgent.cpp
41149 views
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
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
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
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
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
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);
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
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);
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
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);
140
log_error(jfr, system)("GetObjectArrayElement threw an exception");
141
return;
142
}
143
}
144
145
static bool is_valid_jvmti_phase() {
146
return JvmtiEnvBase::get_phase() == JVMTI_PHASE_LIVE;
147
}
148
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);
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
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
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
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
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
227
JfrJvmtiAgent::JfrJvmtiAgent() {}
228
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
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
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
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
287
void JfrJvmtiAgent::destroy() {
288
if (agent != NULL) {
289
delete agent;
290
agent = NULL;
291
}
292
}
293
294