ART: Reprint long messages on abort
Add an abort message parameter to Runtime::Abort. In case the message
is multiline (and will thus not be completely preserved in the
Android abort reason), reprint the message after all threads have
been dumped.
Bug: 31893081
Test: m test-art-host
Change-Id: I65bc77691fec79f7c868a90d6132805fcc91e473
(cherry picked from commit 90a32b16feb58d4edf932fdcb6a070be3418e365)
diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc
index 212e5bd..df8a369 100644
--- a/runtime/base/logging.cc
+++ b/runtime/base/logging.cc
@@ -193,6 +193,8 @@
}
}
LogMessage::~LogMessage() {
+ std::string msg;
+
if (!PrintDirectly(data_->GetSeverity()) && data_->GetSeverity() != LogSeverity::NONE) {
if (data_->GetSeverity() < gMinimumLogSeverity) {
return; // No need to format something we're not going to output.
@@ -202,7 +204,7 @@
if (data_->GetError() != -1) {
data_->GetBuffer() << ": " << strerror(data_->GetError());
}
- std::string msg(data_->ToString());
+ msg = data_->ToString();
// Do the actual logging with the lock held.
{
@@ -216,6 +218,8 @@
size_t nl = msg.find('\n', i);
msg[nl] = '\0';
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), &msg[i]);
+ // Undo zero-termination, so we retain the complete message.
+ msg[nl] = '\n';
i = nl + 1;
}
}
@@ -224,7 +228,7 @@
// Abort if necessary.
if (data_->GetSeverity() == FATAL) {
- Runtime::Abort();
+ Runtime::Abort(msg.c_str());
}
}
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index f334a05..06d376b 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -129,15 +129,9 @@
DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
if (topIndex == max_entries_) {
- std::ostringstream oss;
- oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")\n"
- << MutatorLockedDumpable<IndirectReferenceTable>(*this);
- if (VLOG_IS_ON(jni)) {
- LOG(FATAL) << oss.str();
- } else {
- LOG_FATAL_THIS_THREAD_ONLY(oss.str());
- }
+ LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow "
+ << "(max=" << max_entries_ << ")\n"
+ << MutatorLockedDumpable<IndirectReferenceTable>(*this);
}
// We know there's enough room in the table. Now we just need to find
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index dd94828..9da0268 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -406,7 +406,7 @@
}
};
-void Runtime::Abort() {
+void Runtime::Abort(const char* msg) {
gAborting++; // set before taking any locks
// Ensure that we don't have multiple threads trying to abort at once,
@@ -421,6 +421,12 @@
AbortState state;
LOG(INTERNAL_FATAL) << Dumpable<AbortState>(state);
+ // Sometimes we dump long messages, and the Android abort message only retains the first line.
+ // In those cases, just log the message again, to avoid logcat limits.
+ if (msg != nullptr && strchr(msg, '\n') != nullptr) {
+ LOG(INTERNAL_FATAL) << msg;
+ }
+
// Call the abort hook if we have one.
if (Runtime::Current() != nullptr && Runtime::Current()->abort_ != nullptr) {
LOG(INTERNAL_FATAL) << "Calling abort hook...";
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 38680aa..3b72aa7 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -228,7 +228,7 @@
// Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most
// callers should prefer.
- NO_RETURN static void Abort() REQUIRES(!Locks::abort_lock_);
+ NO_RETURN static void Abort(const char* msg) REQUIRES(!Locks::abort_lock_);
// Returns the "main" ThreadGroup, used when attaching user threads.
jobject GetMainThreadGroup() const;