예제 #1
0
/*
 * Dump the stack traces for all threads to the supplied file, putting
 * a timestamp header on it.
 */
static void logThreadStacks(FILE* fp)
{
    DebugOutputTarget target;

    dvmCreateFileOutputTarget(&target, fp);

    pid_t pid = getpid();
    time_t now = time(NULL);
    struct tm* ptm;
#ifdef HAVE_LOCALTIME_R
    struct tm tmbuf;
    ptm = localtime_r(&now, &tmbuf);
#else
    ptm = localtime(&now);
#endif
    dvmPrintDebugMessage(&target,
        "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n",
        pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
        ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
    printProcessName(&target);
    dvmPrintDebugMessage(&target, "\n");
    dvmDumpJniStats(&target);
    dvmDumpAllThreadsEx(&target, true);
    fprintf(fp, "----- end %d -----\n", pid);
}
예제 #2
0
파일: Stack.cpp 프로젝트: nesl/CAreDroid
/*
 * Dump the native stack for the specified thread.
 */
void dvmDumpNativeStack(const DebugOutputTarget* target, pid_t tid)
{
#ifdef HAVE_ANDROID_OS
    const size_t MAX_DEPTH = 32;
    backtrace_frame_t backtrace[MAX_DEPTH];
    ssize_t frames = unwind_backtrace_thread(tid, backtrace, 0, MAX_DEPTH);
    if (frames > 0) {
        backtrace_symbol_t backtrace_symbols[MAX_DEPTH];
        get_backtrace_symbols(backtrace, frames, backtrace_symbols);

        for (size_t i = 0; i < size_t(frames); i++) {
            char line[MAX_BACKTRACE_LINE_LENGTH];
            format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],
                    line, MAX_BACKTRACE_LINE_LENGTH);
            dvmPrintDebugMessage(target, "  %s\n", line);
        }

        free_backtrace_symbols(backtrace_symbols, frames);
    } else {
        dvmPrintDebugMessage(target, "  (native backtrace unavailable)\n");
    }
#endif
}
예제 #3
0
파일: Stack.cpp 프로젝트: nesl/CAreDroid
static void printWaitMessage(const DebugOutputTarget* target, const char* detail, Object* obj,
        Thread* thread)
{
    std::string msg(StringPrintf("  - waiting %s <%p> ", detail, obj));

    if (obj->clazz != gDvm.classJavaLangClass) {
        // I(16573)   - waiting on <0xf5feda38> (a java.util.LinkedList)
        // I(16573)   - waiting on <0xf5ed54f8> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
        msg += "(a " + dvmHumanReadableType(obj) + ")";
    }

    if (thread != NULL) {
        std::string threadName(dvmGetThreadName(thread));
        StringAppendF(&msg, " held by tid=%d (%s)", thread->threadId, threadName.c_str());
    }

    dvmPrintDebugMessage(target, "%s\n", msg.c_str());
}
/*
 * Print the name of the current process, if we can get it.
 */
static void printProcessName(const DebugOutputTarget* target)
{
    int fd = -1;

    fd = open("/proc/self/cmdline", O_RDONLY, 0);
    if (fd < 0)
        goto bail;

    char tmpBuf[256];
    ssize_t actual;

    actual = read(fd, tmpBuf, sizeof(tmpBuf)-1);
    if (actual <= 0)
        goto bail;

    tmpBuf[actual] = '\0';
    dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf);

bail:
    if (fd >= 0)
        close(fd);
}
예제 #5
0
파일: Stack.cpp 프로젝트: nesl/CAreDroid
/*
 * Dump the stack for the specified thread, which is still running.
 *
 * This is very dangerous, because stack frames are being pushed on and
 * popped off, and if the thread exits we'll be looking at freed memory.
 * The plan here is to take a snapshot of the stack and then dump that
 * to try to minimize the chances of catching it mid-update.  This should
 * work reasonably well on a single-CPU system.
 *
 * There is a small chance that calling here will crash the VM.
 */
