Include non-attached native threads in the SIGQUIT output.
These threads look something like this:
"droid.phasebeam' prio=5 tid=? (not attached)
| sysTid=5369 nice=-4 sched=0/0 cgrp=default
| schedstat=( 0 0 0 ) utm=1 stm=8 core=0 HZ=100
native: __futex_syscall3+8 [0x40074678] (libc.so)
native: __pthread_cond_timedwait_relative+48 [0x40079474] (libc.so)
native: __pthread_cond_timedwait+72 [0x40079528] (libc.so)
native: android::renderscript::Signal::wait(unsigned long long)+58 [0x418bf117] (libRS.so)
native: android::renderscript::LocklessCommandFifo::wait(unsigned long long)+38 [0x418bab97] (libRS.so)
native: android::renderscript::LocklessCommandFifo::get(unsigned int*, unsigned int*, unsigned long long)+22 [0x418babbb] (libRS.so)
native: android::renderscript::ThreadIO::playCoreCommands(android::renderscript::Context*, bool, unsigned long long)+126 [0x418bf84b] (libRS.so)
native: android::renderscript::Context::threadProc(void*)+382 [0x418b7347] (libRS.so)
native: __thread_entry+48 [0x40079d30] (libc.so)
native: pthread_create+180 [0x40079884] (libc.so)
Also fix running tests on Mac OS, which has no /proc/self/cmdline.
Change-Id: Ib5e6f7e23dd45aecdf814e84f573361a5d91bac6
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index 0703b61..33600ba 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -36,8 +36,13 @@
#include "thread_list.h"
#include "utils.h"
+#if !defined(__APPLE__)
+#define HAVE_PROC_CMDLINE
+#endif
+
namespace art {
+#if defined(HAVE_PROC_CMDLINE)
static bool ReadCmdLine(std::string& result) {
if (!ReadFileToString("/proc/self/cmdline", &result)) {
return false;
@@ -45,6 +50,7 @@
std::replace(result.begin(), result.end(), '\0', ' ');
return true;
}
+#endif
SignalCatcher::SignalCatcher(const std::string& stack_trace_file)
: stack_trace_file_(stack_trace_file),
@@ -53,9 +59,11 @@
thread_(NULL) {
SetHaltFlag(false);
+#if defined(HAVE_PROC_CMDLINE)
// Stash the original command line for SIGQUIT reporting.
// By then, /proc/self/cmdline will have been rewritten to something like "system_server".
CHECK(ReadCmdLine(cmd_line_));
+#endif
// Create a raw pthread; its start routine will attach to the runtime.
CHECK_PTHREAD_CALL(pthread_create, (&pthread_, NULL, &Run, this), "signal catcher thread");
@@ -121,6 +129,7 @@
os << "\n"
<< "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
+#if defined(HAVE_PROC_CMDLINE)
std::string current_cmd_line;
if (ReadCmdLine(current_cmd_line) && current_cmd_line != cmd_line_) {
os << "Cmdline: " << current_cmd_line;
@@ -130,6 +139,7 @@
if (current_cmd_line != cmd_line_) {
os << "Original command line: " << cmd_line_ << "\n";
}
+#endif
os << "Build type: " << (kIsDebugBuild ? "debug" : "optimized") << "\n";
diff --git a/src/thread.cc b/src/thread.cc
index 51b8e31..d854c3b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -438,16 +438,16 @@
name.assign(*name_);
}
-void Thread::DumpState(std::ostream& os) const {
+void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) {
std::string group_name;
int priority;
bool is_daemon = false;
- if (peer_ != NULL) {
- priority = DecodeField(WellKnownClasses::java_lang_Thread_priority)->GetInt(peer_);
- is_daemon = DecodeField(WellKnownClasses::java_lang_Thread_daemon)->GetBoolean(peer_);
+ if (thread != NULL && thread->peer_ != NULL) {
+ priority = DecodeField(WellKnownClasses::java_lang_Thread_priority)->GetInt(thread->peer_);
+ is_daemon = DecodeField(WellKnownClasses::java_lang_Thread_daemon)->GetBoolean(thread->peer_);
- Object* thread_group = GetThreadGroup();
+ Object* thread_group = thread->GetThreadGroup();
if (thread_group != NULL) {
Field* group_name_field = DecodeField(WellKnownClasses::java_lang_ThreadGroup_name);
String* group_name_string = reinterpret_cast<String*>(group_name_field->GetObject(thread_group));
@@ -461,33 +461,49 @@
sched_param sp;
CHECK_PTHREAD_CALL(pthread_getschedparam, (pthread_self(), &policy, &sp), __FUNCTION__);
- std::string scheduler_group_name(GetSchedulerGroupName(GetTid()));
+ std::string scheduler_group_name(GetSchedulerGroupName(tid));
if (scheduler_group_name.empty()) {
scheduler_group_name = "default";
}
- os << '"' << *name_ << '"';
- if (is_daemon) {
- os << " daemon";
+ if (thread != NULL) {
+ os << '"' << *thread->name_ << '"';
+ if (is_daemon) {
+ os << " daemon";
+ }
+ os << " prio=" << priority
+ << " tid=" << thread->GetThinLockId()
+ << " " << thread->GetState() << "\n";
+ } else {
+ std::string thread_name;
+ if (ReadFileToString(StringPrintf("/proc/self/task/%d/comm", tid), &thread_name)) {
+ thread_name.resize(thread_name.size() - 1); // Lose the trailing '\n'.
+ } else {
+ thread_name = "<unknown>";
+ }
+ os << '"' << thread_name << '"'
+ << " prio=" << priority
+ << " tid=?"
+ << " (not attached)\n";
}
- os << " prio=" << priority
- << " tid=" << GetThinLockId()
- << " " << GetState() << "\n";
- os << " | group=\"" << group_name << "\""
- << " sCount=" << suspend_count_
- << " dsCount=" << debug_suspend_count_
- << " obj=" << reinterpret_cast<void*>(peer_)
- << " self=" << reinterpret_cast<const void*>(this) << "\n";
- os << " | sysTid=" << GetTid()
- << " nice=" << getpriority(PRIO_PROCESS, GetTid())
+ if (thread != NULL) {
+ os << " | group=\"" << group_name << "\""
+ << " sCount=" << thread->suspend_count_
+ << " dsCount=" << thread->debug_suspend_count_
+ << " obj=" << reinterpret_cast<void*>(thread->peer_)
+ << " self=" << reinterpret_cast<const void*>(thread) << "\n";
+ }
+ os << " | sysTid=" << tid
+ << " nice=" << getpriority(PRIO_PROCESS, tid)
<< " sched=" << policy << "/" << sp.sched_priority
- << " cgrp=" << scheduler_group_name
- << " handle=" << pthread_self() << "\n";
+ << " cgrp=" << scheduler_group_name << "\n";
+
+ // TODO: fix this; it's never worked in art! << " handle=" << pthread_self() << "\n";
// Grab the scheduler stats for this thread.
std::string scheduler_stats;
- if (ReadFileToString(StringPrintf("/proc/self/task/%d/schedstat", GetTid()), &scheduler_stats)) {
+ if (ReadFileToString(StringPrintf("/proc/self/task/%d/schedstat", tid), &scheduler_stats)) {
scheduler_stats.resize(scheduler_stats.size() - 1); // Lose the trailing '\n'.
} else {
scheduler_stats = "0 0 0";
@@ -496,15 +512,21 @@
int utime = 0;
int stime = 0;
int task_cpu = 0;
- GetTaskStats(GetTid(), utime, stime, task_cpu);
+ GetTaskStats(tid, utime, stime, task_cpu);
os << " | schedstat=( " << scheduler_stats << " )"
<< " utm=" << utime
<< " stm=" << stime
- << " core=" << task_cpu;
- os << " HZ=" << sysconf(_SC_CLK_TCK) << "\n"
- << " | stack=" << reinterpret_cast<void*>(stack_begin_) << "-" << reinterpret_cast<void*>(stack_end_)
- << " stackSize=" << PrettySize(stack_size_) << "\n";
+ << " core=" << task_cpu
+ << " HZ=" << sysconf(_SC_CLK_TCK) << "\n";
+ if (thread != NULL) {
+ os << " | stack=" << reinterpret_cast<void*>(thread->stack_begin_) << "-" << reinterpret_cast<void*>(thread->stack_end_)
+ << " stackSize=" << PrettySize(thread->stack_size_) << "\n";
+ }
+}
+
+void Thread::DumpState(std::ostream& os) const {
+ Thread::DumpState(os, this, GetTid());
}
#if !defined(ART_USE_LLVM_COMPILER)
diff --git a/src/thread.h b/src/thread.h
index f51b581..7d885c0 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -129,6 +129,10 @@
// When full == false, dumps a one-line summary of thread state (used for operator<<).
void Dump(std::ostream& os, bool full = true) const;
+ // Dumps the SIGQUIT per-thread header. 'thread' can be NULL for a non-attached thread, in which
+ // case we use 'tid' to identify the thread, and we'll include as much information as we can.
+ static void DumpState(std::ostream& os, const Thread* thread, pid_t tid);
+
ThreadState GetState() const {
return state_;
}
diff --git a/src/thread_list.cc b/src/thread_list.cc
index e8b412c..3d0e0be 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -16,11 +16,14 @@
#include "thread_list.h"
+#include <dirent.h>
+#include <sys/types.h>
#include <unistd.h>
#include "debugger.h"
#include "scoped_heap_lock.h"
#include "scoped_thread_list_lock.h"
+#include "utils.h"
namespace art {
@@ -50,6 +53,15 @@
return find(list_.begin(), list_.end(), thread) != list_.end();
}
+bool ThreadList::Contains(pid_t tid) {
+ for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
+ if ((*it)->tid_ == tid) {
+ return true;
+ }
+ }
+ return false;
+}
+
pid_t ThreadList::GetLockOwner() {
return thread_list_lock_.GetOwner();
}
@@ -57,6 +69,32 @@
void ThreadList::DumpForSigQuit(std::ostream& os) {
ScopedThreadListLock thread_list_lock;
DumpLocked(os);
+ DumpUnattachedThreads(os);
+}
+
+static void DumpUnattachedThread(std::ostream& os, pid_t tid) {
+ Thread::DumpState(os, NULL, tid);
+ DumpKernelStack(os, tid, " kernel: ", false);
+ DumpNativeStack(os, tid, " native: ", false);
+ os << "\n";
+}
+
+void ThreadList::DumpUnattachedThreads(std::ostream& os) {
+ DIR* d = opendir("/proc/self/task");
+ if (!d) {
+ return;
+ }
+
+ dirent de;
+ dirent* result;
+ while (!readdir_r(d, &de, &result) && result != NULL) {
+ char* end;
+ pid_t tid = strtol(de.d_name, &end, 10);
+ if (!*end && !Contains(tid)) {
+ DumpUnattachedThread(os, tid);
+ }
+ }
+ closedir(d);
}
void ThreadList::DumpLocked(std::ostream& os) {
diff --git a/src/thread_list.h b/src/thread_list.h
index f0b4f6b..9e45bfb 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -64,6 +64,9 @@
void ReleaseThreadId(uint32_t id);
bool Contains(Thread* thread);
+ bool Contains(pid_t tid);
+
+ void DumpUnattachedThreads(std::ostream& os);
bool AllOtherThreadsAreDaemons();
void SuspendAllDaemonThreads();