1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "dalvik_system_VMDebug.h"
18
19 #include <string.h>
20 #include <unistd.h>
21
22 #include <sstream>
23
24 #include "nativehelper/jni_macros.h"
25
26 #include "base/file_utils.h"
27 #include "base/histogram-inl.h"
28 #include "base/time_utils.h"
29 #include "class_linker.h"
30 #include "common_throws.h"
31 #include "debugger.h"
32 #include "gc/space/bump_pointer_space.h"
33 #include "gc/space/dlmalloc_space.h"
34 #include "gc/space/large_object_space.h"
35 #include "gc/space/space-inl.h"
36 #include "gc/space/zygote_space.h"
37 #include "handle_scope-inl.h"
38 #include "hprof/hprof.h"
39 #include "jni/java_vm_ext.h"
40 #include "jni/jni_internal.h"
41 #include "mirror/array-alloc-inl.h"
42 #include "mirror/array-inl.h"
43 #include "mirror/class.h"
44 #include "mirror/object_array-inl.h"
45 #include "native_util.h"
46 #include "nativehelper/scoped_local_ref.h"
47 #include "nativehelper/scoped_utf_chars.h"
48 #include "scoped_fast_native_object_access-inl.h"
49 #include "trace.h"
50 #include "well_known_classes.h"
51
52 namespace art {
53
VMDebug_getVmFeatureList(JNIEnv * env,jclass)54 static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
55 static const char* features[] = {
56 "method-trace-profiling",
57 "method-trace-profiling-streaming",
58 "method-sample-profiling",
59 "hprof-heap-dump",
60 "hprof-heap-dump-streaming",
61 };
62 jobjectArray result = env->NewObjectArray(arraysize(features),
63 WellKnownClasses::java_lang_String,
64 nullptr);
65 if (result != nullptr) {
66 for (size_t i = 0; i < arraysize(features); ++i) {
67 ScopedLocalRef<jstring> jfeature(env, env->NewStringUTF(features[i]));
68 if (jfeature.get() == nullptr) {
69 return nullptr;
70 }
71 env->SetObjectArrayElement(result, i, jfeature.get());
72 }
73 }
74 return result;
75 }
76
VMDebug_startAllocCounting(JNIEnv *,jclass)77 static void VMDebug_startAllocCounting(JNIEnv*, jclass) {
78 Runtime::Current()->SetStatsEnabled(true);
79 }
80
VMDebug_stopAllocCounting(JNIEnv *,jclass)81 static void VMDebug_stopAllocCounting(JNIEnv*, jclass) {
82 Runtime::Current()->SetStatsEnabled(false);
83 }
84
VMDebug_getAllocCount(JNIEnv *,jclass,jint kind)85 static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) {
86 return static_cast<jint>(Runtime::Current()->GetStat(kind));
87 }
88
VMDebug_resetAllocCount(JNIEnv *,jclass,jint kinds)89 static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) {
90 Runtime::Current()->ResetStats(kinds);
91 }
92
VMDebug_startMethodTracingDdmsImpl(JNIEnv *,jclass,jint bufferSize,jint flags,jboolean samplingEnabled,jint intervalUs)93 static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags,
94 jboolean samplingEnabled, jint intervalUs) {
95 Trace::StartDDMS(bufferSize,
96 flags,
97 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing,
98 intervalUs);
99 }
100
VMDebug_startMethodTracingFd(JNIEnv * env,jclass,jstring javaTraceFilename ATTRIBUTE_UNUSED,jint javaFd,jint bufferSize,jint flags,jboolean samplingEnabled,jint intervalUs,jboolean streamingOutput)101 static void VMDebug_startMethodTracingFd(JNIEnv* env,
102 jclass,
103 jstring javaTraceFilename ATTRIBUTE_UNUSED,
104 jint javaFd,
105 jint bufferSize,
106 jint flags,
107 jboolean samplingEnabled,
108 jint intervalUs,
109 jboolean streamingOutput) {
110 int originalFd = javaFd;
111 if (originalFd < 0) {
112 ScopedObjectAccess soa(env);
113 soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
114 "Trace fd is invalid: %d",
115 originalFd);
116 return;
117 }
118
119 int fd = DupCloexec(originalFd);
120 if (fd < 0) {
121 ScopedObjectAccess soa(env);
122 soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
123 "dup(%d) failed: %s",
124 originalFd,
125 strerror(errno));
126 return;
127 }
128
129 // Ignore the traceFilename.
130 Trace::TraceOutputMode outputMode = streamingOutput
131 ? Trace::TraceOutputMode::kStreaming
132 : Trace::TraceOutputMode::kFile;
133 Trace::Start(fd,
134 bufferSize,
135 flags,
136 outputMode,
137 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing,
138 intervalUs);
139 }
140
VMDebug_startMethodTracingFilename(JNIEnv * env,jclass,jstring javaTraceFilename,jint bufferSize,jint flags,jboolean samplingEnabled,jint intervalUs)141 static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename,
142 jint bufferSize, jint flags,
143 jboolean samplingEnabled, jint intervalUs) {
144 ScopedUtfChars traceFilename(env, javaTraceFilename);
145 if (traceFilename.c_str() == nullptr) {
146 return;
147 }
148 Trace::Start(traceFilename.c_str(),
149 bufferSize,
150 flags,
151 Trace::TraceOutputMode::kFile,
152 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing,
153 intervalUs);
154 }
155
VMDebug_getMethodTracingMode(JNIEnv *,jclass)156 static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) {
157 return Trace::GetMethodTracingMode();
158 }
159
VMDebug_stopMethodTracing(JNIEnv *,jclass)160 static void VMDebug_stopMethodTracing(JNIEnv*, jclass) {
161 Trace::Stop();
162 }
163
VMDebug_startEmulatorTracing(JNIEnv *,jclass)164 static void VMDebug_startEmulatorTracing(JNIEnv*, jclass) {
165 UNIMPLEMENTED(WARNING);
166 // dvmEmulatorTraceStart();
167 }
168
VMDebug_stopEmulatorTracing(JNIEnv *,jclass)169 static void VMDebug_stopEmulatorTracing(JNIEnv*, jclass) {
170 UNIMPLEMENTED(WARNING);
171 // dvmEmulatorTraceStop();
172 }
173
VMDebug_isDebuggerConnected(JNIEnv *,jclass)174 static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) {
175 // This function will be replaced by the debugger when it's connected. See
176 // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected.
177 return false;
178 }
179
VMDebug_isDebuggingEnabled(JNIEnv * env,jclass)180 static jboolean VMDebug_isDebuggingEnabled(JNIEnv* env, jclass) {
181 ScopedObjectAccess soa(env);
182 return Runtime::Current()->GetRuntimeCallbacks()->IsDebuggerConfigured();
183 }
184
VMDebug_lastDebuggerActivity(JNIEnv *,jclass)185 static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) {
186 // This function will be replaced by the debugger when it's connected. See
187 // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected.
188 return -1;
189 }
190
ThrowUnsupportedOperationException(JNIEnv * env)191 static void ThrowUnsupportedOperationException(JNIEnv* env) {
192 ScopedObjectAccess soa(env);
193 soa.Self()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", nullptr);
194 }
195
VMDebug_startInstructionCounting(JNIEnv * env,jclass)196 static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) {
197 ThrowUnsupportedOperationException(env);
198 }
199
VMDebug_stopInstructionCounting(JNIEnv * env,jclass)200 static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) {
201 ThrowUnsupportedOperationException(env);
202 }
203
VMDebug_getInstructionCount(JNIEnv * env,jclass,jintArray)204 static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) {
205 ThrowUnsupportedOperationException(env);
206 }
207
VMDebug_resetInstructionCount(JNIEnv * env,jclass)208 static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) {
209 ThrowUnsupportedOperationException(env);
210 }
211
VMDebug_printLoadedClasses(JNIEnv * env,jclass,jint flags)212 static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) {
213 class DumpClassVisitor : public ClassVisitor {
214 public:
215 explicit DumpClassVisitor(int dump_flags) : flags_(dump_flags) {}
216
217 bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) {
218 klass->DumpClass(LOG_STREAM(ERROR), flags_);
219 return true;
220 }
221
222 private:
223 const int flags_;
224 };
225 DumpClassVisitor visitor(flags);
226
227 ScopedFastNativeObjectAccess soa(env);
228 return Runtime::Current()->GetClassLinker()->VisitClasses(&visitor);
229 }
230
VMDebug_getLoadedClassCount(JNIEnv * env,jclass)231 static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) {
232 ScopedFastNativeObjectAccess soa(env);
233 return Runtime::Current()->GetClassLinker()->NumLoadedClasses();
234 }
235
236 /*
237 * Returns the thread-specific CPU-time clock value for the current thread,
238 * or -1 if the feature isn't supported.
239 */
VMDebug_threadCpuTimeNanos(JNIEnv *,jclass)240 static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) {
241 return ThreadCpuNanoTime();
242 }
243
244 /*
245 * static void dumpHprofData(String fileName, FileDescriptor fd)
246 *
247 * Cause "hprof" data to be dumped. We can throw an IOException if an
248 * error occurs during file handling.
249 */
VMDebug_dumpHprofData(JNIEnv * env,jclass,jstring javaFilename,jint javaFd)250 static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jint javaFd) {
251 // Only one of these may be null.
252 if (javaFilename == nullptr && javaFd < 0) {
253 ScopedObjectAccess soa(env);
254 ThrowNullPointerException("fileName == null && fd == null");
255 return;
256 }
257
258 std::string filename;
259 if (javaFilename != nullptr) {
260 ScopedUtfChars chars(env, javaFilename);
261 if (env->ExceptionCheck()) {
262 return;
263 }
264 filename = chars.c_str();
265 } else {
266 filename = "[fd]";
267 }
268
269 int fd = javaFd;
270
271 hprof::DumpHeap(filename.c_str(), fd, false);
272 }
273
VMDebug_dumpHprofDataDdms(JNIEnv *,jclass)274 static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) {
275 hprof::DumpHeap("[DDMS]", -1, true);
276 }
277
VMDebug_dumpReferenceTables(JNIEnv * env,jclass)278 static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) {
279 ScopedObjectAccess soa(env);
280 LOG(INFO) << "--- reference table dump ---";
281
282 soa.Env()->DumpReferenceTables(LOG_STREAM(INFO));
283 soa.Vm()->DumpReferenceTables(LOG_STREAM(INFO));
284
285 LOG(INFO) << "---";
286 }
287
VMDebug_crash(JNIEnv *,jclass)288 static void VMDebug_crash(JNIEnv*, jclass) {
289 LOG(FATAL) << "Crashing runtime on request";
290 }
291
VMDebug_infopoint(JNIEnv *,jclass,jint id)292 static void VMDebug_infopoint(JNIEnv*, jclass, jint id) {
293 LOG(INFO) << "VMDebug infopoint " << id << " hit";
294 }
295
VMDebug_countInstancesOfClass(JNIEnv * env,jclass,jclass javaClass,jboolean countAssignable)296 static jlong VMDebug_countInstancesOfClass(JNIEnv* env,
297 jclass,
298 jclass javaClass,
299 jboolean countAssignable) {
300 ScopedObjectAccess soa(env);
301 gc::Heap* const heap = Runtime::Current()->GetHeap();
302 // Caller's responsibility to do GC if desired.
303 ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(javaClass);
304 if (c == nullptr) {
305 return 0;
306 }
307 VariableSizedHandleScope hs(soa.Self());
308 std::vector<Handle<mirror::Class>> classes {hs.NewHandle(c)};
309 uint64_t count = 0;
310 heap->CountInstances(classes, countAssignable, &count);
311 return count;
312 }
313
VMDebug_countInstancesOfClasses(JNIEnv * env,jclass,jobjectArray javaClasses,jboolean countAssignable)314 static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env,
315 jclass,
316 jobjectArray javaClasses,
317 jboolean countAssignable) {
318 ScopedObjectAccess soa(env);
319 gc::Heap* const heap = Runtime::Current()->GetHeap();
320 // Caller's responsibility to do GC if desired.
321 ObjPtr<mirror::ObjectArray<mirror::Class>> decoded_classes =
322 soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses);
323 if (decoded_classes == nullptr) {
324 return nullptr;
325 }
326 VariableSizedHandleScope hs(soa.Self());
327 std::vector<Handle<mirror::Class>> classes;
328 for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) {
329 classes.push_back(hs.NewHandle(decoded_classes->Get(i)));
330 }
331 std::vector<uint64_t> counts(classes.size(), 0u);
332 // Heap::CountInstances can handle null and will put 0 for these classes.
333 heap->CountInstances(classes, countAssignable, &counts[0]);
334 ObjPtr<mirror::LongArray> long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
335 if (long_counts == nullptr) {
336 soa.Self()->AssertPendingOOMException();
337 return nullptr;
338 }
339 for (size_t i = 0; i < counts.size(); ++i) {
340 long_counts->Set(i, counts[i]);
341 }
342 return soa.AddLocalReference<jlongArray>(long_counts);
343 }
344
VMDebug_getInstancesOfClasses(JNIEnv * env,jclass,jobjectArray javaClasses,jboolean includeAssignable)345 static jobjectArray VMDebug_getInstancesOfClasses(JNIEnv* env,
346 jclass,
347 jobjectArray javaClasses,
348 jboolean includeAssignable) {
349 ScopedObjectAccess soa(env);
350 StackHandleScope<2> hs(soa.Self());
351 Handle<mirror::ObjectArray<mirror::Class>> classes = hs.NewHandle(
352 soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses));
353 if (classes == nullptr) {
354 return nullptr;
355 }
356
357 jclass object_array_class = env->FindClass("[Ljava/lang/Object;");
358 if (env->ExceptionCheck() == JNI_TRUE) {
359 return nullptr;
360 }
361 CHECK(object_array_class != nullptr);
362
363 size_t num_classes = classes->GetLength();
364 jobjectArray result = env->NewObjectArray(num_classes, object_array_class, nullptr);
365 if (env->ExceptionCheck() == JNI_TRUE) {
366 return nullptr;
367 }
368
369 gc::Heap* const heap = Runtime::Current()->GetHeap();
370 MutableHandle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr));
371 for (size_t i = 0; i < num_classes; ++i) {
372 h_class.Assign(classes->Get(i));
373
374 VariableSizedHandleScope hs2(soa.Self());
375 std::vector<Handle<mirror::Object>> raw_instances;
376 heap->GetInstances(hs2, h_class, includeAssignable, /* max_count= */ 0, raw_instances);
377 jobjectArray array = env->NewObjectArray(raw_instances.size(),
378 WellKnownClasses::java_lang_Object,
379 nullptr);
380 if (env->ExceptionCheck() == JNI_TRUE) {
381 return nullptr;
382 }
383
384 for (size_t j = 0; j < raw_instances.size(); ++j) {
385 env->SetObjectArrayElement(array, j, raw_instances[j].ToJObject());
386 }
387 env->SetObjectArrayElement(result, i, array);
388 }
389 return result;
390 }
391
392 // We export the VM internal per-heap-space size/alloc/free metrics
393 // for the zygote space, alloc space (application heap), and the large
394 // object space for dumpsys meminfo. The other memory region data such
395 // as PSS, private/shared dirty/shared data are available via
396 // /proc/<pid>/smaps.
VMDebug_getHeapSpaceStats(JNIEnv * env,jclass,jlongArray data)397 static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) {
398 jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, nullptr));
399 if (arr == nullptr || env->GetArrayLength(data) < 9) {
400 return;
401 }
402
403 size_t allocSize = 0;
404 size_t allocUsed = 0;
405 size_t zygoteSize = 0;
406 size_t zygoteUsed = 0;
407 size_t largeObjectsSize = 0;
408 size_t largeObjectsUsed = 0;
409 gc::Heap* heap = Runtime::Current()->GetHeap();
410 {
411 ScopedObjectAccess soa(env);
412 for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) {
413 if (space->IsImageSpace()) {
414 // Currently don't include the image space.
415 } else if (space->IsZygoteSpace()) {
416 gc::space::ZygoteSpace* zygote_space = space->AsZygoteSpace();
417 zygoteSize += zygote_space->Size();
418 zygoteUsed += zygote_space->GetBytesAllocated();
419 } else if (space->IsMallocSpace()) {
420 // This is a malloc space.
421 gc::space::MallocSpace* malloc_space = space->AsMallocSpace();
422 allocSize += malloc_space->GetFootprint();
423 allocUsed += malloc_space->GetBytesAllocated();
424 } else if (space->IsBumpPointerSpace()) {
425 gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace();
426 allocSize += bump_pointer_space->Size();
427 allocUsed += bump_pointer_space->GetBytesAllocated();
428 }
429 }
430 for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) {
431 if (space->IsLargeObjectSpace()) {
432 largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated();
433 largeObjectsUsed += largeObjectsSize;
434 }
435 }
436 }
437 size_t allocFree = allocSize - allocUsed;
438 size_t zygoteFree = zygoteSize - zygoteUsed;
439 size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed;
440
441 int j = 0;
442 arr[j++] = allocSize;
443 arr[j++] = allocUsed;
444 arr[j++] = allocFree;
445 arr[j++] = zygoteSize;
446 arr[j++] = zygoteUsed;
447 arr[j++] = zygoteFree;
448 arr[j++] = largeObjectsSize;
449 arr[j++] = largeObjectsUsed;
450 arr[j++] = largeObjectsFree;
451 env->ReleasePrimitiveArrayCritical(data, arr, 0);
452 }
453
454 // The runtime stat names for VMDebug.getRuntimeStat().
455 enum class VMDebugRuntimeStatId {
456 kArtGcGcCount = 0,
457 kArtGcGcTime,
458 kArtGcBytesAllocated,
459 kArtGcBytesFreed,
460 kArtGcBlockingGcCount,
461 kArtGcBlockingGcTime,
462 kArtGcGcCountRateHistogram,
463 kArtGcBlockingGcCountRateHistogram,
464 kNumRuntimeStats,
465 };
466
VMDebug_getRuntimeStatInternal(JNIEnv * env,jclass,jint statId)467 static jstring VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) {
468 gc::Heap* heap = Runtime::Current()->GetHeap();
469 switch (static_cast<VMDebugRuntimeStatId>(statId)) {
470 case VMDebugRuntimeStatId::kArtGcGcCount: {
471 std::string output = std::to_string(heap->GetGcCount());
472 return env->NewStringUTF(output.c_str());
473 }
474 case VMDebugRuntimeStatId::kArtGcGcTime: {
475 std::string output = std::to_string(NsToMs(heap->GetGcTime()));
476 return env->NewStringUTF(output.c_str());
477 }
478 case VMDebugRuntimeStatId::kArtGcBytesAllocated: {
479 std::string output = std::to_string(heap->GetBytesAllocatedEver());
480 return env->NewStringUTF(output.c_str());
481 }
482 case VMDebugRuntimeStatId::kArtGcBytesFreed: {
483 std::string output = std::to_string(heap->GetBytesFreedEver());
484 return env->NewStringUTF(output.c_str());
485 }
486 case VMDebugRuntimeStatId::kArtGcBlockingGcCount: {
487 std::string output = std::to_string(heap->GetBlockingGcCount());
488 return env->NewStringUTF(output.c_str());
489 }
490 case VMDebugRuntimeStatId::kArtGcBlockingGcTime: {
491 std::string output = std::to_string(NsToMs(heap->GetBlockingGcTime()));
492 return env->NewStringUTF(output.c_str());
493 }
494 case VMDebugRuntimeStatId::kArtGcGcCountRateHistogram: {
495 std::ostringstream output;
496 heap->DumpGcCountRateHistogram(output);
497 return env->NewStringUTF(output.str().c_str());
498 }
499 case VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram: {
500 std::ostringstream output;
501 heap->DumpBlockingGcCountRateHistogram(output);
502 return env->NewStringUTF(output.str().c_str());
503 }
504 default:
505 return nullptr;
506 }
507 }
508
SetRuntimeStatValue(JNIEnv * env,jobjectArray result,VMDebugRuntimeStatId id,const std::string & value)509 static bool SetRuntimeStatValue(JNIEnv* env,
510 jobjectArray result,
511 VMDebugRuntimeStatId id,
512 const std::string& value) {
513 ScopedLocalRef<jstring> jvalue(env, env->NewStringUTF(value.c_str()));
514 if (jvalue.get() == nullptr) {
515 return false;
516 }
517 env->SetObjectArrayElement(result, static_cast<jint>(id), jvalue.get());
518 return true;
519 }
520
VMDebug_getRuntimeStatsInternal(JNIEnv * env,jclass)521 static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) {
522 jobjectArray result = env->NewObjectArray(
523 static_cast<jint>(VMDebugRuntimeStatId::kNumRuntimeStats),
524 WellKnownClasses::java_lang_String,
525 nullptr);
526 if (result == nullptr) {
527 return nullptr;
528 }
529 gc::Heap* heap = Runtime::Current()->GetHeap();
530 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCount,
531 std::to_string(heap->GetGcCount()))) {
532 return nullptr;
533 }
534 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcTime,
535 std::to_string(NsToMs(heap->GetGcTime())))) {
536 return nullptr;
537 }
538 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesAllocated,
539 std::to_string(heap->GetBytesAllocatedEver()))) {
540 return nullptr;
541 }
542 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesFreed,
543 std::to_string(heap->GetBytesFreedEver()))) {
544 return nullptr;
545 }
546 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCount,
547 std::to_string(heap->GetBlockingGcCount()))) {
548 return nullptr;
549 }
550 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcTime,
551 std::to_string(NsToMs(heap->GetBlockingGcTime())))) {
552 return nullptr;
553 }
554 {
555 std::ostringstream output;
556 heap->DumpGcCountRateHistogram(output);
557 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram,
558 output.str())) {
559 return nullptr;
560 }
561 }
562 {
563 std::ostringstream output;
564 heap->DumpBlockingGcCountRateHistogram(output);
565 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram,
566 output.str())) {
567 return nullptr;
568 }
569 }
570 return result;
571 }
572
VMDebug_nativeAttachAgent(JNIEnv * env,jclass,jstring agent,jobject classloader)573 static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobject classloader) {
574 if (agent == nullptr) {
575 ScopedObjectAccess soa(env);
576 ThrowNullPointerException("agent is null");
577 return;
578 }
579
580 if (!Dbg::IsJdwpAllowed()) {
581 ScopedObjectAccess soa(env);
582 ThrowSecurityException("Can't attach agent, process is not debuggable.");
583 return;
584 }
585
586 std::string filename;
587 {
588 ScopedUtfChars chars(env, agent);
589 if (env->ExceptionCheck()) {
590 return;
591 }
592 filename = chars.c_str();
593 }
594
595 Runtime::Current()->AttachAgent(env, filename, classloader);
596 }
597
VMDebug_allowHiddenApiReflectionFrom(JNIEnv * env,jclass,jclass j_caller)598 static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) {
599 Runtime* runtime = Runtime::Current();
600 ScopedObjectAccess soa(env);
601
602 if (!runtime->IsJavaDebuggable()) {
603 ThrowSecurityException("Can't exempt class, process is not debuggable.");
604 return;
605 }
606
607 StackHandleScope<1> hs(soa.Self());
608 Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller)));
609 if (h_caller.IsNull()) {
610 ThrowNullPointerException("argument is null");
611 return;
612 }
613
614 h_caller->SetSkipHiddenApiChecks();
615 }
616
VMDebug_setAllocTrackerStackDepth(JNIEnv * env,jclass,jint stack_depth)617 static void VMDebug_setAllocTrackerStackDepth(JNIEnv* env, jclass, jint stack_depth) {
618 Runtime* runtime = Runtime::Current();
619 if (stack_depth < 0 ||
620 static_cast<size_t>(stack_depth) > gc::AllocRecordObjectMap::kMaxSupportedStackDepth) {
621 ScopedObjectAccess soa(env);
622 soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
623 "Stack depth is invalid: %d",
624 stack_depth);
625 } else {
626 runtime->GetHeap()->SetAllocTrackerStackDepth(static_cast<size_t>(stack_depth));
627 }
628 }
629
630 static JNINativeMethod gMethods[] = {
631 NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
632 NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
633 NATIVE_METHOD(VMDebug, crash, "()V"),
634 NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),
635 NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
636 NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
637 NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
638 NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"),
639 NATIVE_METHOD(VMDebug, getInstancesOfClasses, "([Ljava/lang/Class;Z)[[Ljava/lang/Object;"),
640 NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"),
641 FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
642 NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
643 NATIVE_METHOD(VMDebug, infopoint, "(I)V"),
644 FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
645 FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
646 NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"),
647 FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
648 FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
649 NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
650 NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"),
651 NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
652 NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"),
653 NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"),
654 NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
655 NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"),
656 NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"),
657 NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
658 NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"),
659 NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"),
660 NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
661 FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
662 NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
663 NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
664 NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
665 NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
666 NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"),
667 };
668
register_dalvik_system_VMDebug(JNIEnv * env)669 void register_dalvik_system_VMDebug(JNIEnv* env) {
670 REGISTER_NATIVE_METHODS("dalvik/system/VMDebug");
671 }
672
673 } // namespace art
674