/* * Show a relatively human-readable message describing the failure to * resolve a class. * * TODO: this is somewhat misleading when resolution fails because of * illegal access rather than nonexistent class. */ void dvmLogUnableToResolveClass(const char* missingClassDescr, const Method* meth) { if (gDvm.optimizing) return; char* dotMissingClass = dvmDescriptorToDot(missingClassDescr); char* dotFromClass = dvmDescriptorToDot(meth->clazz->descriptor); //char* methodDescr = dexProtoCopyMethodDescriptor(&meth->prototype); LOGE("Could not find class '%s', referenced from method %s.%s\n", dotMissingClass, dotFromClass, meth->name/*, methodDescr*/); free(dotMissingClass); free(dotFromClass); //free(methodDescr); }
/* * Throw the named exception using the dotted form of the class * descriptor as the exception message, and with the specified cause. */ void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor, const char* messageDescriptor, Object* cause) { char* message = dvmDescriptorToDot(messageDescriptor); dvmThrowChainedException(exceptionDescriptor, message, cause); free(message); }
/* * private static String[] getClassNameList(int cookie) * * Returns a String array that holds the names of all classes in the * specified DEX file. */ static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args, JValue* pResult) { int cookie = args[0]; DexOrJar* pDexOrJar = (DexOrJar*) cookie; DvmDex* pDvmDex; DexFile* pDexFile; ArrayObject* stringArray; Thread* self = dvmThreadSelf(); if (!validateCookie(cookie)) RETURN_VOID(); if (pDexOrJar->isDex) pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); else pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); assert(pDvmDex != NULL); pDexFile = pDvmDex->pDexFile; int count = pDexFile->pHeader->classDefsSize; stringArray = dvmAllocObjectArray(gDvm.classJavaLangString, count, ALLOC_DEFAULT); if (stringArray == NULL) { /* probably OOM */ ALOGD("Failed allocating array of %d strings\n", count); assert(dvmCheckException(self)); RETURN_VOID(); } int i; for (i = 0; i < count; i++) { const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i); const char* descriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); char* className = dvmDescriptorToDot(descriptor); StringObject* str = dvmCreateStringFromCstr(className); dvmSetObjectArrayElement(stringArray, i, (Object *)str); dvmReleaseTrackedAlloc((Object *)str, self); free(className); } dvmReleaseTrackedAlloc((Object*)stringArray, self); RETURN_PTR(stringArray); }
/* * private static String[] getClassNameList(int cookie) * * Returns a String array that holds the names of all classes in the * specified DEX file. */ static void Dalvik_dalvik_system_DexFile_getClassNameList(const u4* args, JValue* pResult) { int cookie = args[0]; DexOrJar* pDexOrJar = (DexOrJar*) cookie; DvmDex* pDvmDex; DexFile* pDexFile; ArrayObject* stringArray; if (!validateCookie(cookie)) RETURN_VOID(); if (pDexOrJar->isDex) pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile); else pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile); assert(pDvmDex != NULL); pDexFile = pDvmDex->pDexFile; int count = pDexFile->pHeader->classDefsSize; stringArray = dvmAllocObjectArray(gDvm.classJavaLangString, count, ALLOC_DEFAULT); if (stringArray == NULL) RETURN_VOID(); // should be an OOM pending StringObject** contents = (StringObject**) stringArray->contents; int i; for (i = 0; i < count; i++) { const DexClassDef* pClassDef = dexGetClassDef(pDexFile, i); const char* descriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); char* className = dvmDescriptorToDot(descriptor); contents[i] = dvmCreateStringFromCstr(className, ALLOC_DEFAULT); dvmReleaseTrackedAlloc((Object*) contents[i], NULL); free(className); } dvmReleaseTrackedAlloc((Object*)stringArray, NULL); RETURN_PTR(stringArray); }
/* * Dump the contents of a raw stack trace to the log. */ void dvmLogRawStackTrace(const int* intVals, int stackDepth) { int i; /* * Run through the array of stack frame data. */ for (i = 0; i < stackDepth; i++) { Method* meth; int lineNumber, pc; const char* sourceFile; char* dotName; meth = (Method*) *intVals++; pc = *intVals++; if (pc == -1) // broken top frame? lineNumber = 0; else lineNumber = dvmLineNumFromPC(meth, pc); // probably don't need to do this, but it looks nicer dotName = dvmDescriptorToDot(meth->clazz->descriptor); if (dvmIsNativeMethod(meth)) { LOGI("\tat %s.%s(Native Method)\n", dotName, meth->name); } else { LOGI("\tat %s.%s(%s:%d)\n", dotName, meth->name, dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, pc)); } free(dotName); sourceFile = dvmGetMethodSourceFile(meth); } }
/* * Dump stack frames, starting from the specified frame and moving down. * * Each frame holds a pointer to the currently executing method, and the * saved program counter from the caller ("previous" frame). This means * we don't have the PC for the current method on the stack, which is * pretty reasonable since it's in the "PC register" for the VM. Because * exceptions need to show the correct line number we actually *do* have * an updated version in the fame's "xtra.currentPc", but it's unreliable. * * Note "framePtr" could be NULL in rare circumstances. */ static void dumpFrames(const DebugOutputTarget* target, void* framePtr, Thread* thread) { const StackSaveArea* saveArea; const Method* method; int checkCount = 0; const u2* currentPc = NULL; bool first = true; /* * The "currentPc" is updated whenever we execute an instruction that * might throw an exception. Show it here. */ if (framePtr != NULL && !dvmIsBreakFrame(framePtr)) { saveArea = SAVEAREA_FROM_FP(framePtr); if (saveArea->xtra.currentPc != NULL) currentPc = saveArea->xtra.currentPc; } while (framePtr != NULL) { saveArea = SAVEAREA_FROM_FP(framePtr); method = saveArea->method; if (dvmIsBreakFrame(framePtr)) { //dvmPrintDebugMessage(target, " (break frame)\n"); } else { int relPc; if (currentPc != NULL) relPc = currentPc - saveArea->method->insns; else relPc = -1; char* className = dvmDescriptorToDot(method->clazz->descriptor); if (dvmIsNativeMethod(method)) dvmPrintDebugMessage(target, " at %s.%s(Native Method)\n", className, method->name); else { dvmPrintDebugMessage(target, " at %s.%s(%s:%s%d)\n", className, method->name, dvmGetMethodSourceFile(method), (relPc >= 0 && first) ? "~" : "", relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc)); } free(className); if (first) { /* * Decorate WAIT and MONITOR threads with some detail on * the first frame. * * warning: wait status not stable, even in suspend */ if (thread->status == THREAD_WAIT || thread->status == THREAD_TIMED_WAIT) { Monitor* mon = thread->waitMonitor; Object* obj = dvmGetMonitorObject(mon); if (obj != NULL) { className = dvmDescriptorToDot(obj->clazz->descriptor); dvmPrintDebugMessage(target, " - waiting on <%p> (a %s)\n", obj, className); free(className); } } else if (thread->status == THREAD_MONITOR) { Object* obj; Thread* owner; if (extractMonitorEnterObject(thread, &obj, &owner)) { className = dvmDescriptorToDot(obj->clazz->descriptor); if (owner != NULL) { char* threadName = dvmGetThreadName(owner); dvmPrintDebugMessage(target, " - waiting to lock <%p> (a %s) held by threadid=%d (%s)\n", obj, className, owner->threadId, threadName); free(threadName); } else { dvmPrintDebugMessage(target, " - waiting to lock <%p> (a %s) held by ???\n", obj, className); } free(className); } } } } /* * Get saved PC for previous frame. There's no savedPc in a "break" * frame, because that represents native or interpreted code * invoked by the VM. The saved PC is sitting in the "PC register", * a local variable on the native stack. */ currentPc = saveArea->savedPc; first = false; if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) { LOGW("Warning: loop in stack trace at frame %d (%p -> %p)\n", checkCount, framePtr, saveArea->prevFrame); break; } framePtr = saveArea->prevFrame; checkCount++; if (checkCount > 300) { dvmPrintDebugMessage(target, " ***** printed %d frames, not showing any more\n", checkCount); break; } } dvmPrintDebugMessage(target, "\n"); }
static int getPrettyClassNameId(const char *descriptor) { hprof_string_id classNameId; char *dotName = dvmDescriptorToDot(descriptor); /* Hprof suggests that array class names be converted from, e.g., * "[[[I" to "int[][][]" and "[Lorg.blort.Spaz;" to * "org.blort.Spaz[]". */ if (dotName[0] == '[') { const char *c; char *newName; char *nc; size_t dim; size_t newLen; c = dotName; dim = 0; while (*c == '[') { dim++; c++; } if (*c == 'L') { c++; } else { /* It's a primitive type; we should use a pretty name. * Add semicolons to make all strings have the format * of object class names. */ switch (*c) { case 'Z': c = "boolean;"; break; case 'C': c = "char;"; break; case 'F': c = "float;"; break; case 'D': c = "double;"; break; case 'B': c = "byte;"; break; case 'S': c = "short;"; break; case 'I': c = "int;"; break; case 'J': c = "long;"; break; default: assert(false); c = "UNKNOWN;"; break; } } /* We have a string of the form "name;" and * we want to replace the semicolon with as many * "[]" pairs as is in dim. */ newLen = strlen(c)-1 + dim*2; newName = malloc(newLen + 1); if (newName == NULL) { return -1; } strcpy(newName, c); newName[newLen] = '\0'; /* Point nc to the semicolon. */ nc = newName + newLen - dim*2; assert(*nc == ';'); while (dim--) { *nc++ = '['; *nc++ = ']'; } assert(*nc == '\0'); classNameId = hprofLookupStringId(newName); free(newName); } else { classNameId = hprofLookupStringId(dotName); } free(dotName); return classNameId; }
/* * Generate an array of StackTraceElement objects from the raw integer * data encoded by dvmFillInStackTrace(). * * "intVals" points to the first {method,pc} pair. * * The returned array is not added to the "local refs" list. */ ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth) { ArrayObject* steArray = NULL; int i; /* init this if we haven't yet */ if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement)) dvmInitClass(gDvm.classJavaLangStackTraceElement); /* allocate a StackTraceElement array */ steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray, stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT); if (steArray == NULL) goto bail; /* * Allocate and initialize a StackTraceElement for each stack frame. * We use the standard constructor to configure the object. */ for (i = 0; i < stackDepth; i++) { Object* ste; Method* meth; StringObject* className; StringObject* methodName; StringObject* fileName; int lineNumber, pc; const char* sourceFile; char* dotName; ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT); if (ste == NULL) goto bail; meth = (Method*) *intVals++; pc = *intVals++; if (pc == -1) // broken top frame? lineNumber = 0; else lineNumber = dvmLineNumFromPC(meth, pc); dotName = dvmDescriptorToDot(meth->clazz->descriptor); className = dvmCreateStringFromCstr(dotName); free(dotName); methodName = dvmCreateStringFromCstr(meth->name); sourceFile = dvmGetMethodSourceFile(meth); if (sourceFile != NULL) fileName = dvmCreateStringFromCstr(sourceFile); else fileName = NULL; /* * Invoke: * public StackTraceElement(String declaringClass, String methodName, * String fileName, int lineNumber) * (where lineNumber==-2 means "native") */ JValue unused; dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init, ste, &unused, className, methodName, fileName, lineNumber); dvmReleaseTrackedAlloc(ste, NULL); dvmReleaseTrackedAlloc((Object*) className, NULL); dvmReleaseTrackedAlloc((Object*) methodName, NULL); dvmReleaseTrackedAlloc((Object*) fileName, NULL); if (dvmCheckException(dvmThreadSelf())) goto bail; dvmSetObjectArrayElement(steArray, i, ste); } bail: dvmReleaseTrackedAlloc((Object*) steArray, NULL); return steArray; }