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