dalvik_system_VMDebug.cc revision 3b6f440dbd066f03a737da6d292074f47b3fbc29
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 "base/histogram-inl.h" 25#include "base/time_utils.h" 26#include "class_linker.h" 27#include "common_throws.h" 28#include "debugger.h" 29#include "gc/space/bump_pointer_space.h" 30#include "gc/space/dlmalloc_space.h" 31#include "gc/space/large_object_space.h" 32#include "gc/space/space-inl.h" 33#include "gc/space/zygote_space.h" 34#include "hprof/hprof.h" 35#include "jni_internal.h" 36#include "mirror/class.h" 37#include "ScopedLocalRef.h" 38#include "ScopedUtfChars.h" 39#include "scoped_fast_native_object_access-inl.h" 40#include "trace.h" 41#include "well_known_classes.h" 42 43namespace art { 44 45static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) { 46 static const char* features[] = { 47 "method-trace-profiling", 48 "method-trace-profiling-streaming", 49 "method-sample-profiling", 50 "hprof-heap-dump", 51 "hprof-heap-dump-streaming", 52 }; 53 jobjectArray result = env->NewObjectArray(arraysize(features), 54 WellKnownClasses::java_lang_String, 55 nullptr); 56 if (result != nullptr) { 57 for (size_t i = 0; i < arraysize(features); ++i) { 58 ScopedLocalRef<jstring> jfeature(env, env->NewStringUTF(features[i])); 59 if (jfeature.get() == nullptr) { 60 return nullptr; 61 } 62 env->SetObjectArrayElement(result, i, jfeature.get()); 63 } 64 } 65 return result; 66} 67 68static void VMDebug_startAllocCounting(JNIEnv*, jclass) { 69 Runtime::Current()->SetStatsEnabled(true); 70} 71 72static void VMDebug_stopAllocCounting(JNIEnv*, jclass) { 73 Runtime::Current()->SetStatsEnabled(false); 74} 75 76static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) { 77 return Runtime::Current()->GetStat(kind); 78} 79 80static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { 81 Runtime::Current()->ResetStats(kinds); 82} 83 84static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags, 85 jboolean samplingEnabled, jint intervalUs) { 86 Trace::Start("[DDMS]", -1, bufferSize, flags, Trace::TraceOutputMode::kDDMS, 87 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, 88 intervalUs); 89} 90 91static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, 92 jobject javaFd, jint bufferSize, jint flags, 93 jboolean samplingEnabled, jint intervalUs, 94 jboolean streamingOutput) { 95 int originalFd = jniGetFDFromFileDescriptor(env, javaFd); 96 if (originalFd < 0) { 97 return; 98 } 99 100 int fd = dup(originalFd); 101 if (fd < 0) { 102 ScopedObjectAccess soa(env); 103 soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", 104 "dup(%d) failed: %s", originalFd, strerror(errno)); 105 return; 106 } 107 108 ScopedUtfChars traceFilename(env, javaTraceFilename); 109 if (traceFilename.c_str() == nullptr) { 110 return; 111 } 112 Trace::TraceOutputMode outputMode = streamingOutput 113 ? Trace::TraceOutputMode::kStreaming 114 : Trace::TraceOutputMode::kFile; 115 Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, outputMode, 116 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, 117 intervalUs); 118} 119 120static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, 121 jint bufferSize, jint flags, 122 jboolean samplingEnabled, jint intervalUs) { 123 ScopedUtfChars traceFilename(env, javaTraceFilename); 124 if (traceFilename.c_str() == nullptr) { 125 return; 126 } 127 Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, Trace::TraceOutputMode::kFile, 128 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, 129 intervalUs); 130} 131 132static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { 133 return Trace::GetMethodTracingMode(); 134} 135 136static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { 137 Trace::Stop(); 138} 139 140static void VMDebug_startEmulatorTracing(JNIEnv*, jclass) { 141 UNIMPLEMENTED(WARNING); 142 // dvmEmulatorTraceStart(); 143} 144 145static void VMDebug_stopEmulatorTracing(JNIEnv*, jclass) { 146 UNIMPLEMENTED(WARNING); 147 // dvmEmulatorTraceStop(); 148} 149 150static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { 151 return Dbg::IsDebuggerActive(); 152} 153 154static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) { 155 return Dbg::IsJdwpConfigured(); 156} 157 158static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { 159 return Dbg::LastDebuggerActivity(); 160} 161 162static void ThrowUnsupportedOperationException(JNIEnv* env) { 163 ScopedObjectAccess soa(env); 164 soa.Self()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", nullptr); 165} 166 167static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) { 168 ThrowUnsupportedOperationException(env); 169} 170 171static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) { 172 ThrowUnsupportedOperationException(env); 173} 174 175static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) { 176 ThrowUnsupportedOperationException(env); 177} 178 179static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) { 180 ThrowUnsupportedOperationException(env); 181} 182 183static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) { 184 class DumpClassVisitor : public ClassVisitor { 185 public: 186 explicit DumpClassVisitor(int dump_flags) : flags_(dump_flags) {} 187 188 bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { 189 klass->DumpClass(LOG_STREAM(ERROR), flags_); 190 return true; 191 } 192 193 private: 194 const int flags_; 195 }; 196 DumpClassVisitor visitor(flags); 197 198 ScopedFastNativeObjectAccess soa(env); 199 return Runtime::Current()->GetClassLinker()->VisitClasses(&visitor); 200} 201 202static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) { 203 ScopedFastNativeObjectAccess soa(env); 204 return Runtime::Current()->GetClassLinker()->NumLoadedClasses(); 205} 206 207/* 208 * Returns the thread-specific CPU-time clock value for the current thread, 209 * or -1 if the feature isn't supported. 210 */ 211static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) { 212 return ThreadCpuNanoTime(); 213} 214 215/* 216 * static void dumpHprofData(String fileName, FileDescriptor fd) 217 * 218 * Cause "hprof" data to be dumped. We can throw an IOException if an 219 * error occurs during file handling. 220 */ 221static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jobject javaFd) { 222 // Only one of these may be null. 223 if (javaFilename == nullptr && javaFd == nullptr) { 224 ScopedObjectAccess soa(env); 225 ThrowNullPointerException("fileName == null && fd == null"); 226 return; 227 } 228 229 std::string filename; 230 if (javaFilename != nullptr) { 231 ScopedUtfChars chars(env, javaFilename); 232 if (env->ExceptionCheck()) { 233 return; 234 } 235 filename = chars.c_str(); 236 } else { 237 filename = "[fd]"; 238 } 239 240 int fd = -1; 241 if (javaFd != nullptr) { 242 fd = jniGetFDFromFileDescriptor(env, javaFd); 243 if (fd < 0) { 244 ScopedObjectAccess soa(env); 245 ThrowRuntimeException("Invalid file descriptor"); 246 return; 247 } 248 } 249 250 hprof::DumpHeap(filename.c_str(), fd, false); 251} 252 253static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) { 254 hprof::DumpHeap("[DDMS]", -1, true); 255} 256 257static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) { 258 ScopedObjectAccess soa(env); 259 LOG(INFO) << "--- reference table dump ---"; 260 261 soa.Env()->DumpReferenceTables(LOG_STREAM(INFO)); 262 soa.Vm()->DumpReferenceTables(LOG_STREAM(INFO)); 263 264 LOG(INFO) << "---"; 265} 266 267static void VMDebug_crash(JNIEnv*, jclass) { 268 LOG(FATAL) << "Crashing runtime on request"; 269} 270 271static void VMDebug_infopoint(JNIEnv*, jclass, jint id) { 272 LOG(INFO) << "VMDebug infopoint " << id << " hit"; 273} 274 275static jlong VMDebug_countInstancesOfClass(JNIEnv* env, 276 jclass, 277 jclass javaClass, 278 jboolean countAssignable) { 279 ScopedObjectAccess soa(env); 280 gc::Heap* const heap = Runtime::Current()->GetHeap(); 281 // Caller's responsibility to do GC if desired. 282 ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(javaClass); 283 if (c == nullptr) { 284 return 0; 285 } 286 VariableSizedHandleScope hs(soa.Self()); 287 std::vector<Handle<mirror::Class>> classes {hs.NewHandle(c)}; 288 uint64_t count = 0; 289 heap->CountInstances(classes, countAssignable, &count); 290 return count; 291} 292 293static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, 294 jclass, 295 jobjectArray javaClasses, 296 jboolean countAssignable) { 297 ScopedObjectAccess soa(env); 298 gc::Heap* const heap = Runtime::Current()->GetHeap(); 299 // Caller's responsibility to do GC if desired. 300 ObjPtr<mirror::ObjectArray<mirror::Class>> decoded_classes = 301 soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses); 302 if (decoded_classes == nullptr) { 303 return nullptr; 304 } 305 VariableSizedHandleScope hs(soa.Self()); 306 std::vector<Handle<mirror::Class>> classes; 307 for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) { 308 classes.push_back(hs.NewHandle(decoded_classes->Get(i))); 309 } 310 std::vector<uint64_t> counts(classes.size(), 0u); 311 // Heap::CountInstances can handle null and will put 0 for these classes. 312 heap->CountInstances(classes, countAssignable, &counts[0]); 313 ObjPtr<mirror::LongArray> long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size()); 314 if (long_counts == nullptr) { 315 soa.Self()->AssertPendingOOMException(); 316 return nullptr; 317 } 318 for (size_t i = 0; i < counts.size(); ++i) { 319 long_counts->Set(i, counts[i]); 320 } 321 return soa.AddLocalReference<jlongArray>(long_counts); 322} 323 324// We export the VM internal per-heap-space size/alloc/free metrics 325// for the zygote space, alloc space (application heap), and the large 326// object space for dumpsys meminfo. The other memory region data such 327// as PSS, private/shared dirty/shared data are available via 328// /proc/<pid>/smaps. 329static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { 330 jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, 0)); 331 if (arr == nullptr || env->GetArrayLength(data) < 9) { 332 return; 333 } 334 335 size_t allocSize = 0; 336 size_t allocUsed = 0; 337 size_t zygoteSize = 0; 338 size_t zygoteUsed = 0; 339 size_t largeObjectsSize = 0; 340 size_t largeObjectsUsed = 0; 341 gc::Heap* heap = Runtime::Current()->GetHeap(); 342 { 343 ScopedObjectAccess soa(env); 344 for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { 345 if (space->IsImageSpace()) { 346 // Currently don't include the image space. 347 } else if (space->IsZygoteSpace()) { 348 gc::space::ZygoteSpace* zygote_space = space->AsZygoteSpace(); 349 zygoteSize += zygote_space->Size(); 350 zygoteUsed += zygote_space->GetBytesAllocated(); 351 } else if (space->IsMallocSpace()) { 352 // This is a malloc space. 353 gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); 354 allocSize += malloc_space->GetFootprint(); 355 allocUsed += malloc_space->GetBytesAllocated(); 356 } else if (space->IsBumpPointerSpace()) { 357 gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); 358 allocSize += bump_pointer_space->Size(); 359 allocUsed += bump_pointer_space->GetBytesAllocated(); 360 } 361 } 362 for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) { 363 if (space->IsLargeObjectSpace()) { 364 largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated(); 365 largeObjectsUsed += largeObjectsSize; 366 } 367 } 368 } 369 size_t allocFree = allocSize - allocUsed; 370 size_t zygoteFree = zygoteSize - zygoteUsed; 371 size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed; 372 373 int j = 0; 374 arr[j++] = allocSize; 375 arr[j++] = allocUsed; 376 arr[j++] = allocFree; 377 arr[j++] = zygoteSize; 378 arr[j++] = zygoteUsed; 379 arr[j++] = zygoteFree; 380 arr[j++] = largeObjectsSize; 381 arr[j++] = largeObjectsUsed; 382 arr[j++] = largeObjectsFree; 383 env->ReleasePrimitiveArrayCritical(data, arr, 0); 384} 385 386// The runtime stat names for VMDebug.getRuntimeStat(). 387enum class VMDebugRuntimeStatId { 388 kArtGcGcCount = 0, 389 kArtGcGcTime, 390 kArtGcBytesAllocated, 391 kArtGcBytesFreed, 392 kArtGcBlockingGcCount, 393 kArtGcBlockingGcTime, 394 kArtGcGcCountRateHistogram, 395 kArtGcBlockingGcCountRateHistogram, 396 kNumRuntimeStats, 397}; 398 399static jobject VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) { 400 gc::Heap* heap = Runtime::Current()->GetHeap(); 401 switch (static_cast<VMDebugRuntimeStatId>(statId)) { 402 case VMDebugRuntimeStatId::kArtGcGcCount: { 403 std::string output = std::to_string(heap->GetGcCount()); 404 return env->NewStringUTF(output.c_str()); 405 } 406 case VMDebugRuntimeStatId::kArtGcGcTime: { 407 std::string output = std::to_string(NsToMs(heap->GetGcTime())); 408 return env->NewStringUTF(output.c_str()); 409 } 410 case VMDebugRuntimeStatId::kArtGcBytesAllocated: { 411 std::string output = std::to_string(heap->GetBytesAllocatedEver()); 412 return env->NewStringUTF(output.c_str()); 413 } 414 case VMDebugRuntimeStatId::kArtGcBytesFreed: { 415 std::string output = std::to_string(heap->GetBytesFreedEver()); 416 return env->NewStringUTF(output.c_str()); 417 } 418 case VMDebugRuntimeStatId::kArtGcBlockingGcCount: { 419 std::string output = std::to_string(heap->GetBlockingGcCount()); 420 return env->NewStringUTF(output.c_str()); 421 } 422 case VMDebugRuntimeStatId::kArtGcBlockingGcTime: { 423 std::string output = std::to_string(NsToMs(heap->GetBlockingGcTime())); 424 return env->NewStringUTF(output.c_str()); 425 } 426 case VMDebugRuntimeStatId::kArtGcGcCountRateHistogram: { 427 std::ostringstream output; 428 heap->DumpGcCountRateHistogram(output); 429 return env->NewStringUTF(output.str().c_str()); 430 } 431 case VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram: { 432 std::ostringstream output; 433 heap->DumpBlockingGcCountRateHistogram(output); 434 return env->NewStringUTF(output.str().c_str()); 435 } 436 default: 437 return nullptr; 438 } 439} 440 441static bool SetRuntimeStatValue(JNIEnv* env, 442 jobjectArray result, 443 VMDebugRuntimeStatId id, 444 const std::string& value) { 445 ScopedLocalRef<jstring> jvalue(env, env->NewStringUTF(value.c_str())); 446 if (jvalue.get() == nullptr) { 447 return false; 448 } 449 env->SetObjectArrayElement(result, static_cast<jint>(id), jvalue.get()); 450 return true; 451} 452 453static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) { 454 jobjectArray result = env->NewObjectArray( 455 static_cast<jint>(VMDebugRuntimeStatId::kNumRuntimeStats), 456 WellKnownClasses::java_lang_String, 457 nullptr); 458 if (result == nullptr) { 459 return nullptr; 460 } 461 gc::Heap* heap = Runtime::Current()->GetHeap(); 462 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCount, 463 std::to_string(heap->GetGcCount()))) { 464 return nullptr; 465 } 466 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcTime, 467 std::to_string(NsToMs(heap->GetGcTime())))) { 468 return nullptr; 469 } 470 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesAllocated, 471 std::to_string(heap->GetBytesAllocatedEver()))) { 472 return nullptr; 473 } 474 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesFreed, 475 std::to_string(heap->GetBytesFreedEver()))) { 476 return nullptr; 477 } 478 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCount, 479 std::to_string(heap->GetBlockingGcCount()))) { 480 return nullptr; 481 } 482 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcTime, 483 std::to_string(NsToMs(heap->GetBlockingGcTime())))) { 484 return nullptr; 485 } 486 { 487 std::ostringstream output; 488 heap->DumpGcCountRateHistogram(output); 489 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram, 490 output.str())) { 491 return nullptr; 492 } 493 } 494 { 495 std::ostringstream output; 496 heap->DumpBlockingGcCountRateHistogram(output); 497 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram, 498 output.str())) { 499 return nullptr; 500 } 501 } 502 return result; 503} 504 505static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) { 506 if (agent == nullptr) { 507 ScopedObjectAccess soa(env); 508 ThrowNullPointerException("agent is null"); 509 return; 510 } 511 512 if (!Dbg::IsJdwpAllowed()) { 513 ScopedObjectAccess soa(env); 514 ThrowSecurityException("Can't attach agent, process is not debuggable."); 515 return; 516 } 517 518 std::string filename; 519 { 520 ScopedUtfChars chars(env, agent); 521 if (env->ExceptionCheck()) { 522 return; 523 } 524 filename = chars.c_str(); 525 } 526 527 Runtime::Current()->AttachAgent(filename); 528} 529 530static JNINativeMethod gMethods[] = { 531 NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), 532 NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), 533 NATIVE_METHOD(VMDebug, crash, "()V"), 534 NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"), 535 NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"), 536 NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), 537 NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), 538 NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), 539 NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), 540 FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"), 541 NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), 542 NATIVE_METHOD(VMDebug, infopoint, "(I)V"), 543 FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"), 544 FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"), 545 NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"), 546 FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"), 547 FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"), 548 NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), 549 NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"), 550 NATIVE_METHOD(VMDebug, startAllocCounting, "()V"), 551 NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"), 552 NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"), 553 NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"), 554 NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V"), 555 NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"), 556 NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"), 557 NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"), 558 NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"), 559 NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"), 560 FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"), 561 NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), 562 NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), 563 NATIVE_METHOD(VMDebug, attachAgent, "(Ljava/lang/String;)V"), 564}; 565 566void register_dalvik_system_VMDebug(JNIEnv* env) { 567 REGISTER_NATIVE_METHODS("dalvik/system/VMDebug"); 568} 569 570} // namespace art 571