Include the thread name in Thread's operator<< output.
For example:
Thread[2,tid=1692,VmWait,Thread*=0x416914d0,"Signal Catcher"]: reacting to signal 3
Change-Id: I2b00e82af2ad0f09141dbf4e418bc0582e9b966d
diff --git a/src/dalvik_system_VMDebug.cc b/src/dalvik_system_VMDebug.cc
index c388e62..5f4b6d9 100644
--- a/src/dalvik_system_VMDebug.cc
+++ b/src/dalvik_system_VMDebug.cc
@@ -224,10 +224,7 @@
* for seeing both interpreted and native stack traces.
*/
void VMDebug_crash(JNIEnv*, jclass) {
- std::ostringstream os;
- os << "Crashing VM on request:\n";
- Thread::Current()->Dump(os);
- LOG(FATAL) << os.str();
+ LOG(FATAL) << "Crashing VM on request";
}
/*
diff --git a/src/debugger.cc b/src/debugger.cc
index 751efa1..f1e6066 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -1088,7 +1088,7 @@
if (thread == NULL) {
return false;
}
- StringAppendF(&name, "<%d> %s", thread->GetThinLockId(), thread->GetName()->ToModifiedUtf8().c_str());
+ StringAppendF(&name, "<%d> %s", thread->GetThinLockId(), thread->GetThreadName()->ToModifiedUtf8().c_str());
return true;
}
@@ -1873,7 +1873,7 @@
Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
} else {
CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
- SirtRef<String> name(t->GetName());
+ SirtRef<String> name(t->GetThreadName());
size_t char_count = (name.get() != NULL) ? name->GetLength() : 0;
const jchar* chars = name->GetCharArray()->GetData();
diff --git a/src/java_lang_Thread.cc b/src/java_lang_Thread.cc
index 46b6241..0338454 100644
--- a/src/java_lang_Thread.cc
+++ b/src/java_lang_Thread.cc
@@ -75,13 +75,17 @@
}
}
-void Thread_nativeSetName(JNIEnv* env, jobject javaThread, jstring) {
+void Thread_nativeSetName(JNIEnv* env, jobject javaThread, jstring javaName) {
ScopedThreadListLock thread_list_lock;
Thread* thread = Thread::FromManagedThread(env, javaThread);
if (thread == NULL) {
return;
}
- Dbg::DdmSendThreadNotification(thread, CHUNK_TYPE("THNM"));
+ ScopedUtfChars name(env, javaName);
+ if (name.c_str() == NULL) {
+ return;
+ }
+ thread->SetThreadName(name.c_str());
}
/*
diff --git a/src/monitor_android.cc b/src/monitor_android.cc
index f80d248..c1949ec 100644
--- a/src/monitor_android.cc
+++ b/src/monitor_android.cc
@@ -69,7 +69,7 @@
cp = EventLogWriteInt(cp, Monitor::IsSensitiveThread());
// Emit self thread name string, <= 37 bytes.
- std::string thread_name(self->GetName()->ToModifiedUtf8());
+ std::string thread_name(self->GetThreadName()->ToModifiedUtf8());
cp = EventLogWriteString(cp, thread_name.c_str(), thread_name.size());
// Emit the wait time, 5 bytes.
diff --git a/src/runtime.cc b/src/runtime.cc
index 114c538..fcbcc08 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -97,7 +97,11 @@
if (self == NULL) {
os << "(Aborting thread was not attached to runtime!)\n";
} else {
- self->Dump(os, true);
+ self->Dump(os);
+ if (self->IsExceptionPending()) {
+ os << "Pending " << PrettyTypeOf(self->GetException()) << " on thread:\n"
+ << self->GetException()->Dump();
+ }
}
}
};
diff --git a/src/thread.cc b/src/thread.cc
index 189e1b9..7722866 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -173,8 +173,8 @@
{
CHECK_EQ(self->GetState(), Thread::kRunnable);
- SirtRef<String> thread_name(self->GetName());
- SetThreadName(thread_name->ToModifiedUtf8().c_str());
+ SirtRef<String> thread_name(self->GetThreadName());
+ self->SetThreadName(thread_name->ToModifiedUtf8().c_str());
}
Dbg::PostThreadStart(self);
@@ -315,7 +315,7 @@
CHECK(!IsExceptionPending()) << " " << PrettyTypeOf(GetException());
SetVmData(peer_, Thread::Current());
- SirtRef<String> peer_thread_name(GetName());
+ SirtRef<String> peer_thread_name(GetThreadName());
if (peer_thread_name.get() == NULL) {
// The Thread constructor should have set the Thread.name to a
// non-null value. However, because we can run without code
@@ -325,11 +325,11 @@
gThread_group->SetObject(peer_, Decode<Object*>(env, thread_group.get()));
gThread_name->SetObject(peer_, Decode<Object*>(env, thread_name.get()));
gThread_priority->SetInt(peer_, thread_priority);
- peer_thread_name.reset(GetName());
+ peer_thread_name.reset(GetThreadName());
}
// thread_name may have been null, so don't trust this to be non-null
if (peer_thread_name.get() != NULL) {
- SetThreadName(GetName()->ToModifiedUtf8().c_str());
+ SetThreadName(peer_thread_name->ToModifiedUtf8().c_str());
}
// Pre-allocate an OutOfMemoryError for the double-OOME case.
@@ -340,6 +340,12 @@
pre_allocated_OutOfMemoryError_ = Decode<Throwable*>(env, exception.get());
}
+void Thread::SetThreadName(const char* name) {
+ name_->assign(name);
+ ::art::SetThreadName(name);
+ Dbg::DdmSendThreadNotification(this, CHUNK_TYPE("THNM"));
+}
+
void Thread::InitStackHwm() {
#if defined(__APPLE__)
// Only needed to run code. Try implementing this with pthread_get_stacksize_np and pthread_get_stackaddr_np.
@@ -368,12 +374,21 @@
#endif
}
-void Thread::Dump(std::ostream& os, bool dump_pending_exception) const {
- DumpState(os);
- DumpStack(os);
- if (dump_pending_exception && IsExceptionPending()) {
- os << "Pending " << PrettyTypeOf(GetException()) << " on thread:\n";
- os << GetException()->Dump();
+void Thread::Dump(std::ostream& os, bool full) const {
+ if (full) {
+ DumpState(os);
+ DumpStack(os);
+ } else {
+ os << "Thread[";
+ if (GetThinLockId() != 0) {
+ // If we're in kStarting, we won't have a thin lock id or tid yet.
+ os << GetThinLockId()
+ << ",tid=" << GetTid() << ',';
+ }
+ os << GetState()
+ << ",Thread*=" << this
+ << ",\"" << *name_ << "\""
+ << "]";
}
}
@@ -402,19 +417,16 @@
return "";
}
-String* Thread::GetName() const {
+String* Thread::GetThreadName() const {
return (peer_ != NULL) ? reinterpret_cast<String*>(gThread_name->GetObject(peer_)) : NULL;
}
void Thread::DumpState(std::ostream& os) const {
- std::string thread_name("<native thread without managed peer>");
std::string group_name;
int priority;
bool is_daemon = false;
if (peer_ != NULL) {
- String* thread_name_string = reinterpret_cast<String*>(gThread_name->GetObject(peer_));
- thread_name = (thread_name_string != NULL) ? thread_name_string->ToModifiedUtf8() : "<null>";
priority = gThread_priority->GetInt(peer_);
is_daemon = gThread_daemon->GetBoolean(peer_);
@@ -424,13 +436,6 @@
group_name = (group_name_string != NULL) ? group_name_string->ToModifiedUtf8() : "<null>";
}
} else {
- // This name may be truncated, but it's the best we can do in the absence of a managed peer.
- std::string stats;
- if (ReadFileToString(StringPrintf("/proc/self/task/%d/stat", GetTid()).c_str(), &stats)) {
- size_t start = stats.find('(') + 1;
- size_t end = stats.find(')') - start;
- thread_name = stats.substr(start, end);
- }
priority = GetNativePriority();
}
@@ -443,7 +448,7 @@
scheduler_group = "default";
}
- os << '"' << thread_name << '"';
+ os << '"' << *name_ << '"';
if (is_daemon) {
os << " daemon";
}
@@ -801,7 +806,8 @@
throwing_OutOfMemoryError_(false),
pre_allocated_OutOfMemoryError_(NULL),
debug_invoke_req_(new DebugInvokeReq),
- trace_stack_(new std::vector<TraceStackFrame>) {
+ trace_stack_(new std::vector<TraceStackFrame>),
+ name_(new std::string("<native thread without managed peer>")) {
CHECK_EQ((sizeof(Thread) % 4), 0U) << sizeof(Thread);
}
@@ -847,6 +853,7 @@
delete debug_invoke_req_;
delete trace_stack_;
+ delete name_;
}
void Thread::HandleUncaughtExceptions() {
@@ -1608,16 +1615,7 @@
}
std::ostream& operator<<(std::ostream& os, const Thread& thread) {
- os << "Thread[";
- if (thread.GetThinLockId() != 0) {
- // If we're in kStarting, we won't have a thin lock id or tid yet.
- os << thread.GetThinLockId()
- << ",tid=" << thread.GetTid() << ',';
- }
- os << thread.GetState()
- << ",Thread*=" << &thread
- << ",Object*=" << thread.GetPeer()
- << "]";
+ thread.Dump(os, false);
return os;
}
diff --git a/src/thread.h b/src/thread.h
index aee784d..856fd5c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -184,7 +184,9 @@
static Thread* FromManagedThread(JNIEnv* env, jobject thread);
static uint32_t LockOwnerFromThreadLock(Object* thread_lock);
- void Dump(std::ostream& os, bool dump_pending_exception = false) const;
+ // When full == true, dumps the detailed thread state and the thread stack (used for SIGQUIT).
+ // When full == false, dumps a one-line summary of thread state (used for operator<<).
+ void Dump(std::ostream& os, bool full = true) const;
State GetState() const {
return state_;
@@ -231,7 +233,10 @@
}
// Returns the java.lang.Thread's name, or NULL.
- String* GetName() const;
+ String* GetThreadName() const;
+
+ // Sets the thread's name.
+ void SetThreadName(const char* name);
Object* GetPeer() const {
return peer_;
@@ -309,8 +314,6 @@
const DexFile& dex_file,
ClassLinker* class_linker);
- void SetName(const char* name);
-
static void Startup();
static void FinishStartup();
static void Shutdown();
@@ -628,6 +631,9 @@
// Stored as a pointer since std::vector is not PACKED.
std::vector<TraceStackFrame>* trace_stack_;
+ // A cached copy of the java.lang.Thread's name.
+ std::string* name_;
+
DISALLOW_COPY_AND_ASSIGN(Thread);
};
diff --git a/src/trace.cc b/src/trace.cc
index c149634..83f50d3 100644
--- a/src/trace.cc
+++ b/src/trace.cc
@@ -402,7 +402,7 @@
static void DumpThread(Thread* t, void* arg) {
std::ostream* os = reinterpret_cast<std::ostream*>(arg);
- *os << StringPrintf("%d\t%s\n", t->GetTid(), t->GetName()->ToModifiedUtf8().c_str());
+ *os << StringPrintf("%d\t%s\n", t->GetTid(), t->GetThreadName()->ToModifiedUtf8().c_str());
}
void Trace::DumpThreadList(std::ostream& os) {