Create stack traces in unstarted runtimes.
Use to diagnose failed initialization in dex2oat of boot (-verbose:compiler).
Fix identity hashCode, ArtMethod.getMethodName, IntegralToString.convertInt and
use of Void when called from a unstarted runtime.
Change-Id: I2d536174b59e2e5f19519f93fc6b5916652fb6cd
diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h
index 1acbbb0..c324d96 100644
--- a/runtime/mirror/stack_trace_element.h
+++ b/runtime/mirror/stack_trace_element.h
@@ -57,6 +57,10 @@
static void ResetClass();
static void VisitRoots(RootCallback* callback, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static Class* GetStackTraceElement() {
+ DCHECK(java_lang_StackTraceElement_ != NULL);
+ return java_lang_StackTraceElement_;
+ }
private:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
@@ -70,11 +74,6 @@
SirtRef<String>& file_name, int32_t line_number)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static Class* GetStackTraceElement() {
- DCHECK(java_lang_StackTraceElement_ != NULL);
- return java_lang_StackTraceElement_;
- }
-
static Class* java_lang_StackTraceElement_;
friend struct art::StackTraceElementOffsets; // for verifying offset information
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index d393a13..6874fe5 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -24,6 +24,7 @@
#include "object_array.h"
#include "object_array-inl.h"
#include "object_utils.h"
+#include "stack_trace_element.h"
#include "utils.h"
#include "well_known_classes.h"
@@ -53,6 +54,15 @@
}
}
+void Throwable::SetStackState(Object* state) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ CHECK(state != nullptr);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObjectVolatile<true>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_), state);
+ } else {
+ SetFieldObjectVolatile<false>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_), state);
+ }
+}
+
bool Throwable::IsCheckedException() {
if (InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error))) {
return false;
@@ -70,24 +80,49 @@
result += "\n";
Object* stack_state = GetStackState();
// check stack state isn't missing or corrupt
- if (stack_state != NULL && stack_state->IsObjectArray()) {
+ if (stack_state != nullptr && stack_state->IsObjectArray()) {
// Decode the internal stack trace into the depth and method trace
ObjectArray<Object>* method_trace = down_cast<ObjectArray<Object>*>(stack_state);
int32_t depth = method_trace->GetLength() - 1;
IntArray* pc_trace = down_cast<IntArray*>(method_trace->Get(depth));
MethodHelper mh;
- for (int32_t i = 0; i < depth; ++i) {
- ArtMethod* method = down_cast<ArtMethod*>(method_trace->Get(i));
- mh.ChangeMethod(method);
- uint32_t dex_pc = pc_trace->Get(i);
- int32_t line_number = mh.GetLineNumFromDexPC(dex_pc);
- const char* source_file = mh.GetDeclaringClassSourceFile();
- result += StringPrintf(" at %s (%s:%d)\n", PrettyMethod(method, true).c_str(),
- source_file, line_number);
+ if (depth == 0) {
+ result += "(Throwable with empty stack trace)";
+ } else {
+ for (int32_t i = 0; i < depth; ++i) {
+ ArtMethod* method = down_cast<ArtMethod*>(method_trace->Get(i));
+ mh.ChangeMethod(method);
+ uint32_t dex_pc = pc_trace->Get(i);
+ int32_t line_number = mh.GetLineNumFromDexPC(dex_pc);
+ const char* source_file = mh.GetDeclaringClassSourceFile();
+ result += StringPrintf(" at %s (%s:%d)\n", PrettyMethod(method, true).c_str(),
+ source_file, line_number);
+ }
+ }
+ } else {
+ Object* stack_trace = GetStackTrace();
+ if (stack_trace != nullptr && stack_trace->IsObjectArray()) {
+ CHECK_EQ(stack_trace->GetClass()->GetComponentType(),
+ StackTraceElement::GetStackTraceElement());
+ ObjectArray<StackTraceElement>* ste_array =
+ down_cast<ObjectArray<StackTraceElement>*>(stack_trace);
+ if (ste_array->GetLength() == 0) {
+ result += "(Throwable with empty stack trace)";
+ } else {
+ for (int32_t i = 0; i < ste_array->GetLength(); ++i) {
+ StackTraceElement* ste = ste_array->Get(i);
+ result += StringPrintf(" at %s (%s:%d)\n",
+ ste->GetMethodName()->ToModifiedUtf8().c_str(),
+ ste->GetFileName()->ToModifiedUtf8().c_str(),
+ ste->GetLineNumber());
+ }
+ }
+ } else {
+ result += "(Throwable with no stack trace)";
}
}
Throwable* cause = GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(Throwable, cause_));
- if (cause != NULL && cause != this) { // Constructor makes cause == this by default.
+ if (cause != nullptr && cause != this) { // Constructor makes cause == this by default.
result += "Caused by: ";
result += cause->Dump();
}
diff --git a/runtime/mirror/throwable.h b/runtime/mirror/throwable.h
index 950b5e7..c4127e0 100644
--- a/runtime/mirror/throwable.h
+++ b/runtime/mirror/throwable.h
@@ -42,6 +42,7 @@
// overridden. Also it asserts rather than throwing exceptions. Currently this is only used
// in cases like the verifier where the checks cannot fail and initCause isn't overridden.
void SetCause(Throwable* cause) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetStackState(Object* state) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsCheckedException() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Class* GetJavaLangThrowable() {
@@ -58,6 +59,9 @@
Object* GetStackState() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_state_));
}
+ Object* GetStackTrace() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetFieldObjectVolatile<Object>(OFFSET_OF_OBJECT_MEMBER(Throwable, stack_trace_));
+ }
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
HeapReference<Throwable> cause_;