ART: Speed up thread stack tracing for ANRs
Share a BacktraceMap for all threads being dumped, which speeds up
ANR dumping. Results from Nexus 9 (average of five):
Before: 0.587s
After: 0.206s
Change-Id: Ia70e0dbd39049318c02de561e7b95258d4849467
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 6c459a3..556ba56 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -326,7 +326,7 @@
if (self == nullptr) {
os << "(Aborting thread was not attached to runtime!)\n";
DumpKernelStack(os, GetTid(), " kernel: ", false);
- DumpNativeStack(os, GetTid(), " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
} else {
os << "Aborting thread:\n";
if (Locks::mutator_lock_->IsExclusiveHeld(self) || Locks::mutator_lock_->IsSharedHeld(self)) {
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index f0b3c4e..122dcb1 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -41,7 +41,7 @@
public:
explicit Backtrace(void* raw_context) : raw_context_(raw_context) {}
void Dump(std::ostream& os) const {
- DumpNativeStack(os, GetTid(), "\t", nullptr, raw_context_);
+ DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_);
}
private:
// Stores the context of the signal that was unexpected and will terminate the runtime. The
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 114e0f6..b0cf418 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -919,9 +919,9 @@
<< "]";
}
-void Thread::Dump(std::ostream& os) const {
+void Thread::Dump(std::ostream& os, BacktraceMap* backtrace_map) const {
DumpState(os);
- DumpStack(os);
+ DumpStack(os, backtrace_map);
}
mirror::String* Thread::GetThreadName(const ScopedObjectAccessAlreadyRunnable& soa) const {
@@ -1480,7 +1480,7 @@
}
}
-void Thread::DumpStack(std::ostream& os) const {
+void Thread::DumpStack(std::ostream& os, BacktraceMap* backtrace_map) const {
// TODO: we call this code when dying but may not have suspended the thread ourself. The
// IsSuspended check is therefore racy with the use for dumping (normally we inhibit
// the race with the thread_suspend_count_lock_).
@@ -1496,7 +1496,7 @@
if (dump_for_abort || ShouldShowNativeStack(this)) {
DumpKernelStack(os, GetTid(), " kernel: ", false);
ArtMethod* method = GetCurrentMethod(nullptr, !dump_for_abort);
- DumpNativeStack(os, GetTid(), " native: ", method);
+ DumpNativeStack(os, GetTid(), backtrace_map, " native: ", method);
}
DumpJavaStack(os);
} else {
diff --git a/runtime/thread.h b/runtime/thread.h
index 8f3461a..138c143 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -42,6 +42,8 @@
#include "stack.h"
#include "thread_state.h"
+class BacktraceMap;
+
namespace art {
namespace gc {
@@ -184,7 +186,7 @@
void ShortDump(std::ostream& os) const;
// Dumps the detailed thread state and the thread stack (used for SIGQUIT).
- void Dump(std::ostream& os) const
+ void Dump(std::ostream& os, BacktraceMap* backtrace_map = nullptr) const
REQUIRES(!Locks::thread_suspend_count_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1042,7 +1044,7 @@
void VerifyStackImpl() SHARED_REQUIRES(Locks::mutator_lock_);
void DumpState(std::ostream& os) const SHARED_REQUIRES(Locks::mutator_lock_);
- void DumpStack(std::ostream& os) const
+ void DumpStack(std::ostream& os, BacktraceMap* backtrace_map = nullptr) const
REQUIRES(!Locks::thread_suspend_count_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 6176acd..bdd5d10 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -18,6 +18,7 @@
#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include <backtrace/BacktraceMap.h>
#include <cutils/trace.h>
#include <dirent.h>
#include <ScopedLocalRef.h>
@@ -109,9 +110,10 @@
void ThreadList::DumpNativeStacks(std::ostream& os) {
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
for (const auto& thread : list_) {
os << "DUMPING THREAD " << thread->GetTid() << "\n";
- DumpNativeStack(os, thread->GetTid(), "\t");
+ DumpNativeStack(os, thread->GetTid(), map.get(), "\t");
os << "\n";
}
}
@@ -138,7 +140,7 @@
// TODO: Reenable this when the native code in system_server can handle it.
// Currently "adb shell kill -3 `pid system_server`" will cause it to exit.
if (false) {
- DumpNativeStack(os, tid, " native: ");
+ DumpNativeStack(os, tid, nullptr, " native: ");
}
os << "\n";
}
@@ -175,7 +177,8 @@
// A closure used by Thread::Dump.
class DumpCheckpoint FINAL : public Closure {
public:
- explicit DumpCheckpoint(std::ostream* os) : os_(os), barrier_(0) {}
+ explicit DumpCheckpoint(std::ostream* os)
+ : os_(os), barrier_(0), backtrace_map_(BacktraceMap::Create(GetTid())) {}
void Run(Thread* thread) OVERRIDE {
// Note thread and self may not be equal if thread was already suspended at the point of the
@@ -184,7 +187,7 @@
std::ostringstream local_os;
{
ScopedObjectAccess soa(self);
- thread->Dump(local_os);
+ thread->Dump(local_os, backtrace_map_.get());
}
local_os << "\n";
{
@@ -213,6 +216,8 @@
std::ostream* const os_;
// The barrier to be passed through and for the requestor to wait upon.
Barrier barrier_;
+ // A backtrace map, so that all threads use a shared info and don't reacquire/parse separately.
+ std::unique_ptr<BacktraceMap> backtrace_map_;
};
void ThreadList::Dump(std::ostream& os) {
@@ -1217,7 +1222,7 @@
std::string thread_name;
self->GetThreadName(thread_name);
std::ostringstream os;
- DumpNativeStack(os, GetTid(), " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
LOG(ERROR) << "Request to unregister unattached thread " << thread_name << "\n" << os.str();
break;
} else {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 62af380..dee4f9c 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -46,7 +46,9 @@
#include <sys/syscall.h>
#endif
-#include <backtrace/Backtrace.h> // For DumpNativeStack.
+// For DumpNativeStack.
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
#if defined(__linux__)
#include <linux/unistd.h>
@@ -1102,7 +1104,7 @@
}
#endif
-void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix,
+void DumpNativeStack(std::ostream& os, pid_t tid, BacktraceMap* existing_map, const char* prefix,
ArtMethod* current_method, void* ucontext_ptr) {
#if __linux__
// b/18119146
@@ -1110,7 +1112,13 @@
return;
}
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+ BacktraceMap* map = existing_map;
+ std::unique_ptr<BacktraceMap> tmp_map;
+ if (map == nullptr) {
+ tmp_map.reset(BacktraceMap::Create(tid));
+ map = tmp_map.get();
+ }
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map));
if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";
return;
@@ -1174,7 +1182,7 @@
}
}
#else
- UNUSED(os, tid, prefix, current_method, ucontext_ptr);
+ UNUSED(os, tid, existing_map, prefix, current_method, ucontext_ptr);
#endif
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 79502c7..bd52b68 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -31,6 +31,8 @@
#include "globals.h"
#include "primitive.h"
+class BacktraceMap;
+
namespace art {
class ArtCode;
@@ -221,12 +223,19 @@
void SetThreadName(const char* thread_name);
// Dumps the native stack for thread 'tid' to 'os'.
-void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "",
- ArtMethod* current_method = nullptr, void* ucontext = nullptr)
+void DumpNativeStack(std::ostream& os,
+ pid_t tid,
+ BacktraceMap* map = nullptr,
+ const char* prefix = "",
+ ArtMethod* current_method = nullptr,
+ void* ucontext = nullptr)
NO_THREAD_SAFETY_ANALYSIS;
// Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86.
-void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true);
+void DumpKernelStack(std::ostream& os,
+ pid_t tid,
+ const char* prefix = "",
+ bool include_count = true);
// Find $ANDROID_ROOT, /system, or abort.
const char* GetAndroidRoot();