void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread)
{
    StackSaveArea* saveArea;
    const u1* origStack;
    u1* stackCopy = NULL;
    int origSize, fpOffset;
    void* fp;
    int depthLimit = 200;

    if (thread == NULL || thread->interpSave.curFrame == NULL) {
        dvmPrintDebugMessage(target,
            "DumpRunning: Thread at %p has no curFrame (threadid=%d)\n",
            thread, (thread != NULL) ? thread->threadId : 0);
        return;
    }

    /* wait for a full quantum */
    sched_yield();

    /* copy the info we need, then the stack itself */
    origSize = thread->interpStackSize;
    origStack = (const u1*) thread->interpStackStart - origSize;
    stackCopy = (u1*) malloc(origSize);
    fpOffset = (u1*) thread->interpSave.curFrame - origStack;
    memcpy(stackCopy, origStack, origSize);

    /*
     * Run through the stack and rewrite the "prev" pointers.
     */
    //ALOGI("DR: fpOff=%d (from %p %p)",fpOffset, origStack,
    //     thread->interpSave.curFrame);
    fp = stackCopy + fpOffset;
    while (true) {
        int prevOffset;

        if (depthLimit-- < 0) {
            /* we're probably screwed */
            dvmPrintDebugMessage(target, "DumpRunning: depth limit hit\n");
            dvmAbort();
        }
        saveArea = SAVEAREA_FROM_FP(fp);
        if (saveArea->prevFrame == NULL)
            break;

        prevOffset = (u1*) saveArea->prevFrame - origStack;
        if (prevOffset < 0 || prevOffset > origSize) {
            dvmPrintDebugMessage(target,
                "DumpRunning: bad offset found: %d (from %p %p)\n",
                prevOffset, origStack, saveArea->prevFrame);
            saveArea->prevFrame = NULL;
            break;
        }

        saveArea->prevFrame = (u4*)(stackCopy + prevOffset);
        fp = saveArea->prevFrame;
    }

    /*
     * We still need to pass the Thread for some monitor wait stuff.
     */
    dumpFrames(target, stackCopy + fpOffset, thread);
    free(stackCopy);
}
예제 #6
0
파일: Stack.cpp 프로젝트: nesl/CAreDroid
/*
 * 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;

    /*
     * We call functions that require us to be holding the thread list lock.
     * It's probable that the caller has already done so, but it's not
     * guaranteed.  If it's not locked, lock it now.
     */
    bool needThreadUnlock = dvmTryLockThreadList();

    /*
     * The "currentPc" is updated whenever we execute an instruction that
     * might throw an exception.  Show it here.
     */
    if (framePtr != NULL && !dvmIsBreakFrame((u4*)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((u4*)framePtr)) {
            //dvmPrintDebugMessage(target, "  (break frame)\n");
        } else {
            int relPc;

            if (currentPc != NULL)
                relPc = currentPc - saveArea->method->insns;
            else
                relPc = -1;

            std::string methodName(dvmHumanReadableMethod(method, false));
            if (dvmIsNativeMethod(method)) {
                dvmPrintDebugMessage(target, "  at %s(Native Method)\n",
                        methodName.c_str());
            } else {
                dvmPrintDebugMessage(target, "  at %s(%s:%s%d)\n",
                        methodName.c_str(), dvmGetMethodSourceFile(method),
                        (relPc >= 0 && first) ? "~" : "",
                        relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));
            }

            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) {
                        Thread* joinThread = NULL;
                        if (obj->clazz == gDvm.classJavaLangVMThread) {
                            joinThread = dvmGetThreadFromThreadObject(obj);
                        }
                        if (joinThread == NULL) {
                            joinThread = dvmGetObjectLockHolder(obj);
                        }
                        printWaitMessage(target, "on", obj, joinThread);
                    }
                } else if (thread->status == THREAD_MONITOR) {
                    Object* obj;
                    Thread* owner;
                    if (extractMonitorEnterObject(thread, &obj, &owner)) {
                        printWaitMessage(target, "to lock", obj, owner);
                    }
                }
            }
        }

        /*
         * 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) {
            ALOGW("Warning: loop in stack trace at frame %d (%p -> %p)",
                checkCount, framePtr, saveArea->prevFrame);
            break;
        }
        framePtr = saveArea->prevFrame;

        checkCount++;
        if (checkCount > 300) {
            dvmPrintDebugMessage(target,
                "  ***** printed %d frames, not showing any more\n",
                checkCount);
            break;
        }
    }

    if (needThreadUnlock) {
        dvmUnlockThreadList();
    }
}
예제 #7
0
/*
 * 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");
}
/*
 * Dump the stack traces for all threads to the log or to a file.  If it's
 * to a file we have a little setup to do.
 */
static void logThreadStacks(void)
{
    DebugOutputTarget target;

    if (gDvm.stackTraceFile == NULL) {
        /* just dump to log file */
        dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
        dvmDumpAllThreadsEx(&target, true);
    } else {
        FILE* fp = NULL;
        int cc, fd;

        /*
         * Open the stack trace output file, creating it if necessary.  It
         * needs to be world-writable so other processes can write to it.
         */
        fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
        if (fd < 0) {
            LOGE("Unable to open stack trace file '%s': %s\n",
                gDvm.stackTraceFile, strerror(errno));
            return;
        }

        /* gain exclusive access to the file */
        cc = flock(fd, LOCK_EX | LOCK_UN);
        if (cc != 0) {
            LOGV("Sleeping on flock(%s)\n", gDvm.stackTraceFile);
            cc = flock(fd, LOCK_EX);
        }
        if (cc != 0) {
            LOGE("Unable to lock stack trace file '%s': %s\n",
                gDvm.stackTraceFile, strerror(errno));
            close(fd);
            return;
        }

        fp = fdopen(fd, "a");
        if (fp == NULL) {
            LOGE("Unable to fdopen '%s' (%d): %s\n",
                gDvm.stackTraceFile, fd, strerror(errno));
            flock(fd, LOCK_UN);
            close(fd);
            return;
        }

        dvmCreateFileOutputTarget(&target, fp);

        pid_t pid = getpid();
        time_t now = time(NULL);
        struct tm* ptm;
#ifdef HAVE_LOCALTIME_R
        struct tm tmbuf;
        ptm = localtime_r(&now, &tmbuf);
#else
        ptm = localtime(&now);
#endif
        dvmPrintDebugMessage(&target,
            "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n",
            pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
            ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
        printProcessName(&target);
        dvmPrintDebugMessage(&target, "\n");
        fflush(fp);     /* emit at least the header if we crash during dump */
        dvmDumpAllThreadsEx(&target, true);
        fprintf(fp, "----- end %d -----\n", pid);

        /*
         * Unlock and close the file, flushing pending data before we unlock
         * it.  The fclose() will close the underyling fd.
         */
        fflush(fp);
        flock(fd, LOCK_UN);
        fclose(fp);

        LOGI("Wrote stack trace to '%s'\n", gDvm.stackTraceFile);
    }
}