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