/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "dalvik_system_VMDebug.h" #include #include #include #include "base/file_utils.h" #include "base/histogram-inl.h" #include "base/time_utils.h" #include "class_linker.h" #include "class_root-inl.h" #include "common_throws.h" #include "debugger.h" #include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "gc/space/bump_pointer_space.h" #include "gc/space/dlmalloc_space.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" #include "gc/space/zygote_space.h" #include "handle_scope-inl.h" #include "hprof/hprof.h" #include "jni/java_vm_ext.h" #include "jni/jni_internal.h" #include "mirror/array-alloc-inl.h" #include "mirror/array-inl.h" #include "mirror/class.h" #include "mirror/executable-inl.h" #include "mirror/object_array-alloc-inl.h" #include "native_util.h" #include "nativehelper/jni_macros.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_utf_chars.h" #include "nativehelper/utils.h" #include "oat/oat_quick_method_header.h" #include "scoped_fast_native_object_access-inl.h" #include "string_array_utils.h" #include "thread-inl.h" #include "trace.h" #include "trace_profile.h" namespace art HIDDEN { static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) { ScopedObjectAccess soa(Thread::ForEnv(env)); return soa.AddLocalReference( CreateStringArray(soa.Self(), { "method-trace-profiling", "method-trace-profiling-streaming", "method-sample-profiling", "hprof-heap-dump", "hprof-heap-dump-streaming", "app_info", })); } static void VMDebug_startAllocCounting(JNIEnv*, jclass) { Runtime::Current()->SetStatsEnabled(true); } static void VMDebug_stopAllocCounting(JNIEnv*, jclass) { Runtime::Current()->SetStatsEnabled(false); } static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) { return static_cast(Runtime::Current()->GetStat(kind)); } static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { Runtime::Current()->ResetStats(kinds); } static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags, jboolean samplingEnabled, jint intervalUs) { Trace::StartDDMS(bufferSize, flags, samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, intervalUs); } static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, [[maybe_unused]] jstring javaTraceFilename, jint javaFd, jint bufferSize, jint flags, jboolean samplingEnabled, jint intervalUs, jboolean streamingOutput) { int originalFd = javaFd; if (originalFd < 0) { ScopedObjectAccess soa(env); soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Trace fd is invalid: %d", originalFd); return; } int fd = DupCloexec(originalFd); if (fd < 0) { ScopedObjectAccess soa(env); soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "dup(%d) failed: %s", originalFd, strerror(errno)); return; } // Ignore the traceFilename. TraceOutputMode outputMode = streamingOutput ? TraceOutputMode::kStreaming : TraceOutputMode::kFile; Trace::Start(fd, bufferSize, flags, outputMode, samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, intervalUs); } static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, jint bufferSize, jint flags, jboolean samplingEnabled, jint intervalUs) { ScopedUtfChars traceFilename(env, javaTraceFilename); if (traceFilename.c_str() == nullptr) { return; } Trace::Start(traceFilename.c_str(), bufferSize, flags, TraceOutputMode::kFile, samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, intervalUs); } static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { return Trace::GetMethodTracingMode(); } static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { Trace::Stop(); } static void VMDebug_stopLowOverheadTraceImpl(JNIEnv*, jclass) { TraceProfiler::Stop(); } static void VMDebug_dumpLowOverheadTraceImpl(JNIEnv* env, jclass, jstring javaProfileFileName) { ScopedUtfChars profileFileName(env, javaProfileFileName); if (profileFileName.c_str() == nullptr) { LOG(ERROR) << "Filename not provided, ignoring the request to dump low-overhead trace"; return; } TraceProfiler::Dump(profileFileName.c_str()); } static void VMDebug_dumpLowOverheadTraceFdImpl(JNIEnv*, jclass, jint originalFd) { if (originalFd < 0) { LOG(ERROR) << "Invalid file descriptor, ignoring the request to dump low-overhead trace"; return; } // Set the O_CLOEXEC flag atomically here, so the file gets closed when a new process is forked. int fd = DupCloexec(originalFd); if (fd < 0) { LOG(ERROR) << "Unable to dup the file descriptor, ignoring the request to dump low-overhead trace"; return; } TraceProfiler::Dump(fd); } static void VMDebug_startLowOverheadTraceImpl(JNIEnv*, jclass) { TraceProfiler::Start(); } static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { // This function will be replaced by the debugger when it's connected. See // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected. return false; } static jboolean VMDebug_isDebuggingEnabled(JNIEnv* env, jclass) { ScopedObjectAccess soa(env); return Runtime::Current()->GetRuntimeCallbacks()->IsDebuggerConfigured(); } static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { // This function will be replaced by the debugger when it's connected. See // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected. return -1; } static void VMDebug_suspendAllAndSendVmStart(JNIEnv*, jclass) REQUIRES_SHARED(Locks::mutator_lock_) { // This function will be replaced by the debugger when it's connected. See // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected. ThrowRuntimeException("ART's suspendAllAndSendVmStart is not implemented"); } static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) { class DumpClassVisitor : public ClassVisitor { public: explicit DumpClassVisitor(int dump_flags) : flags_(dump_flags) {} bool operator()(ObjPtr klass) override REQUIRES_SHARED(Locks::mutator_lock_) { klass->DumpClass(LOG_STREAM(ERROR), flags_); return true; } private: const int flags_; }; DumpClassVisitor visitor(flags); ScopedFastNativeObjectAccess soa(env); return Runtime::Current()->GetClassLinker()->VisitClasses(&visitor); } static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) { ScopedFastNativeObjectAccess soa(env); return Runtime::Current()->GetClassLinker()->NumLoadedClasses(); } /* * Returns the thread-specific CPU-time clock value for the current thread, * or -1 if the feature isn't supported. */ static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) { return ThreadCpuNanoTime(); } /* * static void dumpHprofData(String fileName, FileDescriptor fd) * * Cause "hprof" data to be dumped. We can throw an IOException if an * error occurs during file handling. */ static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jint javaFd) { // Only one of these may be null. if (javaFilename == nullptr && javaFd < 0) { ScopedObjectAccess soa(env); ThrowNullPointerException("fileName == null && fd == null"); return; } std::string filename; if (javaFilename != nullptr) { ScopedUtfChars chars(env, javaFilename); if (env->ExceptionCheck()) { return; } filename = chars.c_str(); } else { filename = "[fd]"; } int fd = javaFd; hprof::DumpHeap(filename.c_str(), fd, false); } static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) { hprof::DumpHeap("[DDMS]", -1, true); } static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) { ScopedObjectAccess soa(env); LOG(INFO) << "--- reference table dump ---"; soa.Env()->DumpReferenceTables(LOG_STREAM(INFO)); soa.Vm()->DumpReferenceTables(LOG_STREAM(INFO)); LOG(INFO) << "---"; } static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass, jboolean countAssignable) { ScopedObjectAccess soa(env); gc::Heap* const heap = Runtime::Current()->GetHeap(); // Caller's responsibility to do GC if desired. ObjPtr c = soa.Decode(javaClass); if (c == nullptr) { return 0; } VariableSizedHandleScope hs(soa.Self()); std::vector> classes {hs.NewHandle(c)}; uint64_t count = 0; heap->CountInstances(classes, countAssignable, &count); return count; } static jobject VMDebug_getExecutableMethodFileOffsetsNative(JNIEnv* env, jclass, jobject javaMethod) { ScopedObjectAccess soa(env); ObjPtr m = soa.Decode(javaMethod); if (m == nullptr) { soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Could not find mirror::Executable for supplied jobject"); return nullptr; } ObjPtr c = m->GetDeclaringClass(); if (c == nullptr) { soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Could not find mirror::Class for supplied jobject"); return nullptr; } ArtMethod* art_method = m->GetArtMethod(); auto oat_method_quick_code = reinterpret_cast(art_method->GetOatMethodQuickCode(kRuntimePointerSize)); if (oat_method_quick_code == nullptr) { LOG(ERROR) << "No OatMethodQuickCode for method " << art_method->PrettyMethod(); return nullptr; } const OatDexFile* oat_dex_file = c->GetDexFile().GetOatDexFile(); if (oat_dex_file == nullptr) { soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Could not find oat_dex_file"); return nullptr; } const OatFile* oat_file = oat_dex_file->GetOatFile(); if (oat_file == nullptr) { soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Could not find oat_file"); return nullptr; } std::string error_msg; const uint8_t* elf_begin = oat_file->ComputeElfBegin(&error_msg); if (elf_begin == nullptr) { soa.Self()->ThrowNewExceptionF( "Ljava/lang/RuntimeException;", "Could not find elf_begin: %s", error_msg.c_str()); return nullptr; } size_t adjusted_offset = oat_method_quick_code - elf_begin; ScopedLocalRef odex_path = CREATE_UTF_OR_RETURN(env, oat_file->GetLocation()); auto odex_offset = reinterpret_cast64(elf_begin); auto method_offset = static_cast(adjusted_offset); ScopedLocalRef clazz(env, env->FindClass("dalvik/system/VMDebug$ExecutableMethodFileOffsets")); if (clazz == nullptr) { soa.Self()->ThrowNewExceptionF( "Ljava/lang/RuntimeException;", "Could not find dalvik/system/VMDebug$ExecutableMethodFileOffsets"); return nullptr; } jmethodID constructor_id = env->GetMethodID(clazz.get(), "", "(Ljava/lang/String;JJ)V"); return env->NewObject(clazz.get(), constructor_id, odex_path.get(), odex_offset, method_offset); } static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses, jboolean countAssignable) { ScopedObjectAccess soa(env); gc::Heap* const heap = Runtime::Current()->GetHeap(); // Caller's responsibility to do GC if desired. ObjPtr> decoded_classes = soa.Decode>(javaClasses); if (decoded_classes == nullptr) { return nullptr; } VariableSizedHandleScope hs(soa.Self()); std::vector> classes; for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) { classes.push_back(hs.NewHandle(decoded_classes->Get(i))); } std::vector counts(classes.size(), 0u); // Heap::CountInstances can handle null and will put 0 for these classes. heap->CountInstances(classes, countAssignable, &counts[0]); ObjPtr long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size()); if (long_counts == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; } for (size_t i = 0; i < counts.size(); ++i) { long_counts->Set(i, counts[i]); } return soa.AddLocalReference(long_counts); } // The runtime stat names for VMDebug.getRuntimeStat(). enum class VMDebugRuntimeStatId { kArtGcGcCount = 0, kArtGcGcTime, kArtGcBytesAllocated, kArtGcBytesFreed, kArtGcBlockingGcCount, kArtGcBlockingGcTime, kArtGcGcCountRateHistogram, kArtGcBlockingGcCountRateHistogram, kArtGcObjectsAllocated, kArtGcTotalTimeWaitingForGc, kArtGcPreOomeGcCount, kNumRuntimeStats, }; static jstring VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) { gc::Heap* heap = Runtime::Current()->GetHeap(); switch (static_cast(statId)) { case VMDebugRuntimeStatId::kArtGcGcCount: { std::string output = std::to_string(heap->GetGcCount()); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcGcTime: { std::string output = std::to_string(NsToMs(heap->GetGcTime())); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcBytesAllocated: { std::string output = std::to_string(heap->GetBytesAllocatedEver()); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcBytesFreed: { std::string output = std::to_string(heap->GetBytesFreedEver()); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcBlockingGcCount: { std::string output = std::to_string(heap->GetBlockingGcCount()); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcBlockingGcTime: { std::string output = std::to_string(NsToMs(heap->GetBlockingGcTime())); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcGcCountRateHistogram: { std::ostringstream output; heap->DumpGcCountRateHistogram(output); return env->NewStringUTF(output.str().c_str()); } case VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram: { std::ostringstream output; heap->DumpBlockingGcCountRateHistogram(output); return env->NewStringUTF(output.str().c_str()); } case VMDebugRuntimeStatId::kArtGcObjectsAllocated: { std::string output = std::to_string(heap->GetObjectsAllocated()); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcTotalTimeWaitingForGc: { std::string output = std::to_string(heap->GetTotalTimeWaitingForGC()); return env->NewStringUTF(output.c_str()); } case VMDebugRuntimeStatId::kArtGcPreOomeGcCount: { std::string output = std::to_string(heap->GetPreOomeGcCount()); return env->NewStringUTF(output.c_str()); } default: return nullptr; } } static bool SetRuntimeStatValue(Thread* self, Handle> array, VMDebugRuntimeStatId id, const std::string& value) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr ovalue = mirror::String::AllocFromModifiedUtf8(self, value.c_str()); if (ovalue == nullptr) { DCHECK(self->IsExceptionPending()); return false; } // We're initializing a newly allocated array object, so we do not need to record that under // a transaction. If the transaction is aborted, the whole object shall be unreachable. array->SetWithoutChecks( static_cast(id), ovalue); return true; } static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) { Thread* self = Thread::ForEnv(env); ScopedObjectAccess soa(self); StackHandleScope<1u> hs(self); int32_t size = enum_cast(VMDebugRuntimeStatId::kNumRuntimeStats); Handle> array = hs.NewHandle( mirror::ObjectArray::Alloc( self, GetClassRoot>(), size)); if (array == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; } gc::Heap* heap = Runtime::Current()->GetHeap(); if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcGcCount, std::to_string(heap->GetGcCount()))) { return nullptr; } if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcGcTime, std::to_string(NsToMs(heap->GetGcTime())))) { return nullptr; } if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcBytesAllocated, std::to_string(heap->GetBytesAllocatedEver()))) { return nullptr; } if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcBytesFreed, std::to_string(heap->GetBytesFreedEver()))) { return nullptr; } if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcBlockingGcCount, std::to_string(heap->GetBlockingGcCount()))) { return nullptr; } if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcBlockingGcTime, std::to_string(NsToMs(heap->GetBlockingGcTime())))) { return nullptr; } { std::ostringstream output; heap->DumpGcCountRateHistogram(output); if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram, output.str())) { return nullptr; } } { std::ostringstream output; heap->DumpBlockingGcCountRateHistogram(output); if (!SetRuntimeStatValue(self, array, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram, output.str())) { return nullptr; } } return soa.AddLocalReference(array.Get()); } static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobject classloader) { if (agent == nullptr) { ScopedObjectAccess soa(env); ThrowNullPointerException("agent is null"); return; } if (!Dbg::IsJdwpAllowed()) { ScopedObjectAccess soa(env); ThrowSecurityException("Can't attach agent, process is not debuggable."); return; } std::string filename; { ScopedUtfChars chars(env, agent); if (env->ExceptionCheck()) { return; } filename = chars.c_str(); } Runtime::Current()->AttachAgent(env, filename, classloader); } static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) { Runtime* runtime = Runtime::Current(); ScopedObjectAccess soa(env); if (!runtime->IsJavaDebuggableAtInit()) { ThrowSecurityException("Can't exempt class, process is not debuggable."); return; } StackHandleScope<1> hs(soa.Self()); Handle h_caller(hs.NewHandle(soa.Decode(j_caller))); if (h_caller.IsNull()) { ThrowNullPointerException("argument is null"); return; } h_caller->SetSkipHiddenApiChecks(); } static void VMDebug_setAllocTrackerStackDepth(JNIEnv* env, jclass, jint stack_depth) { Runtime* runtime = Runtime::Current(); if (stack_depth < 0 || static_cast(stack_depth) > gc::AllocRecordObjectMap::kMaxSupportedStackDepth) { ScopedObjectAccess soa(env); soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Stack depth is invalid: %d", stack_depth); } else { runtime->GetHeap()->SetAllocTrackerStackDepth(static_cast(stack_depth)); } } static void VMDebug_setCurrentProcessName(JNIEnv* env, jclass, jstring process_name) { ScopedObjectAccess soa(env); // Android application ID naming convention states: // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')" // This is fine to convert to std::string const char* c_process_name = env->GetStringUTFChars(process_name, NULL); Runtime::Current()->GetRuntimeCallbacks()->SetCurrentProcessName(std::string(c_process_name)); env->ReleaseStringUTFChars(process_name, c_process_name); } static void VMDebug_addApplication(JNIEnv* env, jclass, jstring package_name) { ScopedObjectAccess soa(env); // Android application ID naming convention states: // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')" // This is fine to convert to std::string const char* c_package_name = env->GetStringUTFChars(package_name, NULL); Runtime::Current()->GetRuntimeCallbacks()->AddApplication(std::string(c_package_name)); env->ReleaseStringUTFChars(package_name, c_package_name); } static void VMDebug_removeApplication(JNIEnv* env, jclass, jstring package_name) { ScopedObjectAccess soa(env); // Android application ID naming convention states: // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')" // This is fine to convert to std::string const char* c_package_name = env->GetStringUTFChars(package_name, NULL); Runtime::Current()->GetRuntimeCallbacks()->RemoveApplication(std::string(c_package_name)); env->ReleaseStringUTFChars(package_name, c_package_name); } static void VMDebug_setWaitingForDebugger(JNIEnv* env, jclass, jboolean waiting) { ScopedObjectAccess soa(env); Runtime::Current()->GetRuntimeCallbacks()->SetWaitingForDebugger(waiting); } static void VMDebug_setUserId(JNIEnv* env, jclass, jint user_id) { ScopedObjectAccess soa(env); Runtime::Current()->GetRuntimeCallbacks()->SetUserId(user_id); } static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"), NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"), NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"), NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"), FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"), NATIVE_METHOD(VMDebug, suspendAllAndSendVmStart, "()V"), NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"), FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"), FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"), NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), NATIVE_METHOD(VMDebug, startAllocCounting, "()V"), NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"), NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"), NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"), NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"), NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"), FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"), NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"), NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"), NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"), NATIVE_METHOD(VMDebug, setCurrentProcessName, "(Ljava/lang/String;)V"), NATIVE_METHOD(VMDebug, setWaitingForDebugger, "(Z)V"), NATIVE_METHOD(VMDebug, addApplication, "(Ljava/lang/String;)V"), NATIVE_METHOD(VMDebug, removeApplication, "(Ljava/lang/String;)V"), NATIVE_METHOD(VMDebug, setUserId, "(I)V"), NATIVE_METHOD(VMDebug, startLowOverheadTraceImpl, "()V"), NATIVE_METHOD(VMDebug, stopLowOverheadTraceImpl, "()V"), NATIVE_METHOD(VMDebug, dumpLowOverheadTraceImpl, "(Ljava/lang/String;)V"), NATIVE_METHOD(VMDebug, dumpLowOverheadTraceFdImpl, "(I)V"), NATIVE_METHOD( VMDebug, getExecutableMethodFileOffsetsNative, "(Ljava/lang/reflect/Method;)Ldalvik/system/VMDebug$ExecutableMethodFileOffsets;"), }; void register_dalvik_system_VMDebug(JNIEnv* env) { REGISTER_NATIVE_METHODS("dalvik/system/VMDebug"); } } // namespace art