/*
* 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 "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()->curFrame);
UNUSED_PARAMETER(args);
if (clazz == NULL)
RETURN_PTR(NULL);
RETURN_PTR(clazz->classLoader);
}
/*
* public static ClassLoader getCallingClassLoader2()
*
* Return the defining class loader of the caller's caller's caller.
*/
static void Dalvik_dalvik_system_VMStack_getCallingClassLoader2(const u4* args,
JValue* pResult)
{
ClassObject* clazz = dvmGetCaller3Class(dvmThreadSelf()->curFrame);
UNUSED_PARAMETER(args);
if (clazz == NULL)
RETURN_PTR(NULL);
RETURN_PTR(clazz->classLoader);
}
/*
* public static Class<?>[] getClasses(int maxDepth, boolean stopAtPrivileged)
*
* 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 */
unsigned int maxSize = args[0];
bool stopAtPrivileged = args[1];
unsigned int size = 0;
const unsigned int kSkip = 2;
const Method** methods = NULL;
int methodCount;
/*
* Get an array with the stack trace in it.
*/
if (!dvmCreateStackTraceArray(dvmThreadSelf()->curFrame, &methods,
&methodCount))
{
LOGE("Failed to create stack trace array\n");
dvmThrowException("Ljava/lang/InternalError;", NULL);
RETURN_VOID();
}
//int i;
//LOGI("dvmCreateStackTraceArray results:\n");
//for (i = 0; i < methodCount; i++) {
// LOGI(" %2d: %s.%s\n",
// i, methods[i]->clazz->descriptor, methods[i]->name);
//}
/*
* Run through the array and count up how many elements there are.
*/
unsigned int idx;
for (idx = kSkip; (int) idx < methodCount && size < maxSize; idx++) {
const Method* meth = methods[idx];
if (dvmIsReflectionMethod(meth))
continue;
if (stopAtPrivileged && dvmIsPrivilegedMethod(meth)) {
/*
* We want the last element of the array to be the caller of
* the privileged method, so we want to include the privileged
* method and the next one.
*/
if (maxSize > size + 2)
maxSize = size + 2;
}
size++;
}
/*
* Create an array object to hold the classes.
* TODO: can use gDvm.classJavaLangClassArray here?
*/
ClassObject* classArrayClass = NULL;
ArrayObject* classes = NULL;
classArrayClass = dvmFindArrayClass("[Ljava/lang/Class;", NULL);
if (classArrayClass == NULL) {
LOGW("Unable to find java.lang.Class array class\n");
goto bail;
}
classes = dvmAllocArray(classArrayClass, size, kObjectArrayRefWidth,
ALLOC_DEFAULT);
if (classes == NULL) {
LOGW("Unable to allocate class array (%d elems)\n", size);
goto bail;
}
/*
* Fill in the array.
*/
ClassObject** objects = (ClassObject**) classes->contents;
unsigned int sidx = 0;
for (idx = kSkip; (int) idx < methodCount && sidx < size; idx++) {
const Method* meth = methods[idx];
if (dvmIsReflectionMethod(meth))
continue;
*objects++ = meth->clazz;
sidx++;
}
bail:
free(methods);
dvmReleaseTrackedAlloc((Object*) classes, NULL);
RETURN_PTR(classes);
}
/*
* 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];
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) {
LOGI("VMStack.getThreadStackTrace: threadObj %p not active\n",
targetThreadObj);
dvmUnlockThreadList();
RETURN_PTR(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.
*/
int stackDepth = -1;
if (thread != self)
dvmSuspendThread(thread);
traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth);
if (thread != self)
dvmResumeThread(thread);
dvmUnlockThreadList();
/*
* Convert the raw buffer into an array of StackTraceElement.
*/
ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
free(traceBuf);
RETURN_PTR(trace);
}
const DalvikNativeMethod dvm_dalvik_system_VMStack[] = {
{ "getCallingClassLoader", "()Ljava/lang/ClassLoader;",
Dalvik_dalvik_system_VMStack_getCallingClassLoader },
{ "getCallingClassLoader2", "()Ljava/lang/ClassLoader;",
Dalvik_dalvik_system_VMStack_getCallingClassLoader2 },
{ "getClasses", "(IZ)[Ljava/lang/Class;",
Dalvik_dalvik_system_VMStack_getClasses },
{ "getThreadStackTrace", "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;",
Dalvik_dalvik_system_VMStack_getThreadStackTrace },
{ NULL, NULL, NULL },
};