diff options
Diffstat (limited to 'vm/Thread.cpp')
-rw-r--r-- | vm/Thread.cpp | 339 |
1 files changed, 222 insertions, 117 deletions
diff --git a/vm/Thread.cpp b/vm/Thread.cpp index 28182d6f2..aa2352af2 100644 --- a/vm/Thread.cpp +++ b/vm/Thread.cpp @@ -30,6 +30,10 @@ #include <errno.h> #include <fcntl.h> +#ifdef HAVE_ANDROID_OS +#include <dirent.h> +#endif + #if defined(HAVE_PRCTL) #include <sys/prctl.h> #endif @@ -54,7 +58,7 @@ pid_t gettid() { return syscall(__NR_gettid);} // Change this to enable logging on cgroup errors #define ENABLE_CGROUP_ERR_LOGGING 0 -// change this to LOGV/LOGD to debug thread activity +// change this to ALOGV/ALOGD to debug thread activity #define LOG_THREAD LOGVV /* @@ -251,13 +255,13 @@ bool dvmThreadStartup() /* allocate a TLS slot */ if (pthread_key_create(&gDvm.pthreadKeySelf, threadExitCheck) != 0) { - LOGE("ERROR: pthread_key_create failed"); + ALOGE("ERROR: pthread_key_create failed"); return false; } /* test our pthread lib */ if (pthread_getspecific(gDvm.pthreadKeySelf) != NULL) - LOGW("WARNING: newly-created pthread TLS slot is not NULL"); + ALOGW("WARNING: newly-created pthread TLS slot is not NULL"); /* prep thread-related locks and conditions */ dvmInitMutex(&gDvm.threadListLock); @@ -278,7 +282,7 @@ bool dvmThreadStartup() gDvm.threadIdMap = dvmAllocBitVector(kMaxThreadId, false); - thread = allocThread(gDvm.stackSize); + thread = allocThread(gDvm.mainThreadStackSize); if (thread == NULL) return false; @@ -472,7 +476,7 @@ static void lockThreadSuspend(const char* who, SuspendCause why) * Could be two app threads both want to patch predicted * chaining cells around the same time. */ - LOGI("threadid=%d ODD: want thread-suspend lock (%s:%s)," + ALOGI("threadid=%d ODD: want thread-suspend lock (%s:%s)," " it's held, no suspend pending", self->threadId, who, getSuspendCauseStr(why)); } else { @@ -484,7 +488,7 @@ static void lockThreadSuspend(const char* who, SuspendCause why) if (sleepIter == 0) startWhen = dvmGetRelativeTimeUsec(); if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) { - LOGE("threadid=%d: couldn't get thread-suspend lock (%s:%s)," + ALOGE("threadid=%d: couldn't get thread-suspend lock (%s:%s)," " bailing", self->threadId, who, getSuspendCauseStr(why)); /* threads are not suspended, thread dump could crash */ @@ -548,12 +552,12 @@ void dvmSlayDaemons() gDvm.offJavaLangThread_daemon)) { /* should never happen; suspend it with the rest */ - LOGW("threadid=%d: non-daemon id=%d still running at shutdown?!", + ALOGW("threadid=%d: non-daemon id=%d still running at shutdown?!", threadId, target->threadId); } std::string threadName(dvmGetThreadName(target)); - LOGV("threadid=%d: suspending daemon id=%d name='%s'", + ALOGV("threadid=%d: suspending daemon id=%d name='%s'", threadId, target->threadId, threadName.c_str()); /* mark as suspended */ @@ -604,7 +608,7 @@ void dvmSlayDaemons() if (target->status == THREAD_RUNNING) { if (!complained) - LOGD("threadid=%d not ready yet", target->threadId); + ALOGD("threadid=%d not ready yet", target->threadId); allSuspended = false; /* keep going so we log each running daemon once */ } @@ -613,12 +617,12 @@ void dvmSlayDaemons() } if (allSuspended) { - LOGV("threadid=%d: all daemons have suspended", threadId); + ALOGV("threadid=%d: all daemons have suspended", threadId); break; } else { if (!complained) { complained = true; - LOGD("threadid=%d: waiting briefly for daemon suspension", + ALOGD("threadid=%d: waiting briefly for daemon suspension", threadId); } } @@ -684,7 +688,7 @@ bool dvmPrepMainThread() Method* init; JValue unused; - LOGV("+++ finishing prep on main VM thread"); + ALOGV("+++ finishing prep on main VM thread"); /* main thread is always first in list at this point */ thread = gDvm.threadList; @@ -695,14 +699,14 @@ bool dvmPrepMainThread() * we create an instance of them. */ if (!dvmInitClass(gDvm.classJavaLangClass)) { - LOGE("'Class' class failed to initialize"); + ALOGE("'Class' class failed to initialize"); return false; } if (!dvmInitClass(gDvm.classJavaLangThreadGroup) || !dvmInitClass(gDvm.classJavaLangThread) || !dvmInitClass(gDvm.classJavaLangVMThread)) { - LOGE("thread classes failed to initialize"); + ALOGE("thread classes failed to initialize"); return false; } @@ -716,7 +720,7 @@ bool dvmPrepMainThread() */ threadObj = dvmAllocObject(gDvm.classJavaLangThread, ALLOC_DEFAULT); if (threadObj == NULL) { - LOGE("unable to allocate main thread object"); + ALOGE("unable to allocate main thread object"); return false; } dvmReleaseTrackedAlloc(threadObj, NULL); @@ -732,7 +736,7 @@ bool dvmPrepMainThread() dvmCallMethod(thread, init, threadObj, &unused, groupObj, threadNameStr, THREAD_NORM_PRIORITY, false); if (dvmCheckException(thread)) { - LOGE("exception thrown while constructing main thread object"); + ALOGE("exception thrown while constructing main thread object"); return false; } @@ -741,7 +745,7 @@ bool dvmPrepMainThread() */ vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT); if (vmThreadObj == NULL) { - LOGE("unable to allocate main vmthread object"); + ALOGE("unable to allocate main vmthread object"); return false; } dvmReleaseTrackedAlloc(vmThreadObj, NULL); @@ -750,7 +754,7 @@ bool dvmPrepMainThread() "(Ljava/lang/Thread;)V"); dvmCallMethod(thread, init, vmThreadObj, &unused, threadObj); if (dvmCheckException(thread)) { - LOGE("exception thrown while constructing main vmthread object"); + ALOGE("exception thrown while constructing main vmthread object"); return false; } @@ -778,7 +782,7 @@ bool dvmPrepMainThread() */ Object* systemLoader = dvmGetSystemClassLoader(); if (systemLoader == NULL) { - LOGW("WARNING: system class loader is NULL (setting main ctxt)"); + ALOGW("WARNING: system class loader is NULL (setting main ctxt)"); /* keep going? */ } else { dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_contextClassLoader, @@ -899,7 +903,7 @@ static bool prepareThread(Thread* thread) thread->handle = pthread_self(); thread->systemTid = dvmGetSysThreadId(); - //LOGI("SYSTEM TID IS %d (pid is %d)", (int) thread->systemTid, + //ALOGI("SYSTEM TID IS %d (pid is %d)", (int) thread->systemTid, // (int) getpid()); /* * If we were called by dvmAttachCurrentThread, the self value is @@ -907,7 +911,7 @@ static bool prepareThread(Thread* thread) */ setThreadSelf(thread); - LOGV("threadid=%d: interp stack at %p", + ALOGV("threadid=%d: interp stack at %p", thread->threadId, thread->interpStackStart - thread->interpStackSize); /* @@ -981,7 +985,7 @@ static void freeThread(Thread* thread) free(interpStackBottom); #else if (munmap(interpStackBottom, thread->interpStackSize) != 0) - LOGW("munmap(thread stack) failed"); + ALOGW("munmap(thread stack) failed"); #endif } @@ -1020,7 +1024,7 @@ static void setThreadSelf(Thread* thread) * here to ensure we clean up after ourselves. */ if (thread != NULL) { - LOGE("pthread_setspecific(%p) failed, err=%d", thread, cc); + ALOGE("pthread_setspecific(%p) failed, err=%d", thread, cc); dvmAbort(); /* the world is fundamentally hosed */ } } @@ -1053,11 +1057,11 @@ static void threadExitCheck(void* arg) Thread* self = (Thread*) arg; assert(self != NULL); - LOGV("threadid=%d: threadExitCheck(%p) count=%d", + ALOGV("threadid=%d: threadExitCheck(%p) count=%d", self->threadId, arg, self->threadExitCheckCount); if (self->status == THREAD_ZOMBIE) { - LOGW("threadid=%d: Weird -- shouldn't be in threadExitCheck", + ALOGW("threadid=%d: Weird -- shouldn't be in threadExitCheck", self->threadId); return; } @@ -1066,17 +1070,17 @@ static void threadExitCheck(void* arg) /* * Spin a couple of times to let other destructors fire. */ - LOGD("threadid=%d: thread exiting, not yet detached (count=%d)", + ALOGD("threadid=%d: thread exiting, not yet detached (count=%d)", self->threadId, self->threadExitCheckCount); self->threadExitCheckCount++; int cc = pthread_setspecific(gDvm.pthreadKeySelf, self); if (cc != 0) { - LOGE("threadid=%d: unable to re-add thread to TLS", + ALOGE("threadid=%d: unable to re-add thread to TLS", self->threadId); dvmAbort(); } } else { - LOGE("threadid=%d: native thread exited without detaching", + ALOGE("threadid=%d: native thread exited without detaching", self->threadId); dvmAbort(); } @@ -1100,7 +1104,7 @@ static void assignThreadId(Thread* thread) */ int num = dvmAllocBit(gDvm.threadIdMap); if (num < 0) { - LOGE("Ran out of thread IDs"); + ALOGE("Ran out of thread IDs"); dvmAbort(); // TODO: make this a non-fatal error result } @@ -1197,13 +1201,13 @@ static void setThreadName(const char *threadName) buf[sizeof(buf)-1] = '\0'; int err = pthread_setname_np(pthread_self(), buf); if (err != 0) { - LOGW("Unable to set the name of current thread to '%s': %s", + ALOGW("Unable to set the name of current thread to '%s': %s", buf, strerror(err)); } #elif defined(HAVE_PRCTL) prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); #else - LOGD("No way to set current thread's name (%s)", s); + ALOGD("No way to set current thread's name (%s)", s); #endif } @@ -1307,7 +1311,7 @@ bool dvmCreateInterpThread(Object* threadObj, int reqStackSize) * resource limits. VirtualMachineError is probably too severe, * so use OutOfMemoryError. */ - LOGE("Thread creation failed (err=%s)", strerror(errno)); + ALOGE("Thread creation failed (err=%s)", strerror(errno)); dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, NULL); @@ -1529,10 +1533,10 @@ static void* interpThreadStart(void* arg) Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run]; JValue unused; - LOGV("threadid=%d: calling run()", self->threadId); + ALOGV("threadid=%d: calling run()", self->threadId); assert(strcmp(run->name, "run") == 0); dvmCallMethod(self, run, self->threadObj, &unused); - LOGV("threadid=%d: exiting", self->threadId); + ALOGV("threadid=%d: exiting", self->threadId); /* * Remove the thread from various lists, report its death, and free @@ -1562,7 +1566,7 @@ static void threadExitUncaughtException(Thread* self, Object* group) Object* handlerObj; Method* uncaughtHandler; - LOGW("threadid=%d: thread exiting with uncaught exception (group=%p)", + ALOGW("threadid=%d: thread exiting with uncaught exception (group=%p)", self->threadId, group); assert(group != NULL); @@ -1594,14 +1598,14 @@ static void threadExitUncaughtException(Thread* self, Object* group) "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V"); if (uncaughtHandler != NULL) { - //LOGI("+++ calling %s.uncaughtException", + //ALOGI("+++ calling %s.uncaughtException", // handlerObj->clazz->descriptor); JValue unused; dvmCallMethod(self, uncaughtHandler, handlerObj, &unused, self->threadObj, exception); } else { /* should be impossible, but handle it anyway */ - LOGW("WARNING: no 'uncaughtException' method in class %s", + ALOGW("WARNING: no 'uncaughtException' method in class %s", handlerObj->clazz->descriptor); dvmSetException(self, exception); dvmLogExceptionStackTrace(); @@ -1655,7 +1659,7 @@ bool dvmCreateInternalThread(pthread_t* pHandle, const char* name, if (pthread_create(pHandle, &threadAttr, internalThreadStart, pArgs) != 0) { - LOGE("internal thread creation failed"); + ALOGE("internal thread creation failed"); free(pArgs->name); free(pArgs); return false; @@ -1683,7 +1687,7 @@ bool dvmCreateInternalThread(pthread_t* pHandle, const char* name, pthread_cond_wait(&gDvm.threadStartCond, &gDvm.threadListLock); if (newThread == NULL) { - LOGW("internal thread create failed (createStatus=%d)", createStatus); + ALOGW("internal thread create failed (createStatus=%d)", createStatus); assert(createStatus < 0); /* don't free pArgs -- if pthread_create succeeded, child owns it */ dvmUnlockThreadList(); @@ -1890,7 +1894,7 @@ bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon) dvmCallMethod(self, init, threadObj, &unused, (Object*)pArgs->group, threadNameStr, os_getThreadPriorityFromSystem(), isDaemon); if (dvmCheckException(self)) { - LOGE("exception thrown while constructing attached thread object"); + ALOGE("exception thrown while constructing attached thread object"); goto fail_unlink; } @@ -1909,7 +1913,7 @@ bool dvmAttachCurrentThread(const JavaVMAttachArgs* pArgs, bool isDaemon) * way that ensures another thread can't call start() on it. */ if (dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread) != NULL) { - LOGW("WOW: thread start hijack"); + ALOGW("WOW: thread start hijack"); dvmThrowIllegalThreadStateException( "thread has already been started"); /* We don't want to free anything associated with the thread @@ -2016,7 +2020,7 @@ void dvmDetachCurrentThread() } if (!topIsNative) { - LOGE("ERROR: detaching thread with interp frames (count=%d)", + ALOGE("ERROR: detaching thread with interp frames (count=%d)", curDepth); dvmDumpThread(self, false); dvmAbort(); @@ -2110,7 +2114,7 @@ void dvmDetachCurrentThread() dvmLockMutex(&traceState->startStopLock); if (traceState->traceEnabled) { - LOGI("threadid=%d: waiting for method trace to finish", + ALOGI("threadid=%d: waiting for method trace to finish", self->threadId); while (traceState->traceEnabled) { dvmWaitCond(&traceState->threadExitCond, @@ -2144,7 +2148,7 @@ void dvmDetachCurrentThread() if (gDvm.nonDaemonThreadCount == 0) { int cc; - LOGV("threadid=%d: last non-daemon thread", self->threadId); + ALOGV("threadid=%d: last non-daemon thread", self->threadId); //dvmDumpAllThreads(false); // cond var guarded by threadListLock, which we already hold cc = pthread_cond_signal(&gDvm.vmExitCond); @@ -2152,7 +2156,7 @@ void dvmDetachCurrentThread() } } - LOGV("threadid=%d: bye!", self->threadId); + ALOGV("threadid=%d: bye!", self->threadId); releaseThreadId(self); dvmUnlockThreadList(); @@ -2261,7 +2265,7 @@ void dvmSuspendSelf(bool jdwpActivity) * If we got here via waitForDebugger(), don't do this part. */ if (jdwpActivity) { - //LOGI("threadid=%d: clearing wait-for-event (my handle=%08x)", + //ALOGI("threadid=%d: clearing wait-for-event (my handle=%08x)", // self->threadId, (int) self->handle); dvmJdwpClearWaitForEventThread(gDvm.jdwpState); } @@ -2276,7 +2280,7 @@ void dvmSuspendSelf(bool jdwpActivity) * dump event is pending (assuming SignalCatcher was resumed for * just long enough to try to grab the thread-suspend lock). */ - LOGD("threadid=%d: still suspended after undo (sc=%d dc=%d)", + ALOGD("threadid=%d: still suspended after undo (sc=%d dc=%d)", self->threadId, self->suspendCount, self->dbgSuspendCount); } } @@ -2318,12 +2322,12 @@ int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio, errno = 0; *pSavedThreadPrio = getpriority(PRIO_PROCESS, thread->systemTid); if (errno != 0) { - LOGW("Unable to get priority for threadid=%d sysTid=%d", + ALOGW("Unable to get priority for threadid=%d sysTid=%d", thread->threadId, thread->systemTid); return 0; } if (get_sched_policy(thread->systemTid, pSavedThreadPolicy) != 0) { - LOGW("Unable to get policy for threadid=%d sysTid=%d", + ALOGW("Unable to get policy for threadid=%d sysTid=%d", thread->threadId, thread->systemTid); return 0; } @@ -2335,10 +2339,10 @@ int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio, */ if (*pSavedThreadPolicy == SP_BACKGROUND) { if (set_sched_policy(thread->systemTid, SP_FOREGROUND) != 0) { - LOGW("Couldn't set fg policy on tid %d", thread->systemTid); + ALOGW("Couldn't set fg policy on tid %d", thread->systemTid); } else { changeFlags |= kChangedPolicy; - LOGD("Temporarily moving tid %d to fg (was %d)", + ALOGD("Temporarily moving tid %d to fg (was %d)", thread->systemTid, *pSavedThreadPolicy); } } @@ -2350,11 +2354,11 @@ int dvmRaiseThreadPriorityIfNeeded(Thread* thread, int* pSavedThreadPrio, if (*pSavedThreadPrio > 0) { const int kHigher = 0; if (setpriority(PRIO_PROCESS, thread->systemTid, kHigher) != 0) { - LOGW("Couldn't raise priority on tid %d to %d", + ALOGW("Couldn't raise priority on tid %d to %d", thread->systemTid, kHigher); } else { changeFlags |= kChangedPriority; - LOGD("Temporarily raised priority on tid %d (%d -> %d)", + ALOGD("Temporarily raised priority on tid %d (%d -> %d)", thread->systemTid, *pSavedThreadPrio, kHigher); } } @@ -2370,10 +2374,10 @@ void dvmResetThreadPriority(Thread* thread, int changeFlags, { if ((changeFlags & kChangedPolicy) != 0) { if (set_sched_policy(thread->systemTid, savedThreadPolicy) != 0) { - LOGW("NOTE: couldn't reset tid %d to (%d)", + ALOGW("NOTE: couldn't reset tid %d to (%d)", thread->systemTid, savedThreadPolicy); } else { - LOGD("Restored policy of %d to %d", + ALOGD("Restored policy of %d to %d", thread->systemTid, savedThreadPolicy); } } @@ -2381,10 +2385,10 @@ void dvmResetThreadPriority(Thread* thread, int changeFlags, if ((changeFlags & kChangedPriority) != 0) { if (setpriority(PRIO_PROCESS, thread->systemTid, savedThreadPrio) != 0) { - LOGW("NOTE: couldn't reset priority on thread %d to %d", + ALOGW("NOTE: couldn't reset priority on thread %d to %d", thread->systemTid, savedThreadPrio); } else { - LOGD("Restored priority on %d to %d", + ALOGD("Restored priority on %d to %d", thread->systemTid, savedThreadPrio); } } @@ -2457,7 +2461,7 @@ static void waitForThreadSuspend(Thread* self, Thread* thread) */ if (gDvmJit.pJitEntryTable && retryCount > 0 && gDvmJit.hasNewChain && thread->inJitCodeCache) { - LOGD("JIT unchain all for threadid=%d", thread->threadId); + ALOGD("JIT unchain all for threadid=%d", thread->threadId); dvmJitUnchainAll(); } #endif @@ -2468,7 +2472,7 @@ static void waitForThreadSuspend(Thread* self, Thread* thread) */ if (!dvmIterativeSleep(sleepIter++, spinSleepTime, startWhen)) { if (spinSleepTime != FIRST_SLEEP) { - LOGW("threadid=%d: spin on suspend #%d threadid=%d (pcf=%d)", + ALOGW("threadid=%d: spin on suspend #%d threadid=%d (pcf=%d)", self->threadId, retryCount, thread->threadId, priChangeFlags); if (retryCount > 1) { @@ -2483,11 +2487,11 @@ static void waitForThreadSuspend(Thread* self, Thread* thread) spinSleepTime = MORE_SLEEP; if (retryCount++ == kMaxRetries) { - LOGE("Fatal spin-on-suspend, dumping threads"); + ALOGE("Fatal spin-on-suspend, dumping threads"); dvmDumpAllThreads(false); /* log this after -- long traces will scroll off log */ - LOGE("threadid=%d: stuck on threadid=%d, giving up", + ALOGE("threadid=%d: stuck on threadid=%d, giving up", self->threadId, thread->threadId); /* try to get a debuggerd dump from the spinning thread */ @@ -2499,7 +2503,7 @@ static void waitForThreadSuspend(Thread* self, Thread* thread) } if (complained) { - LOGW("threadid=%d: spin on suspend resolved in %lld msec", + ALOGW("threadid=%d: spin on suspend resolved in %lld msec", self->threadId, (dvmGetRelativeTimeUsec() - firstStartWhen) / 1000); //dvmDumpThread(thread, false); /* suspended, so dump is safe */ @@ -2988,13 +2992,13 @@ static Object* getStaticThreadGroup(const char* fieldName) groupField = dvmFindStaticField(gDvm.classJavaLangThreadGroup, fieldName, "Ljava/lang/ThreadGroup;"); if (groupField == NULL) { - LOGE("java.lang.ThreadGroup does not have an '%s' field", fieldName); + ALOGE("java.lang.ThreadGroup does not have an '%s' field", fieldName); dvmThrowInternalError("bad definition for ThreadGroup"); return NULL; } groupObj = dvmGetStaticFieldObject(groupField); if (groupObj == NULL) { - LOGE("java.lang.ThreadGroup.%s not initialized", fieldName); + ALOGE("java.lang.ThreadGroup.%s not initialized", fieldName); dvmThrowInternalError(NULL); return NULL; } @@ -3033,7 +3037,7 @@ Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj) } if (thread == NULL) { - LOGW("WARNING: vmThreadObj=%p has thread=%p, not in thread list", + ALOGW("WARNING: vmThreadObj=%p has thread=%p, not in thread list", vmThreadObj, (Thread*)vmData); vmData = 0; } @@ -3176,7 +3180,7 @@ static int getSchedulerGroup(int tid, char* buf, size_t bufLen) return -1; out_bad_data: - LOGE("Bad cgroup data {%s}", lineBuf); + ALOGE("Bad cgroup data {%s}", lineBuf); snprintf(buf, bufLen, "[data-parse-failed]"); fclose(fp); return -1; @@ -3207,6 +3211,61 @@ const char* dvmGetThreadStatusStr(ThreadStatus status) } } +static void dumpSchedStat(const DebugOutputTarget* target, pid_t tid) { +#ifdef HAVE_ANDROID_OS + /* get some bits from /proc/self/stat */ + ProcStatData procStatData; + if (!dvmGetThreadStats(&procStatData, tid)) { + /* failed, use zeroed values */ + memset(&procStatData, 0, sizeof(procStatData)); + } + + /* grab the scheduler stats for this thread */ + char schedstatBuf[64]; + snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/self/task/%d/schedstat", tid); + int schedstatFd = open(schedstatBuf, O_RDONLY); + strcpy(schedstatBuf, "0 0 0"); /* show this if open/read fails */ + if (schedstatFd >= 0) { + ssize_t bytes; + bytes = read(schedstatFd, schedstatBuf, sizeof(schedstatBuf) - 1); + close(schedstatFd); + if (bytes >= 1) { + schedstatBuf[bytes - 1] = '\0'; /* remove trailing newline */ + } + } + + /* show what we got */ + dvmPrintDebugMessage(target, + " | schedstat=( %s ) utm=%lu stm=%lu core=%d\n", + schedstatBuf, procStatData.utime, procStatData.stime, + procStatData.processor); +#endif +} + +struct SchedulerStats { + int policy; + int priority; + char group[32]; +}; + +/* + * Get scheduler statistics. + */ +static void getSchedulerStats(SchedulerStats* stats, pid_t tid) { + struct sched_param sp; + if (pthread_getschedparam(pthread_self(), &stats->policy, &sp) != 0) { + ALOGW("Warning: pthread_getschedparam failed"); + stats->policy = -1; + stats->priority = -1; + } else { + stats->priority = sp.sched_priority; + } + if (getSchedulerGroup(tid, stats->group, sizeof(stats->group)) == 0 && + stats->group[0] == '\0') { + strcpy(stats->group, "default"); + } +} + /* * Print information about the specified thread. * @@ -3222,12 +3281,8 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, StringObject* nameStr; char* threadName = NULL; char* groupName = NULL; - char schedulerGroupBuf[32]; bool isDaemon; int priority; // java.lang.Thread priority - int policy; // pthread policy - struct sched_param sp; // pthread scheduling parameters - char schedstatBuf[64]; // contents of /proc/[pid]/task/[tid]/schedstat /* * Get the java.lang.Thread object. This function gets called from @@ -3242,7 +3297,7 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, */ threadObj = thread->threadObj; if (threadObj == NULL) { - LOGI("Can't dump thread %d: threadObj not set", thread->threadId); + ALOGI("Can't dump thread %d: threadObj not set", thread->threadId); return; } dvmAddTrackedAlloc(threadObj, NULL); @@ -3254,17 +3309,6 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority); isDaemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon); - if (pthread_getschedparam(pthread_self(), &policy, &sp) != 0) { - LOGW("Warning: pthread_getschedparam failed"); - policy = -1; - sp.sched_priority = -1; - } - if (getSchedulerGroup(thread->systemTid, schedulerGroupBuf, - sizeof(schedulerGroupBuf)) == 0 && - schedulerGroupBuf[0] == '\0') { - strcpy(schedulerGroupBuf, "default"); - } - /* a null value for group is not expected, but deal with it anyway */ groupObj = (Object*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group); @@ -3276,6 +3320,9 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, if (groupName == NULL) groupName = strdup("(null; initializing?)"); + SchedulerStats schedStats; + getSchedulerStats(&schedStats, thread->systemTid); + dvmPrintDebugMessage(target, "\"%s\"%s prio=%d tid=%d %s%s\n", threadName, isDaemon ? " daemon" : "", @@ -3293,40 +3340,22 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, dvmPrintDebugMessage(target, " | sysTid=%d nice=%d sched=%d/%d cgrp=%s handle=%d\n", thread->systemTid, getpriority(PRIO_PROCESS, thread->systemTid), - policy, sp.sched_priority, schedulerGroupBuf, (int)thread->handle); + schedStats.policy, schedStats.priority, schedStats.group, (int)thread->handle); - /* get some bits from /proc/self/stat */ - ProcStatData procStatData; - if (!dvmGetThreadStats(&procStatData, thread->systemTid)) { - /* failed, use zeroed values */ - memset(&procStatData, 0, sizeof(procStatData)); - } + dumpSchedStat(target, thread->systemTid); - /* grab the scheduler stats for this thread */ - snprintf(schedstatBuf, sizeof(schedstatBuf), "/proc/self/task/%d/schedstat", - thread->systemTid); - int schedstatFd = open(schedstatBuf, O_RDONLY); - strcpy(schedstatBuf, "0 0 0"); /* show this if open/read fails */ - if (schedstatFd >= 0) { - ssize_t bytes; - bytes = read(schedstatFd, schedstatBuf, sizeof(schedstatBuf) - 1); - close(schedstatFd); - if (bytes >= 1) { - schedstatBuf[bytes-1] = '\0'; /* remove trailing newline */ - } + /* grab the native stack, if possible */ + if (thread->status == THREAD_NATIVE || thread->status == THREAD_VMWAIT) { + dvmDumpNativeStack(target, thread->systemTid); } - /* show what we got */ - dvmPrintDebugMessage(target, - " | schedstat=( %s ) utm=%lu stm=%lu core=%d\n", - schedstatBuf, procStatData.utime, procStatData.stime, - procStatData.processor); - if (isRunning) dvmDumpRunningThreadStack(target, thread); else dvmDumpThreadStack(target, thread); + dvmPrintDebugMessage(target, "\n"); + dvmReleaseTrackedAlloc(threadObj, NULL); free(threadName); free(groupName); @@ -3334,7 +3363,7 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, std::string dvmGetThreadName(Thread* thread) { if (thread->threadObj == NULL) { - LOGW("threadObj is NULL, name not available"); + ALOGW("threadObj is NULL, name not available"); return "-unknown-"; } @@ -3346,6 +3375,59 @@ std::string dvmGetThreadName(Thread* thread) { return result; } +#ifdef HAVE_ANDROID_OS +/* + * Dumps information about a non-Dalvik thread. + */ +static void dumpNativeThread(const DebugOutputTarget* target, pid_t tid) { + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/comm", tid); + + int fd = open(path, O_RDONLY); + char name[64]; + ssize_t n = 0; + if (fd >= 0) { + n = read(fd, name, sizeof(name) - 1); + close(fd); + } + if (n > 0 && name[n - 1] == '\n') { + n -= 1; + } + if (n <= 0) { + strcpy(name, "<no name>"); + } else { + name[n] = '\0'; + } + + SchedulerStats schedStats; + getSchedulerStats(&schedStats, tid); + + dvmPrintDebugMessage(target, + "\"%s\" sysTid=%d nice=%d sched=%d/%d cgrp=%s\n", + name, tid, getpriority(PRIO_PROCESS, tid), + schedStats.policy, schedStats.priority, schedStats.group); + dumpSchedStat(target, tid); + // Temporarily disabled collecting native stacks from non-Dalvik + // threads because sometimes they misbehave. + //dvmDumpNativeStack(target, tid); + + dvmPrintDebugMessage(target, "\n"); +} + +/* + * Returns true if the specified tid is a Dalvik thread. + * Assumes the thread list lock is held. + */ +static bool isDalvikThread(pid_t tid) { + for (Thread* thread = gDvm.threadList; thread != NULL; thread = thread->next) { + if (thread->systemTid == tid) { + return true; + } + } + return false; +} +#endif + /* * Dump all threads to the log file -- just calls dvmDumpAllThreadsEx() with * an output target. @@ -3373,7 +3455,7 @@ void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock) #ifdef HAVE_ANDROID_OS dvmPrintDebugMessage(target, - "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x)\n", + "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x)\n\n", gDvm.threadListLock.value, gDvm._threadSuspendLock.value, gDvm.threadSuspendCountLock.value, @@ -3393,6 +3475,30 @@ void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock) thread = thread->next; } +#ifdef HAVE_ANDROID_OS + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/task", getpid()); + + DIR* d = opendir(path); + if (d) { + dirent de; + dirent* result; + bool first = true; + while (!readdir_r(d, &de, &result) && result) { + char* end; + pid_t tid = strtol(de.d_name, &end, 10); + if (!*end && !isDalvikThread(tid)) { + if (first) { + dvmPrintDebugMessage(target, "NATIVE THREADS:\n"); + first = false; + } + dumpNativeThread(target, tid); + } + } + closedir(d); + } +#endif + if (grabLock) dvmUnlockThreadList(); } @@ -3434,7 +3540,6 @@ void dvmNukeThread(Thread* thread) * The target thread can continue to execute between the two signals. * (The first just causes debuggerd to attach to it.) */ - #ifdef SIGSTKFLT #define SIG SIGSTKFLT #define SIGNAME "SIGSTKFLT" @@ -3445,22 +3550,22 @@ void dvmNukeThread(Thread* thread) #error No signal available for dvmNukeThread #endif - LOGD("threadid=%d: sending two " SIGNAME "s to threadid=%d (tid=%d) to" - " cause debuggerd dump", - dvmThreadSelf()->threadId, thread->threadId, thread->systemTid); + ALOGD("threadid=%d: sending two " SIGNAME "s to threadid=%d (tid=%d) to" + " cause debuggerd dump", + dvmThreadSelf()->threadId, thread->threadId, thread->systemTid); killResult = pthread_kill(thread->handle, SIG); if (killResult != 0) { - LOGD("NOTE: pthread_kill #1 failed: %s", strerror(killResult)); + ALOGD("NOTE: pthread_kill #1 failed: %s", strerror(killResult)); } usleep(2 * 1000 * 1000); // TODO: timed-wait until debuggerd attaches killResult = pthread_kill(thread->handle, SIG); if (killResult != 0) { - LOGD("NOTE: pthread_kill #2 failed: %s", strerror(killResult)); + ALOGD("NOTE: pthread_kill #2 failed: %s", strerror(killResult)); } - LOGD("Sent, pausing to let debuggerd run"); + ALOGD("Sent, pausing to let debuggerd run"); usleep(8 * 1000 * 1000); // TODO: timed-wait until debuggerd finishes /* ignore SIGSEGV so the eventual dmvAbort() doesn't notify debuggerd */ signal(SIGSEGV, SIG_IGN); - LOGD("Continuing"); + ALOGD("Continuing"); } |