Add the signal catcher (for SIGQUIT and SIGUSR1).
Also fix some of the thread implementation.
Change-Id: If2d1b59a149ba1ac192ad9bc74319c8dff228549
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 04a17cf..3907d07 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1920,4 +1920,9 @@
return resolved;
}
+size_t ClassLinker::NumLoadedClasses() const {
+ MutexLock mu(classes_lock_);
+ return classes_.size();
+}
+
} // namespace art
diff --git a/src/class_linker.h b/src/class_linker.h
index cb21cb1..c848825 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -38,6 +38,8 @@
return FindClass(descriptor, NULL);
}
+ size_t NumLoadedClasses() const;
+
// Resolve a String with the given ID from the DexFile, storing the
// result in the DexCache.
String* ResolveString(const DexFile& dex_file,
diff --git a/src/runtime.cc b/src/runtime.cc
index 80914db..2443cc6 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -12,6 +12,7 @@
#include "heap.h"
#include "jni_internal.h"
#include "scoped_ptr.h"
+#include "signal_catcher.h"
#include "thread.h"
namespace art {
@@ -22,6 +23,7 @@
// TODO: use smart pointers instead. (we'll need the pimpl idiom.)
delete class_linker_;
Heap::Destroy();
+ delete signal_catcher_;
delete thread_list_;
delete java_vm_;
Thread::Shutdown();
@@ -321,6 +323,8 @@
LoadJniLibrary(instance_->GetJavaVM(), "javacore");
self->SetState(old_state);
+ instance_->signal_catcher_ = new SignalCatcher;
+
return instance_;
}
@@ -360,6 +364,18 @@
return true;
}
+void Runtime::DumpStatistics(std::ostream& os) {
+ // TODO: dump other runtime statistics?
+ os << "Loaded classes: " << class_linker_->NumLoadedClasses() << "\n";
+ os << "Intern table size: " << class_linker_->GetInternTable().Size() << "\n";
+ // LOGV("VM stats: meth=%d ifld=%d sfld=%d linear=%d",
+ // gDvm.numDeclaredMethods,
+ // gDvm.numDeclaredInstFields,
+ // gDvm.numDeclaredStaticFields,
+ // gDvm.pBootLoaderAlloc->curOffset);
+ // LOGI("GC precise methods: %d", dvmPointerSetGetCount(gDvm.preciseMethods));
+}
+
void Runtime::BlockSignals() {
sigset_t sigset;
if (sigemptyset(&sigset) == -1) {
diff --git a/src/runtime.h b/src/runtime.h
index b6873e3..eaf8ace 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -5,6 +5,7 @@
#include <stdio.h>
+#include <iosfwd>
#include <string>
#include <utility>
#include <vector>
@@ -23,6 +24,7 @@
class DexFile;
class Heap;
class JavaVMExt;
+class SignalCatcher;
class String;
class ThreadList;
@@ -76,6 +78,8 @@
// Detaches the current native thread from the runtime.
bool DetachCurrentThread();
+ void DumpStatistics(std::ostream& os);
+
~Runtime();
size_t GetStackSize() const {
@@ -106,6 +110,8 @@
ClassLinker* class_linker_;
+ SignalCatcher* signal_catcher_;
+
JavaVMExt* java_vm_;
// Hooks supported by JNI_CreateJavaVM
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
new file mode 100644
index 0000000..e6e423d
--- /dev/null
+++ b/src/signal_catcher.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "signal_catcher.h"
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "heap.h"
+#include "thread.h"
+#include "utils.h"
+
+namespace art {
+
+bool SignalCatcher::halt_ = false;
+
+SignalCatcher::SignalCatcher() {
+ // Create a raw pthread; its start routine will attach to the runtime.
+ errno = pthread_create(&thread_, NULL, &Run, NULL);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_create failed for signal catcher thread";
+ }
+}
+
+SignalCatcher::~SignalCatcher() {
+ // Since we know the thread is just sitting around waiting for signals
+ // to arrive, send it one.
+ halt_ = true;
+ pthread_kill(thread_, SIGQUIT);
+ pthread_join(thread_, NULL);
+}
+
+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";
+
+ Runtime::Current()->DumpStatistics(buffer);
+
+ // TODO: dump all threads.
+ // dvmDumpAllThreadsEx(&target, true);
+
+ buffer << "/proc/self/maps:\n" << ReadFileToString("/proc/self/maps");
+ buffer << "----- end " << getpid() << " -----";
+
+ // TODO: resume all threads
+
+ LOG(INFO) << buffer.str();
+}
+
+void SignalCatcher::HandleSigUsr1() {
+ LOG(INFO) << "SIGUSR1 forcing GC (no HPROF)";
+ Heap::CollectGarbage();
+}
+
+void* SignalCatcher::Run(void*) {
+ CHECK(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);
+ sigaddset(&mask, SIGQUIT);
+ sigaddset(&mask, SIGUSR1);
+
+ while (true) {
+ self->SetState(Thread::kWaiting); // TODO: VMWAIT
+
+ // Signals for sigwait() must be blocked but not ignored. We
+ // block signals like SIGQUIT for all threads, so the condition
+ // is met. When the signal hits, we wake up, without any signal
+ // handlers being invoked.
+
+ // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
+ int signal_number;
+ int rc = TEMP_FAILURE_RETRY(sigwait(&mask, &signal_number));
+ if (rc != 0) {
+ PLOG(FATAL) << "sigwait failed";
+ }
+
+ if (!halt_) {
+ LOG(INFO) << *self << ": reacting to signal " << signal_number;
+ }
+
+ // Set our status to runnable, self-suspending if GC in progress.
+ self->SetState(Thread::kRunnable);
+
+ if (halt_) {
+ return NULL;
+ }
+
+ switch (signal_number) {
+ case SIGQUIT:
+ HandleSigQuit();
+ break;
+ case SIGUSR1:
+ HandleSigUsr1();
+ break;
+ default:
+ LOG(ERROR) << "Unexpected signal %d" << signal_number;
+ break;
+ }
+ }
+}
+
+} // namespace art
diff --git a/src/signal_catcher.h b/src/signal_catcher.h
new file mode 100644
index 0000000..1c08542
--- /dev/null
+++ b/src/signal_catcher.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_SIGNAL_CATCHER_H_
+#define ART_SRC_SIGNAL_CATCHER_H_
+
+#include <pthread.h>
+
+namespace art {
+
+class Runtime;
+class Thread;
+
+/*
+ * A thread that catches signals and does something useful. For
+ * example, when a SIGQUIT (Ctrl-\) arrives, we suspend and dump the
+ * status of all threads.
+ */
+class SignalCatcher {
+ public:
+ SignalCatcher();
+ ~SignalCatcher();
+
+ private:
+ static void* Run(void* arg);
+ static void HandleSigQuit();
+ static void HandleSigUsr1();
+
+ static bool halt_;
+ pthread_t thread_;
+};
+
+} // namespace art
+
+#endif // ART_SRC_SIGNAL_CATCHER_H_
diff --git a/src/thread.cc b/src/thread.cc
index 40aaa7d..2f43e23 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -18,6 +18,18 @@
namespace art {
+/* desktop Linux needs a little help with gettid() */
+#if !defined(HAVE_ANDROID_OS)
+#define __KERNEL__
+# include <linux/unistd.h>
+#ifdef _syscall0
+_syscall0(pid_t, gettid)
+#else
+pid_t gettid() { return syscall(__NR_gettid);}
+#endif
+#undef __KERNEL__
+#endif
+
pthread_key_t Thread::pthread_key_self_;
void Thread::InitFunctionPointers() {
@@ -126,33 +138,35 @@
Thread* Thread::Create(const Runtime* runtime) {
size_t stack_size = runtime->GetStackSize();
- scoped_ptr<MemMap> stack(MemMap::Map(stack_size, PROT_READ | PROT_WRITE));
- if (stack == NULL) {
- LOG(FATAL) << "failed to allocate thread stack";
- // notreached
- return NULL;
- }
Thread* new_thread = new Thread;
new_thread->InitCpu();
- new_thread->stack_.reset(stack.release());
- // Since stacks are assumed to grown downward the base is the limit and the limit is the base.
- new_thread->stack_limit_ = stack->GetAddress();
- new_thread->stack_base_ = stack->GetLimit();
pthread_attr_t attr;
- int result = pthread_attr_init(&attr);
- CHECK_EQ(result, 0);
+ errno = pthread_attr_init(&attr);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_attr_init failed";
+ }
- result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- CHECK_EQ(result, 0);
+ errno = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_attr_setdetachstate(PTHREAD_CREATE_DETACHED) failed";
+ }
- pthread_t handle;
- result = pthread_create(&handle, &attr, ThreadStart, new_thread);
- CHECK_EQ(result, 0);
+ errno = pthread_attr_setstacksize(&attr, stack_size);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_attr_setstacksize(" << stack_size << ") failed";
+ }
- result = pthread_attr_destroy(&attr);
- CHECK_EQ(result, 0);
+ errno = pthread_create(&new_thread->handle_, &attr, ThreadStart, new_thread);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_create failed";
+ }
+
+ errno = pthread_attr_destroy(&attr);
+ if (errno != 0) {
+ PLOG(FATAL) << "pthread_attr_destroy failed";
+ }
return new_thread;
}
@@ -160,11 +174,6 @@
Thread* Thread::Attach(const Runtime* runtime) {
Thread* thread = new Thread;
thread->InitCpu();
- thread->stack_limit_ = reinterpret_cast<byte*>(-1); // TODO: getrlimit
- uintptr_t addr = reinterpret_cast<uintptr_t>(&thread); // TODO: ask pthreads
- uintptr_t stack_base = RoundUp(addr, kPageSize);
- thread->stack_base_ = reinterpret_cast<byte*>(stack_base);
- // TODO: set the stack size
thread->handle_ = pthread_self();
@@ -180,6 +189,10 @@
return thread;
}
+pid_t Thread::GetTid() const {
+ return gettid();
+}
+
static void ThreadExitCheck(void* arg) {
LG << "Thread exit check";
}
@@ -401,9 +414,10 @@
std::ostream& operator<<(std::ostream& os, const Thread& thread) {
os << "Thread[" << &thread
- << ",id=" << thread.GetId()
- << ",tid=" << thread.GetNativeId()
- << ",state=" << thread.GetState() << "]";
+ << ",pthread_t=" << thread.GetImpl()
+ << ",tid=" << thread.GetTid()
+ << ",id=" << thread.GetId()
+ << ",state=" << thread.GetState() << "]";
return os;
}
diff --git a/src/thread.h b/src/thread.h
index ac1d7e3..e8ca156 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -225,8 +225,10 @@
return id_;
}
- pid_t GetNativeId() const {
- return native_id_;
+ pid_t GetTid() const;
+
+ pthread_t GetImpl() const {
+ return handle_;
}
bool IsExceptionPending() const {
@@ -428,9 +430,6 @@
State state_;
- // Native (kernel) thread id.
- pid_t native_id_;
-
// Native thread handle.
pthread_t handle_;
@@ -452,15 +451,6 @@
// useful for testing.
const ClassLoader* class_loader_override_;
- // The memory mapping of the stack for non-attached threads.
- scoped_ptr<MemMap> stack_;
-
- // The inclusive base of the control stack.
- byte* stack_base_;
-
- // The exclusive limit of the control stack.
- byte* stack_limit_;
-
// TLS key used to retrieve the VM thread object.
static pthread_key_t pthread_key_self_;
diff --git a/src/utils.cc b/src/utils.cc
index fd30a36..84b1556 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -25,6 +25,15 @@
return contents;
}
+std::string GetIsoDate() {
+ time_t now = time(NULL);
+ struct tm tmbuf;
+ struct tm* ptm = localtime_r(&now, &tmbuf);
+ return StringPrintf("%04d-%02d-%02d %02d:%02d:%02d",
+ ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
+ ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+}
+
std::string PrettyDescriptor(const String* java_descriptor) {
std::string descriptor(java_descriptor->ToModifiedUtf8());
diff --git a/src/utils.h b/src/utils.h
index e52f476..b5210ec 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -163,6 +163,9 @@
std::string ReadFileToString(const char* file_name);
+// Returns the current date in ISO yyyy-mm-dd hh:mm:ss format.
+std::string GetIsoDate();
+
} // namespace art
#endif // ART_SRC_UTILS_H_