blob: 5d632858256e16dcffe47230235c88c3f6ff26ae [file] [log] [blame]
Andreas Gampe3c252f02016-10-27 18:25:17 -07001/* Copyright (C) 2016 The Android Open Source Project
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This file implements interfaces from the file jvmti.h. This implementation
5 * is licensed under the same terms as the file jvmti.h. The
6 * copyright and license information for the file jvmti.h follows.
7 *
8 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
9 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
10 *
11 * This code is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License version 2 only, as
13 * published by the Free Software Foundation. Oracle designates this
14 * particular file as subject to the "Classpath" exception as provided
15 * by Oracle in the LICENSE file that accompanied this code.
16 *
17 * This code is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * version 2 for more details (a copy is included in the LICENSE file that
21 * accompanied this code).
22 *
23 * You should have received a copy of the GNU General Public License version
24 * 2 along with this work; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
28 * or visit www.oracle.com if you need additional information or have any
29 * questions.
30 */
31
32#include "ti_method.h"
33
Andreas Gampe49fc60e2017-08-24 13:19:59 -070034#include <type_traits>
35
Andreas Gampe3c252f02016-10-27 18:25:17 -070036#include "art_jvmti.h"
37#include "art_method-inl.h"
38#include "base/enums.h"
Alex Lightbebd7bd2017-07-25 14:05:52 -070039#include "base/mutex-inl.h"
Andreas Gampe27dfa052017-02-16 15:04:36 -080040#include "dex_file_annotations.h"
Andreas Gampee2abbc62017-09-15 11:59:26 -070041#include "dex_file_types.h"
Alex Lightd78ddec2017-04-18 15:20:38 -070042#include "events-inl.h"
Alex Light0a5ec3d2017-07-25 16:50:26 -070043#include "jit/jit.h"
Andreas Gampe13b27842016-11-07 16:48:23 -080044#include "jni_internal.h"
Alex Lightbebd7bd2017-07-25 14:05:52 -070045#include "mirror/class-inl.h"
46#include "mirror/class_loader.h"
47#include "mirror/object-inl.h"
Andreas Gampe27dfa052017-02-16 15:04:36 -080048#include "mirror/object_array-inl.h"
Andreas Gampe36bcd4f2016-10-28 18:07:18 -070049#include "modifiers.h"
Andreas Gampe373a9b52017-10-18 09:01:57 -070050#include "nativehelper/scoped_local_ref.h"
Alex Lightd78ddec2017-04-18 15:20:38 -070051#include "runtime_callbacks.h"
Andreas Gampe3c252f02016-10-27 18:25:17 -070052#include "scoped_thread_state_change-inl.h"
Alex Lightbebd7bd2017-07-25 14:05:52 -070053#include "stack.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070054#include "thread-current-inl.h"
Alex Lightd78ddec2017-04-18 15:20:38 -070055#include "thread_list.h"
Alex Lighte814f9d2017-07-31 16:14:39 -070056#include "ti_stack.h"
Alex Lightbebd7bd2017-07-25 14:05:52 -070057#include "ti_thread.h"
Alex Light0af8cde2017-04-20 13:35:05 -070058#include "ti_phase.h"
Andreas Gampe3c252f02016-10-27 18:25:17 -070059
60namespace openjdkjvmti {
61
Alex Lightd78ddec2017-04-18 15:20:38 -070062struct TiMethodCallback : public art::MethodCallback {
63 void RegisterNativeMethod(art::ArtMethod* method,
64 const void* cur_method,
65 /*out*/void** new_method)
66 OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
67 if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
68 art::Thread* thread = art::Thread::Current();
Alex Light0af8cde2017-04-20 13:35:05 -070069 art::JNIEnvExt* jnienv = thread->GetJniEnv();
Alex Lightd78ddec2017-04-18 15:20:38 -070070 ScopedLocalRef<jthread> thread_jni(
Alex Light0af8cde2017-04-20 13:35:05 -070071 jnienv, PhaseUtil::IsLivePhase() ? jnienv->AddLocalReference<jthread>(thread->GetPeer())
72 : nullptr);
Alex Lightd78ddec2017-04-18 15:20:38 -070073 art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
74 event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(
75 thread,
Alex Light0af8cde2017-04-20 13:35:05 -070076 static_cast<JNIEnv*>(jnienv),
Alex Lightd78ddec2017-04-18 15:20:38 -070077 thread_jni.get(),
78 art::jni::EncodeArtMethod(method),
79 const_cast<void*>(cur_method),
80 new_method);
81 }
82 }
83
84 EventHandler* event_handler = nullptr;
85};
86
87TiMethodCallback gMethodCallback;
88
89void MethodUtil::Register(EventHandler* handler) {
90 gMethodCallback.event_handler = handler;
91 art::ScopedThreadStateChange stsc(art::Thread::Current(),
92 art::ThreadState::kWaitingForDebuggerToAttach);
93 art::ScopedSuspendAll ssa("Add method callback");
Alex Light21611932017-09-26 13:07:39 -070094 art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
95 callbacks->AddMethodCallback(&gMethodCallback);
Alex Lightd78ddec2017-04-18 15:20:38 -070096}
97
98void MethodUtil::Unregister() {
99 art::ScopedThreadStateChange stsc(art::Thread::Current(),
100 art::ThreadState::kWaitingForDebuggerToAttach);
101 art::ScopedSuspendAll ssa("Remove method callback");
Alex Light21611932017-09-26 13:07:39 -0700102 art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
103 callbacks->RemoveMethodCallback(&gMethodCallback);
Alex Lightd78ddec2017-04-18 15:20:38 -0700104}
105
Alex Light4c174282017-07-05 10:18:18 -0700106jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
107 jmethodID method,
108 jint* size_ptr,
109 unsigned char** bytecode_ptr) {
110 if (method == nullptr) {
111 return ERR(INVALID_METHODID);
112 }
113 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
114
115 if (art_method->IsNative()) {
116 return ERR(NATIVE_METHOD);
117 }
118
119 if (size_ptr == nullptr || bytecode_ptr == nullptr) {
120 return ERR(NULL_POINTER);
121 }
122
123 art::ScopedObjectAccess soa(art::Thread::Current());
124 const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
125 if (code_item == nullptr) {
126 *size_ptr = 0;
127 *bytecode_ptr = nullptr;
128 return OK;
129 }
130 // 2 bytes per instruction for dex code.
131 *size_ptr = code_item->insns_size_in_code_units_ * 2;
132 jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
133 if (err != OK) {
134 return err;
135 }
136 memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
137 return OK;
138}
139
Andreas Gampef71832e2017-01-09 11:38:04 -0800140jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
141 jmethodID method,
142 jint* size_ptr) {
143 if (method == nullptr) {
144 return ERR(INVALID_METHODID);
145 }
146 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
147
148 if (art_method->IsNative()) {
149 return ERR(NATIVE_METHOD);
150 }
151
152 if (size_ptr == nullptr) {
153 return ERR(NULL_POINTER);
154 }
155
156 art::ScopedObjectAccess soa(art::Thread::Current());
157 if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
Andreas Gampee1f79b62017-04-12 21:11:28 -0700158 // Use the shorty.
159 art::ArtMethod* base_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
160 size_t arg_count = art::ArtMethod::NumArgRegisters(base_method->GetShorty());
161 if (!base_method->IsStatic()) {
162 arg_count++;
163 }
164 *size_ptr = static_cast<jint>(arg_count);
Andreas Gampef71832e2017-01-09 11:38:04 -0800165 return ERR(NONE);
166 }
167
168 DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
169 *size_ptr = art_method->GetCodeItem()->ins_size_;
170
171 return ERR(NONE);
172}
173
Alex Lightce68cc62017-07-26 10:30:38 -0700174jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env,
175 jmethodID method,
176 jint* entry_count_ptr,
177 jvmtiLocalVariableEntry** table_ptr) {
178 if (method == nullptr) {
179 return ERR(INVALID_METHODID);
180 }
181 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
182
183 if (art_method->IsNative()) {
184 return ERR(NATIVE_METHOD);
185 }
186
187 if (entry_count_ptr == nullptr || table_ptr == nullptr) {
188 return ERR(NULL_POINTER);
189 }
190
191 art::ScopedObjectAccess soa(art::Thread::Current());
192 const art::DexFile* dex_file = art_method->GetDexFile();
193 const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
194 // TODO code_item == nullptr means that the method is abstract (or native, but we check that
195 // earlier). We should check what is returned by the RI in this situation since it's not clear
196 // what the appropriate return value is from the spec.
197 if (dex_file == nullptr || code_item == nullptr) {
198 return ERR(ABSENT_INFORMATION);
199 }
200
201 struct LocalVariableContext {
202 explicit LocalVariableContext(jvmtiEnv* jenv) : env_(jenv), variables_(), err_(OK) {}
203
204 static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
205 reinterpret_cast<LocalVariableContext*>(raw_ctx)->Insert(entry);
206 }
207
208 void Insert(const art::DexFile::LocalInfo& entry) {
209 if (err_ != OK) {
210 return;
211 }
212 JvmtiUniquePtr<char[]> name_str = CopyString(env_, entry.name_, &err_);
213 if (err_ != OK) {
214 return;
215 }
216 JvmtiUniquePtr<char[]> sig_str = CopyString(env_, entry.descriptor_, &err_);
217 if (err_ != OK) {
218 return;
219 }
220 JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env_, entry.signature_, &err_);
221 if (err_ != OK) {
222 return;
223 }
224 variables_.push_back({
225 .start_location = static_cast<jlocation>(entry.start_address_),
226 .length = static_cast<jint>(entry.end_address_ - entry.start_address_),
227 .name = name_str.release(),
228 .signature = sig_str.release(),
229 .generic_signature = generic_sig_str.release(),
230 .slot = entry.reg_,
231 });
232 }
233
234 jvmtiError Release(jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) {
235 jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables_.size();
236 if (err_ != OK ||
237 (err_ = env_->Allocate(table_size,
238 reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) {
239 Cleanup();
240 return err_;
241 } else {
242 *out_entry_count_ptr = variables_.size();
243 memcpy(*out_table_ptr, variables_.data(), table_size);
244 return OK;
245 }
246 }
247
248 void Cleanup() {
249 for (jvmtiLocalVariableEntry& e : variables_) {
250 env_->Deallocate(reinterpret_cast<unsigned char*>(e.name));
251 env_->Deallocate(reinterpret_cast<unsigned char*>(e.signature));
252 env_->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature));
253 }
254 }
255
256 jvmtiEnv* env_;
257 std::vector<jvmtiLocalVariableEntry> variables_;
258 jvmtiError err_;
259 };
260
261 LocalVariableContext context(env);
262 if (!dex_file->DecodeDebugLocalInfo(code_item,
263 art_method->IsStatic(),
264 art_method->GetDexMethodIndex(),
265 LocalVariableContext::Callback,
266 &context)) {
267 // Something went wrong with decoding the debug information. It might as well not be there.
268 return ERR(ABSENT_INFORMATION);
269 } else {
270 return context.Release(entry_count_ptr, table_ptr);
271 }
272}
273
Andreas Gampef71832e2017-01-09 11:38:04 -0800274jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
275 jmethodID method,
276 jint* max_ptr) {
277 if (method == nullptr) {
278 return ERR(INVALID_METHODID);
279 }
280 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
281
282 if (art_method->IsNative()) {
283 return ERR(NATIVE_METHOD);
284 }
285
286 if (max_ptr == nullptr) {
287 return ERR(NULL_POINTER);
288 }
289
290 art::ScopedObjectAccess soa(art::Thread::Current());
291 if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
292 // This isn't specified as an error case, so return 0.
293 *max_ptr = 0;
294 return ERR(NONE);
295 }
296
297 DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
298 *max_ptr = art_method->GetCodeItem()->registers_size_;
299
300 return ERR(NONE);
301}
302
Andreas Gampe3c252f02016-10-27 18:25:17 -0700303jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
304 jmethodID method,
305 char** name_ptr,
306 char** signature_ptr,
307 char** generic_ptr) {
308 art::ScopedObjectAccess soa(art::Thread::Current());
Andreas Gampe13b27842016-11-07 16:48:23 -0800309 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
Andreas Gampe3c252f02016-10-27 18:25:17 -0700310 art_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
311
Andreas Gampe54711412017-02-21 12:41:43 -0800312 JvmtiUniquePtr<char[]> name_copy;
Andreas Gampe3c252f02016-10-27 18:25:17 -0700313 if (name_ptr != nullptr) {
314 const char* method_name = art_method->GetName();
315 if (method_name == nullptr) {
316 method_name = "<error>";
317 }
Andreas Gampe54711412017-02-21 12:41:43 -0800318 jvmtiError ret;
319 name_copy = CopyString(env, method_name, &ret);
320 if (name_copy == nullptr) {
Andreas Gampe3c252f02016-10-27 18:25:17 -0700321 return ret;
322 }
Andreas Gampe54711412017-02-21 12:41:43 -0800323 *name_ptr = name_copy.get();
Andreas Gampe3c252f02016-10-27 18:25:17 -0700324 }
325
Andreas Gampe54711412017-02-21 12:41:43 -0800326 JvmtiUniquePtr<char[]> signature_copy;
Andreas Gampe3c252f02016-10-27 18:25:17 -0700327 if (signature_ptr != nullptr) {
328 const art::Signature sig = art_method->GetSignature();
329 std::string str = sig.ToString();
Andreas Gampe54711412017-02-21 12:41:43 -0800330 jvmtiError ret;
331 signature_copy = CopyString(env, str.c_str(), &ret);
332 if (signature_copy == nullptr) {
Andreas Gampe3c252f02016-10-27 18:25:17 -0700333 return ret;
334 }
Andreas Gampe54711412017-02-21 12:41:43 -0800335 *signature_ptr = signature_copy.get();
Andreas Gampe3c252f02016-10-27 18:25:17 -0700336 }
337
Andreas Gampe862bdd82016-11-18 13:31:13 -0800338 if (generic_ptr != nullptr) {
339 *generic_ptr = nullptr;
Andreas Gampe27dfa052017-02-16 15:04:36 -0800340 if (!art_method->GetDeclaringClass()->IsProxyClass()) {
341 art::mirror::ObjectArray<art::mirror::String>* str_array =
342 art::annotations::GetSignatureAnnotationForMethod(art_method);
343 if (str_array != nullptr) {
344 std::ostringstream oss;
345 for (int32_t i = 0; i != str_array->GetLength(); ++i) {
346 oss << str_array->Get(i)->ToModifiedUtf8();
347 }
348 std::string output_string = oss.str();
Andreas Gampe54711412017-02-21 12:41:43 -0800349 jvmtiError ret;
350 JvmtiUniquePtr<char[]> generic_copy = CopyString(env, output_string.c_str(), &ret);
351 if (generic_copy == nullptr) {
Andreas Gampe27dfa052017-02-16 15:04:36 -0800352 return ret;
353 }
Andreas Gampe54711412017-02-21 12:41:43 -0800354 *generic_ptr = generic_copy.release();
Andreas Gampe27dfa052017-02-16 15:04:36 -0800355 } else if (soa.Self()->IsExceptionPending()) {
356 // TODO: Should we report an error here?
357 soa.Self()->ClearException();
358 }
359 }
Andreas Gampe862bdd82016-11-18 13:31:13 -0800360 }
Andreas Gampe3c252f02016-10-27 18:25:17 -0700361
362 // Everything is fine, release the buffers.
363 name_copy.release();
364 signature_copy.release();
365
366 return ERR(NONE);
367}
368
Andreas Gampe368a2082016-10-28 17:33:13 -0700369jvmtiError MethodUtil::GetMethodDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
370 jmethodID method,
371 jclass* declaring_class_ptr) {
372 if (declaring_class_ptr == nullptr) {
373 return ERR(NULL_POINTER);
374 }
375
Andreas Gampe13b27842016-11-07 16:48:23 -0800376 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
Andreas Gampe368a2082016-10-28 17:33:13 -0700377 // Note: No GetInterfaceMethodIfProxy, we want to actual class.
378
Andreas Gampe13b27842016-11-07 16:48:23 -0800379 art::ScopedObjectAccess soa(art::Thread::Current());
Andreas Gampe368a2082016-10-28 17:33:13 -0700380 art::mirror::Class* klass = art_method->GetDeclaringClass();
381 *declaring_class_ptr = soa.AddLocalReference<jclass>(klass);
382
383 return ERR(NONE);
384}
385
Andreas Gampef71832e2017-01-09 11:38:04 -0800386jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED,
387 jmethodID method,
388 jlocation* start_location_ptr,
389 jlocation* end_location_ptr) {
390 if (method == nullptr) {
391 return ERR(INVALID_METHODID);
392 }
393 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
394
395 if (art_method->IsNative()) {
396 return ERR(NATIVE_METHOD);
397 }
398
399 if (start_location_ptr == nullptr || end_location_ptr == nullptr) {
400 return ERR(NULL_POINTER);
401 }
402
403 art::ScopedObjectAccess soa(art::Thread::Current());
404 if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
Andreas Gampee1f79b62017-04-12 21:11:28 -0700405 // This isn't specified as an error case, so return -1/-1 as the RI does.
406 *start_location_ptr = -1;
407 *end_location_ptr = -1;
Andreas Gampef71832e2017-01-09 11:38:04 -0800408 return ERR(NONE);
409 }
410
411 DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
412 *start_location_ptr = 0;
413 *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1;
414
415 return ERR(NONE);
416}
417
Andreas Gampe36bcd4f2016-10-28 18:07:18 -0700418jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
419 jmethodID method,
420 jint* modifiers_ptr) {
421 if (modifiers_ptr == nullptr) {
422 return ERR(NULL_POINTER);
423 }
424
Andreas Gampe13b27842016-11-07 16:48:23 -0800425 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
Andreas Gampe36bcd4f2016-10-28 18:07:18 -0700426 uint32_t modifiers = art_method->GetAccessFlags();
427
428 // Note: Keep this code in sync with Executable.fixMethodFlags.
429 if ((modifiers & art::kAccAbstract) != 0) {
430 modifiers &= ~art::kAccNative;
431 }
432 modifiers &= ~art::kAccSynchronized;
433 if ((modifiers & art::kAccDeclaredSynchronized) != 0) {
434 modifiers |= art::kAccSynchronized;
435 }
436 modifiers &= art::kAccJavaFlagsMask;
437
438 *modifiers_ptr = modifiers;
439 return ERR(NONE);
440}
441
Andreas Gampeda3e5612016-12-13 19:00:53 -0800442using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
443
444static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
445 LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
446 jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
447 static_cast<jint>(entry.line_) };
448 context->push_back(jvmti_entry);
449 return false; // Collect all, no early exit.
450}
451
452jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
453 jmethodID method,
454 jint* entry_count_ptr,
455 jvmtiLineNumberEntry** table_ptr) {
456 if (method == nullptr) {
457 return ERR(NULL_POINTER);
458 }
459 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
460 DCHECK(!art_method->IsRuntimeMethod());
461
462 const art::DexFile::CodeItem* code_item;
463 const art::DexFile* dex_file;
464 {
465 art::ScopedObjectAccess soa(art::Thread::Current());
466
467 if (art_method->IsProxyMethod()) {
468 return ERR(ABSENT_INFORMATION);
469 }
470 if (art_method->IsNative()) {
471 return ERR(NATIVE_METHOD);
472 }
473 if (entry_count_ptr == nullptr || table_ptr == nullptr) {
474 return ERR(NULL_POINTER);
475 }
476
477 code_item = art_method->GetCodeItem();
478 dex_file = art_method->GetDexFile();
479 DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
480 }
481
482 LineNumberContext context;
483 bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
484 if (!success) {
485 return ERR(ABSENT_INFORMATION);
486 }
487
488 unsigned char* data;
489 jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry);
490 jvmtiError alloc_error = env->Allocate(mem_size, &data);
491 if (alloc_error != ERR(NONE)) {
492 return alloc_error;
493 }
494 *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data);
495 memcpy(*table_ptr, context.data(), mem_size);
496 *entry_count_ptr = static_cast<jint>(context.size());
497
498 return ERR(NONE);
499}
500
Andreas Gampefdeef522017-01-09 14:40:25 -0800501template <typename T>
502static jvmtiError IsMethodT(jvmtiEnv* env ATTRIBUTE_UNUSED,
503 jmethodID method,
504 T test,
505 jboolean* is_t_ptr) {
506 if (method == nullptr) {
507 return ERR(INVALID_METHODID);
508 }
509 if (is_t_ptr == nullptr) {
510 return ERR(NULL_POINTER);
511 }
512
513 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
514 *is_t_ptr = test(art_method) ? JNI_TRUE : JNI_FALSE;
515
516 return ERR(NONE);
517}
518
519jvmtiError MethodUtil::IsMethodNative(jvmtiEnv* env, jmethodID m, jboolean* is_native_ptr) {
520 auto test = [](art::ArtMethod* method) {
521 return method->IsNative();
522 };
523 return IsMethodT(env, m, test, is_native_ptr);
524}
525
526jvmtiError MethodUtil::IsMethodObsolete(jvmtiEnv* env, jmethodID m, jboolean* is_obsolete_ptr) {
527 auto test = [](art::ArtMethod* method) {
528 return method->IsObsolete();
529 };
530 return IsMethodT(env, m, test, is_obsolete_ptr);
531}
532
533jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* is_synthetic_ptr) {
534 auto test = [](art::ArtMethod* method) {
535 return method->IsSynthetic();
536 };
537 return IsMethodT(env, m, test, is_synthetic_ptr);
538}
539
Alex Lightbebd7bd2017-07-25 14:05:52 -0700540class CommonLocalVariableClosure : public art::Closure {
541 public:
542 CommonLocalVariableClosure(art::Thread* caller,
543 jint depth,
544 jint slot)
545 : result_(ERR(INTERNAL)), caller_(caller), depth_(depth), slot_(slot) {}
546
547 void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
548 art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
549 std::unique_ptr<art::Context> context(art::Context::Create());
550 FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
551 visitor.WalkStack();
552 if (!visitor.FoundFrame()) {
553 // Must have been a bad depth.
554 result_ = ERR(NO_MORE_FRAMES);
555 return;
556 }
557 art::ArtMethod* method = visitor.GetMethod();
Alex Light3dea2122017-10-11 15:56:48 +0000558 // Native and 'art' proxy methods don't have registers.
559 if (method->IsNative() || method->IsProxyMethod()) {
560 // TODO It might be useful to fake up support for get at least on proxy frames.
Alex Lightbebd7bd2017-07-25 14:05:52 -0700561 result_ = ERR(OPAQUE_FRAME);
562 return;
563 } else if (method->GetCodeItem()->registers_size_ <= slot_) {
564 result_ = ERR(INVALID_SLOT);
565 return;
566 }
Alex Light0a5ec3d2017-07-25 16:50:26 -0700567 bool needs_instrument = !visitor.IsShadowFrame();
Alex Lightbebd7bd2017-07-25 14:05:52 -0700568 uint32_t pc = visitor.GetDexPc(/*abort_on_failure*/ false);
Andreas Gampee2abbc62017-09-15 11:59:26 -0700569 if (pc == art::dex::kDexNoIndex) {
Alex Lightbebd7bd2017-07-25 14:05:52 -0700570 // Cannot figure out current PC.
571 result_ = ERR(OPAQUE_FRAME);
572 return;
573 }
574 std::string descriptor;
575 art::Primitive::Type slot_type = art::Primitive::kPrimVoid;
576 jvmtiError err = GetSlotType(method, pc, &descriptor, &slot_type);
577 if (err != OK) {
578 result_ = err;
579 return;
580 }
581
582 err = GetTypeError(method, slot_type, descriptor);
583 if (err != OK) {
584 result_ = err;
585 return;
586 }
587 result_ = Execute(method, visitor);
Alex Light0a5ec3d2017-07-25 16:50:26 -0700588 if (needs_instrument) {
589 art::Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(self);
590 }
Alex Lightbebd7bd2017-07-25 14:05:52 -0700591 }
592
593 jvmtiError GetResult() const {
594 return result_;
595 }
596
597 protected:
598 virtual jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
599 REQUIRES(art::Locks::mutator_lock_) = 0;
600 virtual jvmtiError GetTypeError(art::ArtMethod* method,
601 art::Primitive::Type type,
602 const std::string& descriptor)
603 REQUIRES(art::Locks::mutator_lock_) = 0;
604
605 jvmtiError GetSlotType(art::ArtMethod* method,
606 uint32_t dex_pc,
607 /*out*/std::string* descriptor,
608 /*out*/art::Primitive::Type* type)
609 REQUIRES(art::Locks::mutator_lock_) {
610 const art::DexFile* dex_file = method->GetDexFile();
611 const art::DexFile::CodeItem* code_item = method->GetCodeItem();
612 if (dex_file == nullptr || code_item == nullptr) {
613 return ERR(OPAQUE_FRAME);
614 }
615
616 struct GetLocalVariableInfoContext {
617 explicit GetLocalVariableInfoContext(jint slot,
618 uint32_t pc,
619 std::string* out_descriptor,
620 art::Primitive::Type* out_type)
621 : found_(false), jslot_(slot), pc_(pc), descriptor_(out_descriptor), type_(out_type) {
622 *descriptor_ = "";
623 *type_ = art::Primitive::kPrimVoid;
624 }
625
626 static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
627 reinterpret_cast<GetLocalVariableInfoContext*>(raw_ctx)->Handle(entry);
628 }
629
630 void Handle(const art::DexFile::LocalInfo& entry) {
631 if (found_) {
632 return;
633 } else if (entry.start_address_ <= pc_ &&
634 entry.end_address_ > pc_ &&
635 entry.reg_ == jslot_) {
636 found_ = true;
637 *type_ = art::Primitive::GetType(entry.descriptor_[0]);
638 *descriptor_ = entry.descriptor_;
639 }
640 return;
641 }
642
643 bool found_;
644 jint jslot_;
645 uint32_t pc_;
646 std::string* descriptor_;
647 art::Primitive::Type* type_;
648 };
649
650 GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type);
651 if (!dex_file->DecodeDebugLocalInfo(code_item,
652 method->IsStatic(),
653 method->GetDexMethodIndex(),
654 GetLocalVariableInfoContext::Callback,
655 &context) || !context.found_) {
656 // Something went wrong with decoding the debug information. It might as well not be there.
657 return ERR(INVALID_SLOT);
658 } else {
659 return OK;
660 }
661 }
662
663 jvmtiError result_;
664 art::Thread* caller_;
665 jint depth_;
666 jint slot_;
667};
668
669class GetLocalVariableClosure : public CommonLocalVariableClosure {
670 public:
671 GetLocalVariableClosure(art::Thread* caller,
672 jint depth,
673 jint slot,
674 art::Primitive::Type type,
675 jvalue* val)
676 : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
677
678 protected:
679 jvmtiError GetTypeError(art::ArtMethod* method ATTRIBUTE_UNUSED,
680 art::Primitive::Type slot_type,
681 const std::string& descriptor ATTRIBUTE_UNUSED)
682 OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
683 switch (slot_type) {
684 case art::Primitive::kPrimByte:
685 case art::Primitive::kPrimChar:
686 case art::Primitive::kPrimInt:
687 case art::Primitive::kPrimShort:
688 case art::Primitive::kPrimBoolean:
689 return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
690 case art::Primitive::kPrimLong:
691 case art::Primitive::kPrimFloat:
692 case art::Primitive::kPrimDouble:
693 case art::Primitive::kPrimNot:
694 return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
695 case art::Primitive::kPrimVoid:
696 LOG(FATAL) << "Unexpected primitive type " << slot_type;
697 UNREACHABLE();
698 }
699 }
700
701 jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
702 OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
703 switch (type_) {
704 case art::Primitive::kPrimNot: {
705 uint32_t ptr_val;
706 if (!visitor.GetVReg(method,
707 static_cast<uint16_t>(slot_),
708 art::kReferenceVReg,
709 &ptr_val)) {
710 return ERR(OPAQUE_FRAME);
711 }
712 art::ObjPtr<art::mirror::Object> obj(reinterpret_cast<art::mirror::Object*>(ptr_val));
713 val_->l = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
714 break;
715 }
716 case art::Primitive::kPrimInt:
717 case art::Primitive::kPrimFloat: {
718 if (!visitor.GetVReg(method,
719 static_cast<uint16_t>(slot_),
720 type_ == art::Primitive::kPrimFloat ? art::kFloatVReg : art::kIntVReg,
721 reinterpret_cast<uint32_t*>(&val_->i))) {
722 return ERR(OPAQUE_FRAME);
723 }
724 break;
725 }
726 case art::Primitive::kPrimDouble:
727 case art::Primitive::kPrimLong: {
728 auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
729 auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
730 if (!visitor.GetVRegPair(method,
731 static_cast<uint16_t>(slot_),
732 lo_type,
733 high_type,
734 reinterpret_cast<uint64_t*>(&val_->j))) {
735 return ERR(OPAQUE_FRAME);
736 }
737 break;
738 }
739 default: {
740 LOG(FATAL) << "unexpected register type " << type_;
741 UNREACHABLE();
742 }
743 }
744 return OK;
745 }
746
747 private:
748 art::Primitive::Type type_;
749 jvalue* val_;
750};
751
752jvmtiError MethodUtil::GetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
753 jthread thread,
754 jint depth,
755 jint slot,
756 art::Primitive::Type type,
757 jvalue* val) {
758 if (depth < 0) {
759 return ERR(ILLEGAL_ARGUMENT);
760 }
761 art::Thread* self = art::Thread::Current();
Alex Light0a5ec3d2017-07-25 16:50:26 -0700762 // Suspend JIT since it can get confused if we deoptimize methods getting jitted.
763 art::jit::ScopedJitSuspend suspend_jit;
Alex Lightbebd7bd2017-07-25 14:05:52 -0700764 art::ScopedObjectAccess soa(self);
Alex Lightb1e31a82017-10-04 16:57:36 -0700765 art::Locks::thread_list_lock_->ExclusiveLock(self);
Alex Light7ddc23d2017-09-22 15:33:41 -0700766 art::Thread* target = nullptr;
767 jvmtiError err = ERR(INTERNAL);
768 if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
Alex Lightb1e31a82017-10-04 16:57:36 -0700769 art::Locks::thread_list_lock_->ExclusiveUnlock(self);
Alex Light7ddc23d2017-09-22 15:33:41 -0700770 return err;
Alex Lightbebd7bd2017-07-25 14:05:52 -0700771 }
772 GetLocalVariableClosure c(self, depth, slot, type, val);
Alex Lightb1e31a82017-10-04 16:57:36 -0700773 // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
Alex Lighte5179ce2017-10-31 22:28:11 +0000774 if (!target->RequestSynchronousCheckpoint(&c)) {
Alex Lightbebd7bd2017-07-25 14:05:52 -0700775 return ERR(THREAD_NOT_ALIVE);
776 } else {
777 return c.GetResult();
778 }
779}
780
781class SetLocalVariableClosure : public CommonLocalVariableClosure {
782 public:
783 SetLocalVariableClosure(art::Thread* caller,
784 jint depth,
785 jint slot,
786 art::Primitive::Type type,
787 jvalue val)
788 : CommonLocalVariableClosure(caller, depth, slot), type_(type), val_(val) {}
789
790 protected:
791 jvmtiError GetTypeError(art::ArtMethod* method,
792 art::Primitive::Type slot_type,
793 const std::string& descriptor)
794 OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
795 switch (slot_type) {
796 case art::Primitive::kPrimNot: {
797 if (type_ != art::Primitive::kPrimNot) {
798 return ERR(TYPE_MISMATCH);
799 } else if (val_.l == nullptr) {
800 return OK;
801 } else {
802 art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
803 art::ObjPtr<art::mirror::Class> set_class =
804 caller_->DecodeJObject(val_.l)->GetClass();
805 art::ObjPtr<art::mirror::ClassLoader> loader =
806 method->GetDeclaringClass()->GetClassLoader();
807 art::ObjPtr<art::mirror::Class> slot_class =
808 cl->LookupClass(caller_, descriptor.c_str(), loader);
809 DCHECK(!slot_class.IsNull());
810 return slot_class->IsAssignableFrom(set_class) ? OK : ERR(TYPE_MISMATCH);
811 }
812 }
813 case art::Primitive::kPrimByte:
814 case art::Primitive::kPrimChar:
815 case art::Primitive::kPrimInt:
816 case art::Primitive::kPrimShort:
817 case art::Primitive::kPrimBoolean:
818 return type_ == art::Primitive::kPrimInt ? OK : ERR(TYPE_MISMATCH);
819 case art::Primitive::kPrimLong:
820 case art::Primitive::kPrimFloat:
821 case art::Primitive::kPrimDouble:
822 return type_ == slot_type ? OK : ERR(TYPE_MISMATCH);
823 case art::Primitive::kPrimVoid:
824 LOG(FATAL) << "Unexpected primitive type " << slot_type;
825 UNREACHABLE();
826 }
827 }
828
829 jvmtiError Execute(art::ArtMethod* method, art::StackVisitor& visitor)
830 OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
831 switch (type_) {
832 case art::Primitive::kPrimNot: {
833 uint32_t ptr_val;
834 art::ObjPtr<art::mirror::Object> obj(caller_->DecodeJObject(val_.l));
835 ptr_val = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(obj.Ptr()));
836 if (!visitor.SetVReg(method,
837 static_cast<uint16_t>(slot_),
838 ptr_val,
839 art::kReferenceVReg)) {
840 return ERR(OPAQUE_FRAME);
841 }
842 break;
843 }
844 case art::Primitive::kPrimInt:
845 case art::Primitive::kPrimFloat: {
846 if (!visitor.SetVReg(method,
847 static_cast<uint16_t>(slot_),
848 static_cast<uint32_t>(val_.i),
849 type_ == art::Primitive::kPrimFloat ? art::kFloatVReg
850 : art::kIntVReg)) {
851 return ERR(OPAQUE_FRAME);
852 }
853 break;
854 }
855 case art::Primitive::kPrimDouble:
856 case art::Primitive::kPrimLong: {
857 auto lo_type = type_ == art::Primitive::kPrimLong ? art::kLongLoVReg : art::kDoubleLoVReg;
858 auto high_type = type_ == art::Primitive::kPrimLong ? art::kLongHiVReg : art::kDoubleHiVReg;
859 if (!visitor.SetVRegPair(method,
860 static_cast<uint16_t>(slot_),
861 static_cast<uint64_t>(val_.j),
862 lo_type,
863 high_type)) {
864 return ERR(OPAQUE_FRAME);
865 }
866 break;
867 }
868 default: {
869 LOG(FATAL) << "unexpected register type " << type_;
870 UNREACHABLE();
871 }
872 }
873 return OK;
874 }
875
876 private:
877 art::Primitive::Type type_;
878 jvalue val_;
879};
880
881jvmtiError MethodUtil::SetLocalVariableGeneric(jvmtiEnv* env ATTRIBUTE_UNUSED,
882 jthread thread,
883 jint depth,
884 jint slot,
885 art::Primitive::Type type,
886 jvalue val) {
887 if (depth < 0) {
888 return ERR(ILLEGAL_ARGUMENT);
889 }
890 art::Thread* self = art::Thread::Current();
Alex Light0a5ec3d2017-07-25 16:50:26 -0700891 // Suspend JIT since it can get confused if we deoptimize methods getting jitted.
892 art::jit::ScopedJitSuspend suspend_jit;
Alex Lightbebd7bd2017-07-25 14:05:52 -0700893 art::ScopedObjectAccess soa(self);
Alex Lightb1e31a82017-10-04 16:57:36 -0700894 art::Locks::thread_list_lock_->ExclusiveLock(self);
Alex Light7ddc23d2017-09-22 15:33:41 -0700895 art::Thread* target = nullptr;
896 jvmtiError err = ERR(INTERNAL);
897 if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
Alex Lightb1e31a82017-10-04 16:57:36 -0700898 art::Locks::thread_list_lock_->ExclusiveUnlock(self);
Alex Light7ddc23d2017-09-22 15:33:41 -0700899 return err;
Alex Lightbebd7bd2017-07-25 14:05:52 -0700900 }
901 SetLocalVariableClosure c(self, depth, slot, type, val);
Alex Lightb1e31a82017-10-04 16:57:36 -0700902 // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
Alex Lighte5179ce2017-10-31 22:28:11 +0000903 if (!target->RequestSynchronousCheckpoint(&c)) {
Alex Lightbebd7bd2017-07-25 14:05:52 -0700904 return ERR(THREAD_NOT_ALIVE);
905 } else {
906 return c.GetResult();
907 }
908}
909
910class GetLocalInstanceClosure : public art::Closure {
911 public:
912 GetLocalInstanceClosure(art::Thread* caller, jint depth, jobject* val)
913 : result_(ERR(INTERNAL)),
914 caller_(caller),
915 depth_(depth),
916 val_(val) {}
917
918 void Run(art::Thread* self) OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
919 art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
920 std::unique_ptr<art::Context> context(art::Context::Create());
921 FindFrameAtDepthVisitor visitor(self, context.get(), depth_);
922 visitor.WalkStack();
923 if (!visitor.FoundFrame()) {
924 // Must have been a bad depth.
925 result_ = ERR(NO_MORE_FRAMES);
926 return;
927 }
Alex Lightbebd7bd2017-07-25 14:05:52 -0700928 result_ = OK;
929 art::ObjPtr<art::mirror::Object> obj = visitor.GetThisObject();
930 *val_ = obj.IsNull() ? nullptr : caller_->GetJniEnv()->AddLocalReference<jobject>(obj);
931 }
932
933 jvmtiError GetResult() const {
934 return result_;
935 }
936
937 private:
938 jvmtiError result_;
939 art::Thread* caller_;
940 jint depth_;
941 jobject* val_;
942};
943
944jvmtiError MethodUtil::GetLocalInstance(jvmtiEnv* env ATTRIBUTE_UNUSED,
945 jthread thread,
946 jint depth,
947 jobject* data) {
948 if (depth < 0) {
949 return ERR(ILLEGAL_ARGUMENT);
950 }
951 art::Thread* self = art::Thread::Current();
952 art::ScopedObjectAccess soa(self);
Alex Lightb1e31a82017-10-04 16:57:36 -0700953 art::Locks::thread_list_lock_->ExclusiveLock(self);
Alex Light7ddc23d2017-09-22 15:33:41 -0700954 art::Thread* target = nullptr;
955 jvmtiError err = ERR(INTERNAL);
956 if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
Alex Lightb1e31a82017-10-04 16:57:36 -0700957 art::Locks::thread_list_lock_->ExclusiveUnlock(self);
Alex Light7ddc23d2017-09-22 15:33:41 -0700958 return err;
Alex Lightbebd7bd2017-07-25 14:05:52 -0700959 }
960 GetLocalInstanceClosure c(self, depth, data);
Alex Lightb1e31a82017-10-04 16:57:36 -0700961 // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
Alex Lighte5179ce2017-10-31 22:28:11 +0000962 if (!target->RequestSynchronousCheckpoint(&c)) {
Alex Lightbebd7bd2017-07-25 14:05:52 -0700963 return ERR(THREAD_NOT_ALIVE);
964 } else {
965 return c.GetResult();
966 }
967}
968
969#define FOR_JVMTI_JVALUE_TYPES(fn) \
970 fn(jint, art::Primitive::kPrimInt, i) \
971 fn(jlong, art::Primitive::kPrimLong, j) \
972 fn(jfloat, art::Primitive::kPrimFloat, f) \
973 fn(jdouble, art::Primitive::kPrimDouble, d) \
974 fn(jobject, art::Primitive::kPrimNot, l)
975
976namespace impl {
977
978template<typename T> void WriteJvalue(T, jvalue*);
979template<typename T> void ReadJvalue(jvalue, T*);
980template<typename T> art::Primitive::Type GetJNIType();
981
982#define JNI_TYPE_CHAR(type, prim, id) \
983template<> art::Primitive::Type GetJNIType<type>() { \
984 return prim; \
985}
986
987FOR_JVMTI_JVALUE_TYPES(JNI_TYPE_CHAR);
988
989#undef JNI_TYPE_CHAR
990
Andreas Gampe49fc60e2017-08-24 13:19:59 -0700991#define RW_JVALUE(srctype, prim, id) \
992 template<> void ReadJvalue<srctype>(jvalue in, std::add_pointer<srctype>::type out) { \
Alex Lightbebd7bd2017-07-25 14:05:52 -0700993 *out = in.id; \
994 } \
Andreas Gampe49fc60e2017-08-24 13:19:59 -0700995 template<> void WriteJvalue<srctype>(srctype in, jvalue* out) { \
Alex Lightbebd7bd2017-07-25 14:05:52 -0700996 out->id = in; \
997 }
998
999FOR_JVMTI_JVALUE_TYPES(RW_JVALUE);
1000
1001#undef RW_JVALUE
1002
1003} // namespace impl
1004
1005template<typename T>
1006jvmtiError MethodUtil::SetLocalVariable(jvmtiEnv* env,
1007 jthread thread,
1008 jint depth,
1009 jint slot,
1010 T data) {
1011 jvalue v = {.j = 0};
1012 art::Primitive::Type type = impl::GetJNIType<T>();
1013 impl::WriteJvalue(data, &v);
1014 return SetLocalVariableGeneric(env, thread, depth, slot, type, v);
1015}
1016
1017template<typename T>
1018jvmtiError MethodUtil::GetLocalVariable(jvmtiEnv* env,
1019 jthread thread,
1020 jint depth,
1021 jint slot,
1022 T* data) {
1023 if (data == nullptr) {
1024 return ERR(NULL_POINTER);
1025 }
1026 jvalue v = {.j = 0};
1027 art::Primitive::Type type = impl::GetJNIType<T>();
1028 jvmtiError err = GetLocalVariableGeneric(env, thread, depth, slot, type, &v);
1029 if (err != OK) {
1030 return err;
1031 } else {
1032 impl::ReadJvalue(v, data);
1033 return OK;
1034 }
1035}
1036
Andreas Gampe49fc60e2017-08-24 13:19:59 -07001037#define GET_SET_LV(srctype, prim, id) \
1038 template jvmtiError MethodUtil::GetLocalVariable<srctype>(jvmtiEnv*, \
1039 jthread, \
1040 jint, \
1041 jint, \
1042 std::add_pointer<srctype>::type); \
1043 template jvmtiError MethodUtil::SetLocalVariable<srctype>(jvmtiEnv*, \
1044 jthread, \
1045 jint, \
1046 jint, \
1047 srctype);
Alex Lightbebd7bd2017-07-25 14:05:52 -07001048
1049FOR_JVMTI_JVALUE_TYPES(GET_SET_LV);
1050
1051#undef GET_SET_LV
1052
1053#undef FOR_JVMTI_JVALUE_TYPES
1054
Andreas Gampe3c252f02016-10-27 18:25:17 -07001055} // namespace openjdkjvmti