/*
* 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.VMStack
*/
#include "Dalvik.h"
#include "UniquePtr.h"
#include "native/InternalNativePriv.h"
/*
* public static ClassLoader getCallingClassLoader()
*
* Return the defining class loader of the caller's caller.
*/
static void Dalvik_dalvik_system_VMStack_getCallingClassLoader(const u4* args,
JValue* pResult)
{
ClassObject* clazz =
dvmGetCaller2Class(dvmThreadSelf()->interpSave.curFrame);
UNUSED_PARAMETER(args);
if (clazz == NULL)
RETURN_PTR(NULL);
RETURN_PTR(clazz->classLoader);
}
/*
* public static Class<?> getStackClass2()
*
* Returns the class of the caller's caller's caller.
*/
static void Dalvik_dalvik_system_VMStack_getStackClass2(const u4* args,
JValue* pResult)
{
ClassObject* clazz =
dvmGetCaller3Class(dvmThreadSelf()->interpSave.curFrame);
UNUSED_PARAMETER(args);
RETURN_PTR(clazz);
}
/*
* public static Class<?>[] getClasses(int maxDepth)
*
* Create an array of classes for the methods on the stack, skipping the
* first two and all reflection methods. If "stopAtPrivileged" is set,
* stop shortly after we encounter a privileged class.
*/
static void Dalvik_dalvik_system_VMStack_getClasses(const u4* args,
JValue* pResult)
{
/* note "maxSize" is unsigned, so -1 turns into a very large value */
size_t maxSize = args[0];
size_t size = 0;
const size_t kSkip = 2;
/*
* Get an array with the stack trace in it.
*/
void *fp = dvmThreadSelf()->interpSave.curFrame;
size_t depth = dvmComputeExactFrameDepth(fp);
UniquePtr<const Method*[]> methods(new const Method*[depth]);
dvmFillStackTraceArray(fp, methods.get(), depth);
/*
* Run through the array and count up how many elements there are.
*/
for (size_t i = kSkip; i < depth && size < maxSize; ++i) {
const Method* meth = methods[i];
if (dvmIsReflectionMethod(meth))
continue;
size++;
}
/*
* Create an array object to hold the classes.
* TODO: can use gDvm.classJavaLangClassArray here?
*/
ClassObject* classArrayClass = dvmFindArrayClass("[Ljava/lang/Class;",
NULL);
if (classArrayClass == NULL) {
ALOGW("Unable to find java.lang.Class array class");
return;
}
ArrayObject* classes = dvmAllocArrayByClass(classArrayClass,
size,
ALLOC_DEFAULT);
if (classes == NULL) {
ALOGW("Unable to allocate class array of %zd elements", size);
return;
}
/*
* Fill in the array.
*/
size_t objCount = 0;
for (size_t i = kSkip; i < depth; ++i) {
if (dvmIsReflectionMethod(methods[i])) {
continue;
}
Object* klass = (Object *)methods[i]->clazz;
dvmSetObjectArrayElement(classes, objCount, klass);
objCount++;
}
assert(objCount == classes->length);
dvmReleaseTrackedAlloc((Object*)classes, NULL);
RETURN_PTR(classes);
}
/*
* Return a trace buffer for the specified thread or NULL if the
* thread is not still alive. *depth is set to the length of a
* non-NULL trace buffer. Caller is responsible for freeing the trace
* buffer.
*/
static int* getTraceBuf(Object* targetThreadObj, size_t* pStackDepth)
{
Thread* self = dvmThreadSelf();
Thread* thread;
int* traceBuf;
assert(targetThreadObj != NULL);
dvmLockThreadList(self);
/*
* Make sure the thread is still alive and in the list.
*/
for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
if (thread->threadObj == targetThreadObj)
break;
}
if (thread == NULL) {
ALOGI("VMStack.getTraceBuf: threadObj %p not active",
targetThreadObj);
dvmUnlockThreadList();
return NULL;
}
/*
* Suspend the thread, pull out the stack trace, then resume the thread
* and release the thread list lock. If we're being asked to examine
* our own stack trace, skip the suspend/resume.
*/
if (thread != self)
dvmSuspendThread(thread);
traceBuf = dvmFillInStackTraceRaw(thread, pStackDepth);
if (thread != self)
dvmResumeThread(thread);
dvmUnlockThreadList();
return traceBuf;
}
/*
* public static StackTraceElement[] getThreadStackTrace(Thread t)
*
* Retrieve the stack trace of the specified thread and return it as an
* array of StackTraceElement. Returns NULL on failure.
*/
static void Dalvik_dalvik_system_VMStack_getThreadStackTrace(const u4* args,
JValue* pResult)
{
Object* targetThreadObj = (Object*) args[0];
size_t stackDepth;
int* traceBuf = getTraceBuf(targetThreadObj, &stackDepth);
if (traceBuf == NULL)
RETURN_PTR(NULL);
/*
* Convert the raw buffer into an array of StackTraceElement.
*/
ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
free(traceBuf);
RETURN_PTR(trace);
}
/*
* public static int fillStackTraceElements(Thread t, StackTraceElement[] stackTraceElements)
*
* Retrieve a partial stack trace of the specified thread and return
* the number of frames filled. Returns 0 on failure.
*/
static void Dalvik_dalvik_system_VMStack_fillStackTraceElements(const u4* args,
JValue* pResult)
{
Object* targetThreadObj = (Object*) args[0];
ArrayObject* steArray = (ArrayObject*) args[1];
size_t stackDepth;
int* traceBuf = getTraceBuf(targetThreadObj, &stackDepth);
if (traceBuf == NULL)
RETURN_PTR(NULL);
/*
* Set the raw buffer into an array of StackTraceElement.
*/
if (stackDepth > steArray->length) {
stackDepth = steArray->length;
}
dvmFillStackTraceElements(traceBuf, stackDepth, steArray);
free(traceBuf);
RETURN_INT(stackDepth);
}
const DalvikNativeMethod dvm_dalvik_system_VMStack[] = {
{ "getCallingClassLoader", "()Ljava/lang/ClassLoader;",
Dalvik_dalvik_system_VMStack_getCallingClassLoader },
{ "getStackClass2", "()Ljava/lang/Class;",
Dalvik_dalvik_system_VMStack_getStackClass2 },
{ "getClasses", "(I)[Ljava/lang/Class;",
Dalvik_dalvik_system_VMStack_getClasses },
{ "getThreadStackTrace", "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;",
Dalvik_dalvik_system_VMStack_getThreadStackTrace },
{ "fillStackTraceElements", "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I",
Dalvik_dalvik_system_VMStack_fillStackTraceElements },
{ NULL, NULL, NULL },
};