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