More SIGQUIT handling, various Thread fixes.
Change-Id: I3233c300d1c838c2eee0ba9be6018b7fbd907386
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 8a30279..dc58565 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -917,7 +917,7 @@
* to "running" mode before doing the checks.
*/
void checkInstance(InstanceKind kind, jobject java_object) {
- const char* what;
+ const char* what = NULL;
switch (kind) {
case kClass:
what = "jclass";
diff --git a/src/dex_file.cc b/src/dex_file.cc
index c8e7c70..ad0b3b9 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -215,7 +215,7 @@
// Note that current_thread can be NULL if we're parsing the bootclasspath
// during JNI_CreateJavaVM.
Thread* current_thread = Thread::Current();
- Thread::State old;
+ Thread::State old(Thread::kUnknown);
if (current_thread != NULL) {
old = current_thread->SetState(Thread::kNative);
}
diff --git a/src/image_test.cc b/src/image_test.cc
index fd88b20..b205fe2 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -74,7 +74,8 @@
// enable to display maps to debug boot_base and boot_limit checking problems below
if (false) {
const char* maps_file = "/proc/self/maps";
- std::string contents = ReadFileToString(maps_file);
+ std::string contents;
+ CHECK(ReadFileToString(maps_file, &contents));
LG << maps_file << ":\n" << contents;
}
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 4cd809e..a7c7dc7 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -490,7 +490,8 @@
CHECK_GE(args.version, JNI_VERSION_1_2);
Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
- return runtime->AttachCurrentThread(args.name, p_env, as_daemon) ? JNI_OK : JNI_ERR;
+ runtime->AttachCurrentThread(args.name, p_env, as_daemon);
+ return JNI_OK;
}
class SharedLibrary {
diff --git a/src/runtime.cc b/src/runtime.cc
index 3934571..bb3ead7 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -335,8 +335,8 @@
if (!Thread::Startup()) {
return false;
}
- Thread* current_thread = Thread::Attach(this);
- thread_list_->Register(current_thread);
+
+ thread_list_->Register(Thread::Attach(this));
class_linker_ = ClassLinker::Create(options->boot_class_path_, intern_table_, Heap::GetBootSpace());
@@ -420,18 +420,18 @@
CHECK_EQ(sigprocmask(SIG_BLOCK, &sigset, NULL), 0);
}
-bool Runtime::AttachCurrentThread(const char* name, JNIEnv** penv, bool as_daemon) {
+void Runtime::AttachCurrentThread(const char* name, JNIEnv** penv, bool as_daemon) {
if (as_daemon) {
UNIMPLEMENTED(WARNING) << "TODO: do something different for daemon threads";
}
- return Thread::Attach(instance_) != NULL;
+ Thread* t = Thread::Attach(instance_);
+ thread_list_->Register(t);
}
-bool Runtime::DetachCurrentThread() {
+void Runtime::DetachCurrentThread() {
Thread* self = Thread::Current();
thread_list_->Unregister(self);
delete self;
- return true;
}
void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
diff --git a/src/runtime.h b/src/runtime.h
index 4ae15bf..4e00ec1 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -79,12 +79,12 @@
static void Abort(const char* file, int line);
// Attaches the current native thread to the runtime.
- bool AttachCurrentThread(const char* name, JNIEnv** jni_env, bool as_daemon);
+ void AttachCurrentThread(const char* name, JNIEnv** jni_env, bool as_daemon);
void CallExitHook(jint status);
// Detaches the current native thread from the runtime.
- bool DetachCurrentThread();
+ void DetachCurrentThread();
void DumpStatistics(std::ostream& os);
@@ -106,6 +106,10 @@
return java_vm_;
}
+ ThreadList* GetThreadList() const {
+ return thread_list_;
+ }
+
void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
private:
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index 5979a0f..e2e2360 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -50,23 +50,31 @@
void SignalCatcher::HandleSigQuit() {
// TODO: suspend all threads
- std::stringstream buffer;
- buffer << "\n"
- << "\n"
- << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n"
- << "Cmd line: " << ReadFileToString("/proc/self/cmdline") << "\n";
+ std::stringstream os;
+ os << "\n"
+ << "\n"
+ << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
- Runtime::Current()->DumpStatistics(buffer);
+ std::string cmdline;
+ if (ReadFileToString("/proc/self/cmdline", &cmdline)) {
+ std::replace(cmdline.begin(), cmdline.end(), '\0', ' ');
+ os << "Cmd line: " << cmdline << "\n";
+ }
- // TODO: dump all threads.
- // dvmDumpAllThreadsEx(&target, true);
+ Runtime* runtime = Runtime::Current();
+ runtime->DumpStatistics(os);
+ runtime->GetThreadList()->Dump(os);
- buffer << "/proc/self/maps:\n" << ReadFileToString("/proc/self/maps");
- buffer << "----- end " << getpid() << " -----";
+ std::string maps;
+ if (ReadFileToString("/proc/self/maps", &maps)) {
+ os << "/proc/self/maps:\n" << maps;
+ }
+
+ os << "----- end " << getpid() << " -----";
// TODO: resume all threads
- LOG(INFO) << buffer.str();
+ LOG(INFO) << os.str();
}
void SignalCatcher::HandleSigUsr1() {
@@ -93,12 +101,10 @@
}
void* SignalCatcher::Run(void*) {
- CHECK(Runtime::Current()->AttachCurrentThread("Signal Catcher", NULL, true));
+ Runtime::Current()->AttachCurrentThread("Signal Catcher", NULL, true);
Thread* self = Thread::Current();
CHECK(self != NULL);
- LOG(INFO) << "Signal catcher thread started " << *self;
-
// Set up mask with signals we want to handle.
sigset_t mask;
sigemptyset(&mask);
@@ -108,6 +114,7 @@
while (true) {
int signal_number = WaitForSignal(self, mask);
if (halt_) {
+ Runtime::Current()->DetachCurrentThread();
return NULL;
}
diff --git a/src/thread.cc b/src/thread.cc
index 7f48fa1..c0e986c 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -206,6 +206,7 @@
thread->InitCpu();
thread->handle_ = pthread_self();
+ thread->tid_ = gettid();
thread->state_ = kRunnable;
@@ -220,11 +221,151 @@
}
void Thread::Dump(std::ostream& os) const {
- os << "UNIMPLEMENTED: Thread::Dump\n";
+ /*
+ * Get the java.lang.Thread object. This function gets called from
+ * some weird debug contexts, so it's possible that there's a GC in
+ * progress on some other thread. To decrease the chances of the
+ * thread object being moved out from under us, we add the reference
+ * to the tracked allocation list, which pins it in place.
+ *
+ * If threadObj is NULL, the thread is still in the process of being
+ * attached to the VM, and there's really nothing interesting to
+ * say about it yet.
+ */
+ os << "TODO: pin Thread before dumping\n";
+#if 0
+ if (java_thread_ == NULL) {
+ LOGI("Can't dump thread %d: threadObj not set", threadId);
+ return;
+ }
+ dvmAddTrackedAlloc(java_thread_, NULL);
+#endif
+
+ DumpState(os);
+ DumpStack(os);
+
+#if 0
+ dvmReleaseTrackedAlloc(java_thread_, NULL);
+#endif
}
-pid_t Thread::GetTid() const {
- return gettid();
+std::string GetSchedulerGroup(pid_t tid) {
+ // /proc/<pid>/group looks like this:
+ // 2:devices:/
+ // 1:cpuacct,cpu:/
+ // We want the third field from the line whose second field contains the "cpu" token.
+ std::string cgroup_file;
+ if (!ReadFileToString("/proc/self/cgroup", &cgroup_file)) {
+ return "";
+ }
+ std::vector<std::string> cgroup_lines;
+ Split(cgroup_file, '\n', cgroup_lines);
+ for (size_t i = 0; i < cgroup_lines.size(); ++i) {
+ std::vector<std::string> cgroup_fields;
+ Split(cgroup_lines[i], ':', cgroup_fields);
+ std::vector<std::string> cgroups;
+ Split(cgroup_fields[1], ',', cgroups);
+ for (size_t i = 0; i < cgroups.size(); ++i) {
+ if (cgroups[i] == "cpu") {
+ return cgroup_fields[2].substr(1); // Skip the leading slash.
+ }
+ }
+ }
+ return "";
+}
+
+void Thread::DumpState(std::ostream& os) const {
+ std::string thread_name("unknown");
+ int priority = -1;
+ bool is_daemon = false;
+#if 0 // TODO
+ nameStr = (StringObject*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
+ threadName = dvmCreateCstrFromString(nameStr);
+ priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
+ is_daemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
+#else
+ thread_name = "TODO";
+ priority = -1;
+ is_daemon = false;
+#endif
+
+ int policy;
+ sched_param sp;
+ errno = pthread_getschedparam(handle_, &policy, &sp);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_getschedparam failed";
+ }
+
+ std::string scheduler_group(GetSchedulerGroup(GetTid()));
+ if (scheduler_group.empty()) {
+ scheduler_group = "default";
+ }
+
+ std::string group_name("(null; initializing?)");
+#if 0
+ groupObj = (Object*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
+ if (groupObj != NULL) {
+ nameStr = (StringObject*) dvmGetFieldObject(groupObj, gDvm.offJavaLangThreadGroup_name);
+ groupName = dvmCreateCstrFromString(nameStr);
+ }
+#else
+ group_name = "TODO";
+#endif
+
+ os << '"' << thread_name << '"';
+ if (is_daemon) {
+ os << " daemon";
+ }
+ os << " prio=" << priority
+ << " tid=" << GetId()
+ << " " << state_ << "\n";
+
+ int suspend_count = 0; // TODO
+ int debug_suspend_count = 0; // TODO
+ void* java_thread_ = NULL; // TODO
+ os << " | group=\"" << group_name << "\""
+ << " sCount=" << suspend_count
+ << " dsCount=" << debug_suspend_count
+ << " obj=" << reinterpret_cast<void*>(java_thread_)
+ << " self=" << reinterpret_cast<const void*>(this) << "\n";
+ os << " | sysTid=" << GetTid()
+ << " nice=" << getpriority(PRIO_PROCESS, GetTid())
+ << " sched=" << policy << "/" << sp.sched_priority
+ << " cgrp=" << scheduler_group
+ << " handle=" << GetImpl() << "\n";
+
+ // Grab the scheduler stats for this thread.
+ std::string scheduler_stats;
+ if (ReadFileToString(StringPrintf("/proc/self/task/%d/schedstat", GetTid()).c_str(), &scheduler_stats)) {
+ scheduler_stats.resize(scheduler_stats.size() - 1); // Lose the trailing '\n'.
+ } else {
+ scheduler_stats = "0 0 0";
+ }
+
+ int utime = 0;
+ int stime = 0;
+ int task_cpu = 0;
+ std::string stats;
+ if (ReadFileToString(StringPrintf("/proc/self/task/%d/stat", GetTid()).c_str(), &stats)) {
+ // Skip the command, which may contain spaces.
+ stats = stats.substr(stats.find(')') + 2);
+ // Extract the three fields we care about.
+ std::vector<std::string> fields;
+ Split(stats, ' ', fields);
+ utime = strtoull(fields[11].c_str(), NULL, 10);
+ stime = strtoull(fields[12].c_str(), NULL, 10);
+ task_cpu = strtoull(fields[36].c_str(), NULL, 10);
+ }
+
+ os << " | schedstat=( " << scheduler_stats << " )"
+ << " utm=" << utime
+ << " stm=" << stime
+ << " core=" << task_cpu
+ << " HZ=" << sysconf(_SC_CLK_TCK) << "\n";
+}
+
+void Thread::DumpStack(std::ostream& os) const {
+ os << "UNIMPLEMENTED: Thread::DumpStack\n";
}
static void ThreadExitCheck(void* arg) {
@@ -589,13 +730,24 @@
return find(list_.begin(), list_.end(), thread) != list_.end();
}
+void ThreadList::Dump(std::ostream& os) {
+ MutexLock mu(lock_);
+ os << "DALVIK THREADS (" << list_.size() << "):\n";
+ typedef std::list<Thread*>::const_iterator It; // TODO: C++0x auto
+ for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
+ (*it)->Dump(os);
+ }
+}
+
void ThreadList::Register(Thread* thread) {
+ //LOG(INFO) << "ThreadList::Register() " << *thread;
MutexLock mu(lock_);
CHECK(!Contains(thread));
list_.push_front(thread);
}
void ThreadList::Unregister(Thread* thread) {
+ //LOG(INFO) << "ThreadList::Unregister() " << *thread;
MutexLock mu(lock_);
CHECK(Contains(thread));
list_.remove(thread);
diff --git a/src/thread.h b/src/thread.h
index 87cfb6c..2e2444f 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -256,7 +256,9 @@
return id_;
}
- pid_t GetTid() const;
+ pid_t GetTid() const {
+ return tid_;
+ }
pthread_t GetImpl() const {
return handle_;
@@ -442,6 +444,9 @@
~Thread();
friend class Runtime; // For ~Thread.
+ void DumpState(std::ostream& os) const;
+ void DumpStack(std::ostream& os) const;
+
void InitCpu();
void InitFunctionPointers();
@@ -450,6 +455,12 @@
// Managed thread id.
uint32_t id_;
+ // System thread id.
+ pid_t tid_;
+
+ // Native thread handle.
+ pthread_t handle_;
+
// FIXME: placeholder for the gc cardTable
uint32_t card_table_;
@@ -470,9 +481,6 @@
State state_;
- // Native thread handle.
- pthread_t handle_;
-
// Initialized to "this". On certain architectures (such as x86) reading
// off of Thread::Current is easy but getting the address of Thread::Current
// is hard. This field can be read off of Thread::Current to give the address.
@@ -515,6 +523,8 @@
~ThreadList();
+ void Dump(std::ostream& os);
+
void Register(Thread* thread);
void Unregister(Thread* thread);
diff --git a/src/utils.cc b/src/utils.cc
index f957580..3be3fa6 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -9,21 +9,23 @@
namespace art {
-std::string ReadFileToString(const char* file_name) {
- UniquePtr<File> file(OS::OpenFile(file_name, false));
- CHECK(file.get() != NULL);
+bool ReadFileToString(const std::string& file_name, std::string* result) {
+ UniquePtr<File> file(OS::OpenFile(file_name.c_str(), false));
+ if (file.get() == NULL) {
+ return false;
+ }
- std::string contents;
char buf[8 * KB];
while (true) {
int64_t n = file->Read(buf, sizeof(buf));
- CHECK_NE(-1, n);
- if (n == 0) {
- break;
+ if (n == -1) {
+ return false;
}
- contents.append(buf, n);
+ if (n == 0) {
+ return true;
+ }
+ result->append(buf, n);
}
- return contents;
}
std::string GetIsoDate() {
diff --git a/src/utils.h b/src/utils.h
index 3a6f404..4c2a7f9 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -168,7 +168,7 @@
// Returns the JNI native function name for the overloaded method 'm'.
std::string JniLongName(const Method* m);
-std::string ReadFileToString(const char* file_name);
+bool ReadFileToString(const std::string& file_name, std::string* result);
// Returns the current date in ISO yyyy-mm-dd hh:mm:ss format.
std::string GetIsoDate();