/*
* 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 },
};