1 /* Copyright (C) 2017 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 <vector>
33
34 #include "ti_extension.h"
35
36 #include "art_jvmti.h"
37 #include "events.h"
38 #include "ti_allocator.h"
39 #include "ti_class.h"
40 #include "ti_ddms.h"
41 #include "ti_dump.h"
42 #include "ti_heap.h"
43 #include "ti_logging.h"
44 #include "ti_monitor.h"
45
46 #include "thread-inl.h"
47
48 namespace openjdkjvmti {
49
50 struct CParamInfo {
51 const char* name;
52 jvmtiParamKind kind;
53 jvmtiParamTypes base_type;
54 jboolean null_ok;
55
ToParamInfoopenjdkjvmti::CParamInfo56 jvmtiParamInfo ToParamInfo(jvmtiEnv* env,
57 /*out*/std::vector<JvmtiUniquePtr<char[]>>* char_buffers,
58 /*out*/jvmtiError* err) const {
59 JvmtiUniquePtr<char[]> param_name = CopyString(env, name, err);
60 char* name_ptr = param_name.get();
61 char_buffers->push_back(std::move(param_name));
62 return jvmtiParamInfo{ name_ptr, kind, base_type, null_ok };
63 }
64 };
65
GetExtensionFunctions(jvmtiEnv * env,jint * extension_count_ptr,jvmtiExtensionFunctionInfo ** extensions)66 jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env,
67 jint* extension_count_ptr,
68 jvmtiExtensionFunctionInfo** extensions) {
69 if (extension_count_ptr == nullptr || extensions == nullptr) {
70 return ERR(NULL_POINTER);
71 }
72
73 std::vector<jvmtiExtensionFunctionInfo> ext_vector;
74
75 // Holders for allocated values.
76 std::vector<JvmtiUniquePtr<char[]>> char_buffers;
77 std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers;
78 std::vector<JvmtiUniquePtr<jvmtiError[]>> error_buffers;
79
80 auto add_extension = [&](jvmtiExtensionFunction func,
81 const char* id,
82 const char* short_description,
83 const std::vector<CParamInfo>& params,
84 const std::vector<jvmtiError>& errors) {
85 jvmtiExtensionFunctionInfo func_info;
86 jvmtiError error;
87
88 func_info.func = func;
89
90 JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error);
91 if (id_ptr == nullptr) {
92 return error;
93 }
94 func_info.id = id_ptr.get();
95 char_buffers.push_back(std::move(id_ptr));
96
97 JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error);
98 if (descr == nullptr) {
99 return error;
100 }
101 func_info.short_description = descr.get();
102 char_buffers.push_back(std::move(descr));
103
104 func_info.param_count = params.size();
105 if (!params.empty()) {
106 JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr =
107 AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, params.size(), &error);
108 if (params_ptr == nullptr) {
109 return error;
110 }
111 func_info.params = params_ptr.get();
112 param_buffers.push_back(std::move(params_ptr));
113
114 for (jint i = 0; i != func_info.param_count; ++i) {
115 func_info.params[i] = params[i].ToParamInfo(env, &char_buffers, &error);
116 if (error != OK) {
117 return error;
118 }
119 }
120 } else {
121 func_info.params = nullptr;
122 }
123
124 func_info.error_count = errors.size();
125 if (!errors.empty()) {
126 JvmtiUniquePtr<jvmtiError[]> errors_ptr =
127 AllocJvmtiUniquePtr<jvmtiError[]>(env, errors.size(), &error);
128 if (errors_ptr == nullptr) {
129 return error;
130 }
131 func_info.errors = errors_ptr.get();
132 error_buffers.push_back(std::move(errors_ptr));
133
134 for (jint i = 0; i != func_info.error_count; ++i) {
135 func_info.errors[i] = errors[i];
136 }
137 } else {
138 func_info.errors = nullptr;
139 }
140
141 ext_vector.push_back(func_info);
142
143 return ERR(NONE);
144 };
145
146 jvmtiError error;
147
148 // Heap extensions.
149 error = add_extension(
150 reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetObjectHeapId),
151 "com.android.art.heap.get_object_heap_id",
152 "Retrieve the heap id of the the object tagged with the given argument. An "
153 "arbitrary object is chosen if multiple objects exist with the same tag.",
154 {
155 { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false},
156 { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false}
157 },
158 { JVMTI_ERROR_NOT_FOUND });
159 if (error != ERR(NONE)) {
160 return error;
161 }
162
163 error = add_extension(
164 reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName),
165 "com.android.art.heap.get_heap_name",
166 "Retrieve the name of the heap with the given id.",
167 {
168 { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
169 { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false}
170 },
171 { JVMTI_ERROR_ILLEGAL_ARGUMENT });
172 if (error != ERR(NONE)) {
173 return error;
174 }
175
176 error = add_extension(
177 reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::IterateThroughHeapExt),
178 "com.android.art.heap.iterate_through_heap_ext",
179 "Iterate through a heap. This is equivalent to the standard IterateThroughHeap function,"
180 " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks"
181 " structure is reused, with the callbacks field overloaded to a signature of "
182 "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).",
183 {
184 { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
185 { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true},
186 { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false},
187 { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true}
188 },
189 {
190 ERR(MUST_POSSESS_CAPABILITY),
191 ERR(INVALID_CLASS),
192 ERR(NULL_POINTER),
193 });
194 if (error != ERR(NONE)) {
195 return error;
196 }
197
198 error = add_extension(
199 reinterpret_cast<jvmtiExtensionFunction>(AllocUtil::GetGlobalJvmtiAllocationState),
200 "com.android.art.alloc.get_global_jvmti_allocation_state",
201 "Returns the total amount of memory currently allocated by all jvmtiEnvs through the"
202 " 'Allocate' jvmti function. This does not include any memory that has been deallocated"
203 " through the 'Deallocate' function. This number is approximate and might not correspond"
204 " exactly to the sum of the sizes of all not freed allocations.",
205 {
206 { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false},
207 },
208 { ERR(NULL_POINTER) });
209 if (error != ERR(NONE)) {
210 return error;
211 }
212
213 // DDMS extension
214 error = add_extension(
215 reinterpret_cast<jvmtiExtensionFunction>(DDMSUtil::HandleChunk),
216 "com.android.art.internal.ddm.process_chunk",
217 "Handles a single ddms chunk request and returns a response. The reply data is in the ddms"
218 " chunk format. It returns the processed chunk. This is provided for backwards compatibility"
219 " reasons only. Agents should avoid making use of this extension when possible and instead"
220 " use the other JVMTI entrypoints explicitly.",
221 {
222 { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
223 { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
224 { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, true },
225 { "type_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
226 { "data_len_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
227 { "data_out", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_JBYTE, false }
228 },
229 { ERR(NULL_POINTER), ERR(ILLEGAL_ARGUMENT), ERR(OUT_OF_MEMORY) });
230 if (error != ERR(NONE)) {
231 return error;
232 }
233
234 // GetClassLoaderClassDescriptors extension
235 error = add_extension(
236 reinterpret_cast<jvmtiExtensionFunction>(ClassUtil::GetClassLoaderClassDescriptors),
237 "com.android.art.class.get_class_loader_class_descriptors",
238 "Retrieves a list of all the classes (as class descriptors) that the given class loader is"
239 " capable of being the defining class loader for. The return format is a list of"
240 " null-terminated descriptor strings of the form \"L/java/lang/Object;\". Each descriptor"
241 " will be in the list at most once. If the class_loader is null the bootclassloader will be"
242 " used. If the class_loader is not null it must either be a java.lang.BootClassLoader, a"
243 " dalvik.system.BaseDexClassLoader or a derived type. The data_out list and all elements"
244 " must be deallocated by the caller.",
245 {
246 { "class_loader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, true },
247 { "class_descriptor_count_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
248 { "data_out", JVMTI_KIND_ALLOC_ALLOC_BUF, JVMTI_TYPE_CCHAR, false },
249 },
250 {
251 ERR(NULL_POINTER),
252 ERR(ILLEGAL_ARGUMENT),
253 ERR(OUT_OF_MEMORY),
254 ERR(NOT_IMPLEMENTED),
255 });
256 if (error != ERR(NONE)) {
257 return error;
258 }
259
260 // Raw monitors no suspend
261 error = add_extension(
262 reinterpret_cast<jvmtiExtensionFunction>(MonitorUtil::RawMonitorEnterNoSuspend),
263 "com.android.art.concurrent.raw_monitor_enter_no_suspend",
264 "Normally entering a monitor will not return until both the monitor is locked and the"
265 " current thread is not suspended. This method will return once the monitor is locked"
266 " even if the thread is suspended. Note that using rawMonitorWait will wait until the"
267 " thread is not suspended again on wakeup and so should be avoided.",
268 {
269 { "raw_monitor", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false },
270 },
271 {
272 ERR(NULL_POINTER),
273 ERR(INVALID_MONITOR),
274 });
275 if (error != ERR(NONE)) {
276 return error;
277 }
278
279 // GetLastError extension
280 error = add_extension(
281 reinterpret_cast<jvmtiExtensionFunction>(LogUtil::GetLastError),
282 "com.android.art.misc.get_last_error_message",
283 "In some cases the jvmti plugin will log data about errors to the android logcat. These can"
284 " be useful to tools so we make (some) of the messages available here as well. This will"
285 " fill the given 'msg' buffer with the last non-fatal message associated with this"
286 " jvmti-env. Note this is best-effort only, not all log messages will be accessible through"
287 " this API. This will return the last error-message from all threads. Care should be taken"
288 " interpreting the return value when used with a multi-threaded program. The error message"
289 " will only be cleared by a call to 'com.android.art.misc.clear_last_error_message' and will"
290 " not be cleared by intervening successful calls. If no (tracked) error message has been"
291 " sent since the last call to clear_last_error_message this API will return"
292 " JVMTI_ERROR_ABSENT_INFORMATION. Not all failures will cause an error message to be"
293 " recorded.",
294 {
295 { "msg", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false },
296 },
297 {
298 ERR(NULL_POINTER),
299 ERR(ABSENT_INFORMATION),
300 });
301 if (error != ERR(NONE)) {
302 return error;
303 }
304
305 // ClearLastError extension
306 error = add_extension(
307 reinterpret_cast<jvmtiExtensionFunction>(LogUtil::ClearLastError),
308 "com.android.art.misc.clear_last_error_message",
309 "Clears the error message returned by 'com.android.art.misc.get_last_error_message'.",
310 { },
311 { });
312 if (error != ERR(NONE)) {
313 return error;
314 }
315
316 // DumpInternalState
317 error = add_extension(
318 reinterpret_cast<jvmtiExtensionFunction>(DumpUtil::DumpInternalState),
319 "com.android.art.misc.get_plugin_internal_state",
320 "Gets internal state about the plugin and serializes it to the given msg. "
321 "There is no particular format to this message beyond being human readable.",
322 {
323 { "msg", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false },
324 },
325 { ERR(NULL_POINTER) });
326 if (error != ERR(NONE)) {
327 return error;
328 }
329
330 // Copy into output buffer.
331
332 *extension_count_ptr = ext_vector.size();
333 JvmtiUniquePtr<jvmtiExtensionFunctionInfo[]> out_data =
334 AllocJvmtiUniquePtr<jvmtiExtensionFunctionInfo[]>(env, ext_vector.size(), &error);
335 if (out_data == nullptr) {
336 return error;
337 }
338 memcpy(out_data.get(),
339 ext_vector.data(),
340 ext_vector.size() * sizeof(jvmtiExtensionFunctionInfo));
341 *extensions = out_data.release();
342
343 // Release all the buffer holders, we're OK now.
344 for (auto& holder : char_buffers) {
345 holder.release();
346 }
347 for (auto& holder : param_buffers) {
348 holder.release();
349 }
350 for (auto& holder : error_buffers) {
351 holder.release();
352 }
353
354 return OK;
355 }
356
357
GetExtensionEvents(jvmtiEnv * env,jint * extension_count_ptr,jvmtiExtensionEventInfo ** extensions)358 jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env,
359 jint* extension_count_ptr,
360 jvmtiExtensionEventInfo** extensions) {
361 std::vector<jvmtiExtensionEventInfo> ext_vector;
362
363 // Holders for allocated values.
364 std::vector<JvmtiUniquePtr<char[]>> char_buffers;
365 std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers;
366
367 auto add_extension = [&](ArtJvmtiEvent extension_event_index,
368 const char* id,
369 const char* short_description,
370 const std::vector<CParamInfo>& params) {
371 DCHECK(IsExtensionEvent(extension_event_index));
372 jvmtiExtensionEventInfo event_info;
373 jvmtiError error;
374
375 event_info.extension_event_index = static_cast<jint>(extension_event_index);
376
377 JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error);
378 if (id_ptr == nullptr) {
379 return error;
380 }
381 event_info.id = id_ptr.get();
382 char_buffers.push_back(std::move(id_ptr));
383
384 JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error);
385 if (descr == nullptr) {
386 return error;
387 }
388 event_info.short_description = descr.get();
389 char_buffers.push_back(std::move(descr));
390
391 event_info.param_count = params.size();
392 if (!params.empty()) {
393 JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr =
394 AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, params.size(), &error);
395 if (params_ptr == nullptr) {
396 return error;
397 }
398 event_info.params = params_ptr.get();
399 param_buffers.push_back(std::move(params_ptr));
400
401 for (jint i = 0; i != event_info.param_count; ++i) {
402 event_info.params[i] = params[i].ToParamInfo(env, &char_buffers, &error);
403 if (error != OK) {
404 return error;
405 }
406 }
407 } else {
408 event_info.params = nullptr;
409 }
410
411 ext_vector.push_back(event_info);
412
413 return ERR(NONE);
414 };
415
416 jvmtiError error;
417 error = add_extension(
418 ArtJvmtiEvent::kDdmPublishChunk,
419 "com.android.art.internal.ddm.publish_chunk",
420 "Called when there is new ddms information that the agent or other clients can use. The"
421 " agent is given the 'type' of the ddms chunk and a 'data_size' byte-buffer in 'data'."
422 " The 'data' pointer is only valid for the duration of the publish_chunk event. The agent"
423 " is responsible for interpreting the information present in the 'data' buffer. This is"
424 " provided for backwards-compatibility support only. Agents should prefer to use relevant"
425 " JVMTI events and functions above listening for this event.",
426 {
427 { "jni_env", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, false },
428 { "type", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
429 { "data_size", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
430 { "data", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false },
431 });
432 if (error != OK) {
433 return error;
434 }
435
436 // Copy into output buffer.
437
438 *extension_count_ptr = ext_vector.size();
439 JvmtiUniquePtr<jvmtiExtensionEventInfo[]> out_data =
440 AllocJvmtiUniquePtr<jvmtiExtensionEventInfo[]>(env, ext_vector.size(), &error);
441 if (out_data == nullptr) {
442 return error;
443 }
444 memcpy(out_data.get(),
445 ext_vector.data(),
446 ext_vector.size() * sizeof(jvmtiExtensionEventInfo));
447 *extensions = out_data.release();
448
449 // Release all the buffer holders, we're OK now.
450 for (auto& holder : char_buffers) {
451 holder.release();
452 }
453 for (auto& holder : param_buffers) {
454 holder.release();
455 }
456
457 return OK;
458 }
459
SetExtensionEventCallback(jvmtiEnv * env,jint extension_event_index,jvmtiExtensionEvent callback,EventHandler * event_handler)460 jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env,
461 jint extension_event_index,
462 jvmtiExtensionEvent callback,
463 EventHandler* event_handler) {
464 if (!IsExtensionEvent(extension_event_index)) {
465 return ERR(ILLEGAL_ARGUMENT);
466 }
467 ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env);
468 jvmtiEventMode mode = callback == nullptr ? JVMTI_DISABLE : JVMTI_ENABLE;
469 // Lock the event_info_mutex_ while we set the event to make sure it isn't lost by a concurrent
470 // change to the normal callbacks.
471 {
472 art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_);
473 if (art_env->event_callbacks.get() == nullptr) {
474 art_env->event_callbacks.reset(new ArtJvmtiEventCallbacks());
475 }
476 jvmtiError err = art_env->event_callbacks->Set(extension_event_index, callback);
477 if (err != OK) {
478 return err;
479 }
480 }
481 return event_handler->SetEvent(art_env,
482 /*thread=*/nullptr,
483 static_cast<ArtJvmtiEvent>(extension_event_index),
484 mode);
485 }
486
487 } // namespace openjdkjvmti
488