/* * 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. */ /* * dalvik.system.VMDebug */ #include "Dalvik.h" #include "native/InternalNativePriv.h" #include <string.h> #include <unistd.h> #include <errno.h> /* * Extracts the fd from a FileDescriptor object. * * If an error is encountered, or the extracted descriptor is numerically * invalid, this returns -1 with an exception raised. */ static int getFileDescriptor(Object* obj) { assert(obj != NULL); assert(strcmp(obj->clazz->descriptor, "Ljava/io/FileDescriptor;") == 0); InstField* field = dvmFindInstanceField(obj->clazz, "descriptor", "I"); if (field == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldException;", "No FileDescriptor.descriptor field"); return -1; } int fd = dvmGetFieldInt(obj, field->byteOffset); if (fd < 0) { dvmThrowExceptionFmt("Ljava/lang/RuntimeException;", "Invalid file descriptor"); return -1; } return fd; } /* * Convert an array of char* into a String[]. * * Returns NULL on failure, with an exception raised. */ static ArrayObject* convertStringArray(char** strings, size_t count) { Thread* self = dvmThreadSelf(); /* * Allocate an array to hold the String objects. */ ClassObject* stringArrayClass = dvmFindArrayClass("[Ljava/lang/String;", NULL); if (stringArrayClass == NULL) { /* shouldn't happen */ LOGE("Unable to find [Ljava/lang/String;\n"); dvmAbort(); } ArrayObject* stringArray = dvmAllocArrayByClass(stringArrayClass, count, ALLOC_DEFAULT); if (stringArray == NULL) { /* probably OOM */ LOGD("Failed allocating array of %d strings\n", count); assert(dvmCheckException(self)); return NULL; } /* * Create the individual String objects and add them to the array. */ size_t i; for (i = 0; i < count; i++) { Object *str = (Object *)dvmCreateStringFromCstr(strings[i]); if (str == NULL) { /* probably OOM; drop out now */ assert(dvmCheckException(self)); dvmReleaseTrackedAlloc((Object*)stringArray, self); return NULL; } dvmSetObjectArrayElement(stringArray, i, str); /* stored in tracked array, okay to release */ dvmReleaseTrackedAlloc(str, self); } dvmReleaseTrackedAlloc((Object*)stringArray, self); return stringArray; } /* * static String[] getVmFeatureList() * * Return a set of strings describing available VM features (this is chiefly * of interest to DDMS). Some features may be controlled by compile-time * or command-line flags. */ static void Dalvik_dalvik_system_VMDebug_getVmFeatureList(const u4* args, JValue* pResult) { static const int MAX_FEATURE_COUNT = 10; char* features[MAX_FEATURE_COUNT]; int idx = 0; /* VM responds to DDMS method profiling requests */ features[idx++] = "method-trace-profiling"; features[idx++] = "method-trace-profiling-streaming"; #ifdef WITH_HPROF /* VM responds to DDMS heap dump requests */ features[idx++] = "hprof-heap-dump"; features[idx++] = "hprof-heap-dump-streaming"; #endif assert(idx <= MAX_FEATURE_COUNT); LOGV("+++ sending up %d features\n", idx); ArrayObject* arrayObj = convertStringArray(features, idx); RETURN_PTR(arrayObj); /* will be null on OOM */ } /* These must match the values in dalvik.system.VMDebug. */ enum { KIND_ALLOCATED_OBJECTS = 1<<0, KIND_ALLOCATED_BYTES = 1<<1, KIND_FREED_OBJECTS = 1<<2, KIND_FREED_BYTES = 1<<3, KIND_GC_INVOCATIONS = 1<<4, KIND_CLASS_INIT_COUNT = 1<<5, KIND_CLASS_INIT_TIME = 1<<6, #if PROFILE_EXTERNAL_ALLOCATIONS KIND_EXT_ALLOCATED_OBJECTS = 1<<12, KIND_EXT_ALLOCATED_BYTES = 1<<13, KIND_EXT_FREED_OBJECTS = 1<<14, KIND_EXT_FREED_BYTES = 1<<15, #endif // PROFILE_EXTERNAL_ALLOCATIONS KIND_GLOBAL_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS, KIND_GLOBAL_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES, KIND_GLOBAL_FREED_OBJECTS = KIND_FREED_OBJECTS, KIND_GLOBAL_FREED_BYTES = KIND_FREED_BYTES, KIND_GLOBAL_GC_INVOCATIONS = KIND_GC_INVOCATIONS, KIND_GLOBAL_CLASS_INIT_COUNT = KIND_CLASS_INIT_COUNT, KIND_GLOBAL_CLASS_INIT_TIME = KIND_CLASS_INIT_TIME, #if PROFILE_EXTERNAL_ALLOCATIONS KIND_GLOBAL_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS, KIND_GLOBAL_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES, KIND_GLOBAL_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS, KIND_GLOBAL_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES, #endif // PROFILE_EXTERNAL_ALLOCATIONS KIND_THREAD_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS << 16, KIND_THREAD_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES << 16, KIND_THREAD_FREED_OBJECTS = KIND_FREED_OBJECTS << 16, KIND_THREAD_FREED_BYTES = KIND_FREED_BYTES << 16, #if PROFILE_EXTERNAL_ALLOCATIONS KIND_THREAD_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS << 16, KIND_THREAD_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES << 16, KIND_THREAD_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS << 16, KIND_THREAD_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES << 16, #endif // PROFILE_EXTERNAL_ALLOCATIONS KIND_THREAD_GC_INVOCATIONS = KIND_GC_INVOCATIONS << 16, // TODO: failedAllocCount, failedAllocSize }; #define KIND_ALL_COUNTS 0xffffffff /* * Zero out the specified fields. */ static void clearAllocProfStateFields(AllocProfState *allocProf, unsigned int kinds) { if (kinds & KIND_ALLOCATED_OBJECTS) { allocProf->allocCount = 0; } if (kinds & KIND_ALLOCATED_BYTES) { allocProf->allocSize = 0; } if (kinds & KIND_FREED_OBJECTS) { allocProf->freeCount = 0; } if (kinds & KIND_FREED_BYTES) { allocProf->freeSize = 0; } if (kinds & KIND_GC_INVOCATIONS) { allocProf->gcCount = 0; } if (kinds & KIND_CLASS_INIT_COUNT) { allocProf->classInitCount = 0; } if (kinds & KIND_CLASS_INIT_TIME) { allocProf->classInitTime = 0; } #if PROFILE_EXTERNAL_ALLOCATIONS if (kinds & KIND_EXT_ALLOCATED_OBJECTS) { allocProf->externalAllocCount = 0; } if (kinds & KIND_EXT_ALLOCATED_BYTES) { allocProf->externalAllocSize = 0; } if (kinds & KIND_EXT_FREED_OBJECTS) { allocProf->externalFreeCount = 0; } if (kinds & KIND_EXT_FREED_BYTES) { allocProf->externalFreeSize = 0; } #endif // PROFILE_EXTERNAL_ALLOCATIONS } /* * static void startAllocCounting() * * Reset the counters and enable counting. * * TODO: this currently only resets the per-thread counters for the current * thread. If we actually start using the per-thread counters we'll * probably want to fix this. */ static void Dalvik_dalvik_system_VMDebug_startAllocCounting(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS); clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS); dvmStartAllocCounting(); RETURN_VOID(); } /* * public static void stopAllocCounting() */ static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); dvmStopAllocCounting(); RETURN_VOID(); } /* * private static int getAllocCount(int kind) */ static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args, JValue* pResult) { AllocProfState *allocProf; unsigned int kind = args[0]; if (kind < (1<<16)) { allocProf = &gDvm.allocProf; } else { allocProf = &dvmThreadSelf()->allocProf; kind >>= 16; } switch (kind) { case KIND_ALLOCATED_OBJECTS: pResult->i = allocProf->allocCount; break; case KIND_ALLOCATED_BYTES: pResult->i = allocProf->allocSize; break; case KIND_FREED_OBJECTS: pResult->i = allocProf->freeCount; break; case KIND_FREED_BYTES: pResult->i = allocProf->freeSize; break; case KIND_GC_INVOCATIONS: pResult->i = allocProf->gcCount; break; case KIND_CLASS_INIT_COUNT: pResult->i = allocProf->classInitCount; break; case KIND_CLASS_INIT_TIME: /* convert nsec to usec, reduce to 32 bits */ pResult->i = (int) (allocProf->classInitTime / 1000); break; #if PROFILE_EXTERNAL_ALLOCATIONS case KIND_EXT_ALLOCATED_OBJECTS: pResult->i = allocProf->externalAllocCount; break; case KIND_EXT_ALLOCATED_BYTES: pResult->i = allocProf->externalAllocSize; break; case KIND_EXT_FREED_OBJECTS: pResult->i = allocProf->externalFreeCount; break; case KIND_EXT_FREED_BYTES: pResult->i = allocProf->externalFreeSize; break; #endif // PROFILE_EXTERNAL_ALLOCATIONS default: assert(false); pResult->i = -1; } } /* * public static void resetAllocCount(int kinds) */ static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args, JValue* pResult) { unsigned int kinds = args[0]; clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff); clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16); RETURN_VOID(); } /* * static void startMethodTracingNative(String traceFileName, * FileDescriptor fd, int bufferSize, int flags) * * Start method trace profiling. * * If both "traceFileName" and "fd" are null, the result will be sent * directly to DDMS. (The non-DDMS versions of the calls are expected * to enforce non-NULL filenames.) */ static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args, JValue* pResult) { StringObject* traceFileStr = (StringObject*) args[0]; Object* traceFd = (Object*) args[1]; int bufferSize = args[2]; int flags = args[3]; if (bufferSize == 0) { // Default to 8MB per the documentation. bufferSize = 8 * 1024 * 1024; } if (bufferSize < 1024) { dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL); RETURN_VOID(); } char* traceFileName = NULL; if (traceFileStr != NULL) traceFileName = dvmCreateCstrFromString(traceFileStr); int fd = -1; if (traceFd != NULL) { int origFd = getFileDescriptor(traceFd); if (origFd < 0) RETURN_VOID(); fd = dup(origFd); if (fd < 0) { dvmThrowExceptionFmt("Ljava/lang/RuntimeException;", "dup(%d) failed: %s", origFd, strerror(errno)); RETURN_VOID(); } } dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]", fd, bufferSize, flags, (traceFileName == NULL && fd == -1)); free(traceFileName); RETURN_VOID(); } /* * static boolean isMethodTracingActive() * * Determine whether method tracing is currently active. */ static void Dalvik_dalvik_system_VMDebug_isMethodTracingActive(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); RETURN_BOOLEAN(dvmIsMethodTraceActive()); } /* * static void stopMethodTracing() * * Stop method tracing. */ static void Dalvik_dalvik_system_VMDebug_stopMethodTracing(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); dvmMethodTraceStop(); RETURN_VOID(); } /* * static void startEmulatorTracing() * * Start sending method trace info to the emulator. */ static void Dalvik_dalvik_system_VMDebug_startEmulatorTracing(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); dvmEmulatorTraceStart(); RETURN_VOID(); } /* * static void stopEmulatorTracing() * * Start sending method trace info to the emulator. */ static void Dalvik_dalvik_system_VMDebug_stopEmulatorTracing(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); dvmEmulatorTraceStop(); RETURN_VOID(); } /* * static int setAllocationLimit(int limit) * * Set the current allocation limit in this thread. Return the previous * value. */ static void Dalvik_dalvik_system_VMDebug_setAllocationLimit(const u4* args, JValue* pResult) { #if defined(WITH_ALLOC_LIMITS) gDvm.checkAllocLimits = true; Thread* self = dvmThreadSelf(); int newLimit = args[0]; int oldLimit = self->allocLimit; if (newLimit < -1) { LOGE("WARNING: bad limit request (%d)\n", newLimit); newLimit = -1; } self->allocLimit = newLimit; RETURN_INT(oldLimit); #else UNUSED_PARAMETER(args); RETURN_INT(-1); #endif } /* * static int setGlobalAllocationLimit(int limit) * * Set the allocation limit for this process. Returns the previous value. */ static void Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit(const u4* args, JValue* pResult) { #if defined(WITH_ALLOC_LIMITS) gDvm.checkAllocLimits = true; int newLimit = args[0]; int oldLimit = gDvm.allocationLimit; if (newLimit < -1 || newLimit > 0) { LOGE("WARNING: bad limit request (%d)\n", newLimit); newLimit = -1; } // TODO: should use an atomic swap here gDvm.allocationLimit = newLimit; RETURN_INT(oldLimit); #else UNUSED_PARAMETER(args); RETURN_INT(-1); #endif } /* * static boolean isDebuggerConnected() * * Returns "true" if a debugger is attached. */ static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); RETURN_BOOLEAN(dvmDbgIsDebuggerConnected()); } /* * static boolean isDebuggingEnabled() * * Returns "true" if debugging is enabled. */ static void Dalvik_dalvik_system_VMDebug_isDebuggingEnabled(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); RETURN_BOOLEAN(gDvm.jdwpConfigured); } /* * static long lastDebuggerActivity() * * Returns the time, in msec, since we last had an interaction with the * debugger (send or receive). */ static void Dalvik_dalvik_system_VMDebug_lastDebuggerActivity(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); RETURN_LONG(dvmDbgLastDebuggerActivity()); } /* * static void startInstructionCounting() */ static void Dalvik_dalvik_system_VMDebug_startInstructionCounting(const u4* args, JValue* pResult) { dvmStartInstructionCounting(); RETURN_VOID(); } /* * static void stopInstructionCounting() */ static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args, JValue* pResult) { dvmStopInstructionCounting(); RETURN_VOID(); } /* * static boolean getInstructionCount(int[] counts) * * Grab a copy of the global instruction count array. * * Since the instruction counts aren't synchronized, we use sched_yield * to improve our chances of finishing without contention. (Only makes * sense on a uniprocessor.) */ static void Dalvik_dalvik_system_VMDebug_getInstructionCount(const u4* args, JValue* pResult) { ArrayObject* countArray = (ArrayObject*) args[0]; int* storage; storage = (int*) countArray->contents; sched_yield(); memcpy(storage, gDvm.executedInstrCounts, kNumDalvikInstructions * sizeof(int)); RETURN_VOID(); } /* * static boolean resetInstructionCount() * * Reset the instruction count array. */ static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args, JValue* pResult) { sched_yield(); memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int)); RETURN_VOID(); } /* * static void printLoadedClasses(int flags) * * Dump the list of loaded classes. */ static void Dalvik_dalvik_system_VMDebug_printLoadedClasses(const u4* args, JValue* pResult) { int flags = args[0]; dvmDumpAllClasses(flags); RETURN_VOID(); } /* * static int getLoadedClassCount() * * Return the number of loaded classes */ static void Dalvik_dalvik_system_VMDebug_getLoadedClassCount(const u4* args, JValue* pResult) { int count; UNUSED_PARAMETER(args); count = dvmGetNumLoadedClasses(); RETURN_INT(count); } /* * Returns the thread-specific CPU-time clock value for the current thread, * or -1 if the feature isn't supported. */ static void Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos(const u4* args, JValue* pResult) { jlong result; #ifdef HAVE_POSIX_CLOCKS struct timespec now; clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now); result = (jlong) (now.tv_sec*1000000000LL + now.tv_nsec); #else result = (jlong) -1; #endif RETURN_LONG(result); } /* * 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 Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args, JValue* pResult) { #ifdef WITH_HPROF StringObject* fileNameStr = (StringObject*) args[0]; Object* fileDescriptor = (Object*) args[1]; char* fileName; int result; /* * Only one of these may be NULL. */ if (fileNameStr == NULL && fileDescriptor == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } if (fileNameStr != NULL) { fileName = dvmCreateCstrFromString(fileNameStr); if (fileName == NULL) { /* unexpected -- malloc failure? */ dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?"); RETURN_VOID(); } } else { fileName = strdup("[fd]"); } int fd = -1; if (fileDescriptor != NULL) { fd = getFileDescriptor(fileDescriptor); if (fd < 0) RETURN_VOID(); } result = hprofDumpHeap(fileName, fd, false); free(fileName); if (result != 0) { /* ideally we'd throw something more specific based on actual failure */ dvmThrowException("Ljava/lang/RuntimeException;", "Failure during heap dump -- check log output for details"); RETURN_VOID(); } #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif RETURN_VOID(); } /* * static void dumpHprofDataDdms() * * Cause "hprof" data to be computed and sent directly to DDMS. */ static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args, JValue* pResult) { #ifdef WITH_HPROF int result; result = hprofDumpHeap("[DDMS]", -1, true); if (result != 0) { /* ideally we'd throw something more specific based on actual failure */ dvmThrowException("Ljava/lang/RuntimeException;", "Failure during heap dump -- check log output for details"); RETURN_VOID(); } #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif RETURN_VOID(); } /* * static boolean cacheRegisterMap(String classAndMethodDescr) * * If the specified class is loaded, and the named method exists, ensure * that the method's register map is ready for use. If the class/method * cannot be found, nothing happens. * * This can improve the zygote's sharing of compressed register maps. Do * this after class preloading. * * Returns true if the register map is cached and ready, either as a result * of this call or earlier activity. Returns false if the class isn't loaded, * if the method couldn't be found, or if the method has no register map. * * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.) */ static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args, JValue* pResult) { StringObject* classAndMethodDescStr = (StringObject*) args[0]; ClassObject* clazz; bool result = false; if (classAndMethodDescStr == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } char* classAndMethodDesc = NULL; /* * Pick the string apart. We have a local copy, so just modify it * in place. */ classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr); char* methodName = strchr(classAndMethodDesc, '.'); if (methodName == NULL) { dvmThrowException("Ljava/lang/RuntimeException;", "method name not found in string"); RETURN_VOID(); } *methodName++ = '\0'; char* methodDescr = strchr(methodName, ':'); if (methodDescr == NULL) { dvmThrowException("Ljava/lang/RuntimeException;", "method descriptor not found in string"); RETURN_VOID(); } *methodDescr++ = '\0'; //LOGD("GOT: %s %s %s\n", classAndMethodDesc, methodName, methodDescr); /* * Find the class, but only if it's already loaded. */ clazz = dvmLookupClass(classAndMethodDesc, NULL, false); if (clazz == NULL) { LOGD("Class %s not found in bootstrap loader\n", classAndMethodDesc); goto bail; } Method* method; /* * Find the method, which could be virtual or direct, defined directly * or inherited. */ if (methodName[0] == '<') { /* * Constructor or class initializer. Only need to examine the * "direct" list, and don't need to search up the class hierarchy. */ method = dvmFindDirectMethodByDescriptor(clazz, methodName, methodDescr); } else { /* * Try both lists, and scan up the tree. */ method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName, methodDescr); if (method == NULL) { method = dvmFindDirectMethodHierByDescriptor(clazz, methodName, methodDescr); } } if (method != NULL) { /* * Got it. See if there's a register map here. */ const RegisterMap* pMap; pMap = dvmGetExpandedRegisterMap(method); if (pMap == NULL) { LOGV("No map for %s.%s %s\n", classAndMethodDesc, methodName, methodDescr); } else { LOGV("Found map %s.%s %s\n", classAndMethodDesc, methodName, methodDescr); result = true; } } else { LOGV("Unable to find %s.%s %s\n", classAndMethodDesc, methodName, methodDescr); } bail: free(classAndMethodDesc); RETURN_BOOLEAN(result); } /* * static void dumpReferenceTables() */ static void Dalvik_dalvik_system_VMDebug_dumpReferenceTables(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); UNUSED_PARAMETER(pResult); LOGI("--- reference table dump ---\n"); dvmDumpJniReferenceTables(); // could dump thread's internalLocalRefTable, probably not useful // ditto for thread's jniMonitorRefTable LOGI("---\n"); RETURN_VOID(); } /* * static void crash() * * Dump the current thread's interpreted stack and abort the VM. Useful * for seeing both interpreted and native stack traces. * * (Might want to restrict this to debuggable processes as a security * measure, or check SecurityManager.checkExit().) */ static void Dalvik_dalvik_system_VMDebug_crash(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); UNUSED_PARAMETER(pResult); LOGW("Crashing VM on request\n"); dvmDumpThread(dvmThreadSelf(), false); dvmAbort(); } /* * static void infopoint(int id) * * Provide a hook for gdb to hang to so that the VM can be stopped when * user-tagged source locations are being executed. */ static void Dalvik_dalvik_system_VMDebug_infopoint(const u4* args, JValue* pResult) { gDvm.nativeDebuggerActive = true; LOGD("VMDebug infopoint %d hit", args[0]); gDvm.nativeDebuggerActive = false; RETURN_VOID(); } static void Dalvik_dalvik_system_VMDebug_countInstancesOfClass(const u4* args, JValue* pResult) { ClassObject* clazz = (ClassObject*)args[0]; bool countAssignable = args[1]; if (clazz == NULL) { RETURN_LONG(0); } if (countAssignable) { size_t count = dvmCountAssignableInstancesOfClass(clazz); RETURN_LONG((long long)count); } else { size_t count = dvmCountInstancesOfClass(clazz); RETURN_LONG((long long)count); } } const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = { { "getVmFeatureList", "()[Ljava/lang/String;", Dalvik_dalvik_system_VMDebug_getVmFeatureList }, { "getAllocCount", "(I)I", Dalvik_dalvik_system_VMDebug_getAllocCount }, { "resetAllocCount", "(I)V", Dalvik_dalvik_system_VMDebug_resetAllocCount }, { "startAllocCounting", "()V", Dalvik_dalvik_system_VMDebug_startAllocCounting }, { "stopAllocCounting", "()V", Dalvik_dalvik_system_VMDebug_stopAllocCounting }, { "startMethodTracingNative", "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V", Dalvik_dalvik_system_VMDebug_startMethodTracingNative }, { "isMethodTracingActive", "()Z", Dalvik_dalvik_system_VMDebug_isMethodTracingActive }, { "stopMethodTracing", "()V", Dalvik_dalvik_system_VMDebug_stopMethodTracing }, { "startEmulatorTracing", "()V", Dalvik_dalvik_system_VMDebug_startEmulatorTracing }, { "stopEmulatorTracing", "()V", Dalvik_dalvik_system_VMDebug_stopEmulatorTracing }, { "setAllocationLimit", "(I)I", Dalvik_dalvik_system_VMDebug_setAllocationLimit }, { "setGlobalAllocationLimit", "(I)I", Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit }, { "startInstructionCounting", "()V", Dalvik_dalvik_system_VMDebug_startInstructionCounting }, { "stopInstructionCounting", "()V", Dalvik_dalvik_system_VMDebug_stopInstructionCounting }, { "resetInstructionCount", "()V", Dalvik_dalvik_system_VMDebug_resetInstructionCount }, { "getInstructionCount", "([I)V", Dalvik_dalvik_system_VMDebug_getInstructionCount }, { "isDebuggerConnected", "()Z", Dalvik_dalvik_system_VMDebug_isDebuggerConnected }, { "isDebuggingEnabled", "()Z", Dalvik_dalvik_system_VMDebug_isDebuggingEnabled }, { "lastDebuggerActivity", "()J", Dalvik_dalvik_system_VMDebug_lastDebuggerActivity }, { "printLoadedClasses", "(I)V", Dalvik_dalvik_system_VMDebug_printLoadedClasses }, { "getLoadedClassCount", "()I", Dalvik_dalvik_system_VMDebug_getLoadedClassCount }, { "threadCpuTimeNanos", "()J", Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos }, { "dumpHprofData", "(Ljava/lang/String;Ljava/io/FileDescriptor;)V", Dalvik_dalvik_system_VMDebug_dumpHprofData }, { "dumpHprofDataDdms", "()V", Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms }, { "cacheRegisterMap", "(Ljava/lang/String;)Z", Dalvik_dalvik_system_VMDebug_cacheRegisterMap }, { "dumpReferenceTables", "()V", Dalvik_dalvik_system_VMDebug_dumpReferenceTables }, { "crash", "()V", Dalvik_dalvik_system_VMDebug_crash }, { "infopoint", "(I)V", Dalvik_dalvik_system_VMDebug_infopoint }, { "countInstancesOfClass", "(Ljava/lang/Class;Z)J", Dalvik_dalvik_system_VMDebug_countInstancesOfClass }, { NULL, NULL, NULL }, };