/* * 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" #ifdef WITH_PROFILER /* 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, #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, #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 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 } #endif /* * 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); #ifdef WITH_PROFILER clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS); clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS); dvmStartAllocCounting(); #endif RETURN_VOID(); } /* * public static void stopAllocCounting() */ static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); #ifdef WITH_PROFILER dvmStopAllocCounting(); #endif RETURN_VOID(); } /* * private static int getAllocCount(int kind) */ static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args, JValue* pResult) { #ifdef WITH_PROFILER 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; #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; } #else RETURN_INT(-1); #endif } /* * public static void resetAllocCount(int kinds) */ static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args, JValue* pResult) { #ifdef WITH_PROFILER unsigned int kinds = args[0]; clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff); clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16); #endif RETURN_VOID(); } /* * static void startMethodTracing(String traceFileName, java.io.FileDescriptor, * int bufferSize, int flags) * * Start method trace profiling. */ static void Dalvik_dalvik_system_VMDebug_startMethodTracing(const u4* args, JValue* pResult) { #ifdef WITH_PROFILER StringObject* traceFileStr = (StringObject*) args[0]; DataObject* traceFd = (DataObject*) args[1]; int bufferSize = args[2]; int flags = args[3]; char* traceFileName; if (bufferSize == 0) { // Default to 8MB per the documentation. bufferSize = 8 * 1024 * 1024; } if (traceFileStr == NULL || bufferSize < 1024) { dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL); RETURN_VOID(); } traceFileName = dvmCreateCstrFromString(traceFileStr); int fd = -1; if (traceFd != NULL) { InstField* field = dvmFindInstanceField(traceFd->obj.clazz, "descriptor", "I"); if (field == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldException;", "No FileDescriptor.descriptor field"); RETURN_VOID(); } fd = dup(dvmGetFieldInt(&traceFd->obj, field->byteOffset)); } dvmMethodTraceStart(traceFileName, fd, bufferSize, flags); free(traceFileName); #else // throw exception? #endif 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); #ifdef WITH_PROFILER RETURN_BOOLEAN(dvmIsMethodTraceActive()); #else RETURN_BOOLEAN(false); #endif } /* * static void stopMethodTracing() * * Stop method tracing. */ static void Dalvik_dalvik_system_VMDebug_stopMethodTracing(const u4* args, JValue* pResult) { UNUSED_PARAMETER(args); #ifdef WITH_PROFILER dvmMethodTraceStop(); #else // throw exception? #endif 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); #ifdef WITH_PROFILER dvmEmulatorTraceStart(); #else // throw exception? #endif 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); #ifdef WITH_PROFILER dvmEmulatorTraceStop(); #else // throw exception? #endif 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) { #if defined(WITH_PROFILER) dvmStartInstructionCounting(); #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif RETURN_VOID(); } /* * static void stopInstructionCounting() */ static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args, JValue* pResult) { #if defined(WITH_PROFILER) dvmStopInstructionCounting(); #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif 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) { #if defined(WITH_PROFILER) ArrayObject* countArray = (ArrayObject*) args[0]; int* storage; storage = (int*) countArray->contents; sched_yield(); memcpy(storage, gDvm.executedInstrCounts, kNumDalvikInstructions * sizeof(int)); #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif RETURN_VOID(); } /* * static boolean resetInstructionCount() * * Reset the instruction count array. */ static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args, JValue* pResult) { #if defined(WITH_PROFILER) sched_yield(); memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int)); #else dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); #endif 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) * * 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]; char* fileName; int result; if (fileNameStr == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } fileName = dvmCreateCstrFromString(fileNameStr); if (fileName == NULL) { /* unexpected -- malloc failure? */ dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?"); RETURN_VOID(); } result = hprofDumpHeap(fileName); 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(); } const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = { { "getAllocCount", "(I)I", Dalvik_dalvik_system_VMDebug_getAllocCount }, { "resetAllocCount", "(I)V", Dalvik_dalvik_system_VMDebug_resetAllocCount }, //{ "print", "(Ljava/lang/String;)V", // Dalvik_dalvik_system_VMDebug_print }, { "startAllocCounting", "()V", Dalvik_dalvik_system_VMDebug_startAllocCounting }, { "stopAllocCounting", "()V", Dalvik_dalvik_system_VMDebug_stopAllocCounting }, { "startMethodTracing", "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V", Dalvik_dalvik_system_VMDebug_startMethodTracing }, { "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;)V", Dalvik_dalvik_system_VMDebug_dumpHprofData }, { NULL, NULL, NULL }, };