Stack overflow error and unit test.
Change-Id: Ie7198569207b1b87c50d986df002c551ad5d7d3a
diff --git a/src/asm_support.h b/src/asm_support.h
index 17a29cf..3a8a8ce 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -9,13 +9,13 @@
#define rLR r14
#define SUSPEND_CHECK_INTERVAL (1000)
// Offset of field Thread::top_of_managed_stack_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 333
+#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 341
// Offset of field Thread::top_of_managed_stack_pc_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 337
+#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 345
#elif defined(__i386__)
// Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 0x165
+#define THREAD_SELF_OFFSET 365
#endif
#endif // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 58cd53b..595bc98 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -2084,7 +2084,7 @@
break;
case kArmThrowStackOverflow:
funcOffset =
- OFFSETOF_MEMBER(Thread, pStackOverflowFromCode);
+ OFFSETOF_MEMBER(Thread, pThrowStackOverflowFromCode);
// Restore stack alignment
opRegImm(cUnit, kOpAdd, rSP, cUnit->numSpills * 4);
break;
diff --git a/src/runtime_support.S b/src/runtime_support.S
index 0e57592..185cdc2 100644
--- a/src/runtime_support.S
+++ b/src/runtime_support.S
@@ -58,6 +58,15 @@
mov r3, sp @ pass SP
b artThrowArrayBoundsFromCode @ artThrowArrayBoundsFromCode(index, limit, Thread*, SP)
+ .global art_throw_stack_overflow_from_code
+ .extern artThrowStackOverflowFromCode
+art_throw_stack_overflow_from_code:
+ stmdb sp!, {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, lr}
+ sub sp, #16 @ 4 words of space, bottom word will hold Method*
+ mov r1, r9 @ pass Thread::Current
+ mov r2, sp @ pass SP
+ b artThrowStackOverflowFromCode @ artThrowStackOverflowFromCode(method, Thread*, SP)
+
.global art_invoke_interface_trampoline
.extern artFindInterfaceMethodInCacheFromCode
/*
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 734ba0b..de2a975 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -16,6 +16,7 @@
extern "C" void art_throw_array_bounds_from_code(int32_t index, int32_t limit);
extern "C" void art_throw_div_zero_from_code();
extern "C" void art_throw_null_pointer_exception_from_code();
+ extern "C" void art_throw_stack_overflow_from_code(void*);
extern "C" void art_unlock_object_from_code(void*, void*);
/* Conversions */
diff --git a/src/thread.cc b/src/thread.cc
index 989f56d..57bc09b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -128,12 +128,17 @@
thread->DeliverException();
}
-// TODO: placeholder
-void StackOverflowFromCode(Method* method) {
- Thread::Current()->SetTopOfStackPC(reinterpret_cast<uintptr_t>(__builtin_return_address(0)));
- Thread::Current()->Dump(std::cerr);
- //NOTE: to save code space, this handler needs to look up its own Thread*
- UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
+extern "C" void artThrowStackOverflowFromCode(Method* method, Thread* thread, Method** sp) {
+ // Place a special frame at the TOS that will save all callee saves
+ Runtime* runtime = Runtime::Current();
+ *sp = runtime->GetCalleeSaveMethod();
+ thread->SetTopOfStack(sp, 0);
+ thread->SetStackEndForStackOverflow();
+ thread->ThrowNewException("Ljava/lang/StackOverflowError;",
+ "stack size %zdkb; default stack size: %zdkb",
+ thread->GetStackSize() / KB, runtime->GetDefaultStackSize() / KB);
+ thread->ResetDefaultStackEnd();
+ thread->DeliverException();
}
// TODO: placeholder
@@ -359,6 +364,7 @@
pThrowArrayBoundsFromCode = art_throw_array_bounds_from_code;
pThrowDivZeroFromCode = art_throw_div_zero_from_code;
pThrowNullPointerFromCode = art_throw_null_pointer_exception_from_code;
+ pThrowStackOverflowFromCode = art_throw_stack_overflow_from_code;
pUnlockObjectFromCode = art_unlock_object_from_code;
#endif
pDeliverException = art_deliver_exception_from_code;
@@ -381,7 +387,6 @@
pLockObjectFromCode = LockObjectFromCode;
pFindInstanceFieldFromCode = Field::FindInstanceFieldFromCode;
pCheckSuspendFromCode = artCheckSuspendFromCode;
- pStackOverflowFromCode = StackOverflowFromCode;
pThrowVerificationErrorFromCode = ThrowVerificationErrorFromCode;
pThrowNegArraySizeFromCode = ThrowNegArraySizeFromCode;
pThrowRuntimeExceptionFromCode = ThrowRuntimeExceptionFromCode;
@@ -573,18 +578,17 @@
pthread_attr_t attributes;
CHECK_PTHREAD_CALL(pthread_getattr_np, (pthread_, &attributes), __FUNCTION__);
- void* stack_base;
- size_t stack_size;
- CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, &stack_base, &stack_size), __FUNCTION__);
+ void* temp_stack_base;
+ CHECK_PTHREAD_CALL(pthread_attr_getstack, (&attributes, &temp_stack_base, &stack_size_),
+ __FUNCTION__);
+ stack_base_ = reinterpret_cast<byte*>(temp_stack_base);
- if (stack_size <= kStackOverflowReservedBytes) {
- LOG(FATAL) << "attempt to attach a thread with a too-small stack (" << stack_size << " bytes)";
+ if (stack_size_ <= kStackOverflowReservedBytes) {
+ LOG(FATAL) << "attempt to attach a thread with a too-small stack (" << stack_size_ << " bytes)";
}
- // stack_base is the "lowest addressable byte" of the stack.
- // Our stacks grow down, so we want stack_end_ to be near there, but reserving enough room
- // to throw a StackOverflowError.
- stack_end_ = reinterpret_cast<byte*>(stack_base) + kStackOverflowReservedBytes;
+ // Set stack_end_ to the bottom of the stack saving space of stack overflows
+ ResetDefaultStackEnd();
// Sanity check.
int stack_variable;
diff --git a/src/thread.h b/src/thread.h
index a33d361..39d984b 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -167,7 +167,8 @@
kSuspended = 9, // suspended, usually by GC or debugger
};
- static const size_t kStackOverflowReservedBytes = 1024; // Space to throw a StackOverflowError in.
+ // Space to throw a StackOverflowError in.
+ static const size_t kStackOverflowReservedBytes = 3 * KB;
static const size_t kDefaultStackSize = 64 * KB;
@@ -225,7 +226,7 @@
Field* (*pFindInstanceFieldFromCode)(uint32_t, const Method*);
void (*pCheckSuspendFromCode)(Thread*);
void (*pTestSuspendFromCode)();
- void (*pStackOverflowFromCode)(Method*);
+ void (*pThrowStackOverflowFromCode)(void*);
void (*pThrowNullPointerFromCode)();
void (*pThrowArrayBoundsFromCode)(int32_t, int32_t);
void (*pThrowDivZeroFromCode)();
@@ -489,6 +490,26 @@
return ThreadOffset(OFFSETOF_VOLATILE_MEMBER(Thread, state_));
}
+ // Size of stack less any space reserved for stack overflow
+ size_t GetStackSize() {
+ return stack_size_ - (stack_end_ - stack_base_);
+ }
+
+ // Set the stack end to that to be used during a stack overflow
+ void SetStackEndForStackOverflow() {
+ // During stack overflow we allow use of the full stack
+ CHECK(stack_end_ != stack_base_) << "Need to increase: kStackOverflowReservedBytes ("
+ << kStackOverflowReservedBytes << ")";
+ stack_end_ = stack_base_;
+ }
+
+ // Set the stack end to that to be used during regular execution
+ void ResetDefaultStackEnd() {
+ // Our stacks grow down, so we want stack_end_ to be near there, but reserving enough room
+ // to throw a StackOverflowError.
+ stack_end_ = stack_base_ + kStackOverflowReservedBytes;
+ }
+
static ThreadOffset StackEndOffset() {
return ThreadOffset(OFFSETOF_MEMBER(Thread, stack_end_));
}
@@ -577,6 +598,12 @@
// We leave extra space so there's room for the code that throws StackOverflowError.
byte* stack_end_;
+ // Size of the stack
+ size_t stack_size_;
+
+ // The "lowest addressable byte" of the stack
+ byte* stack_base_;
+
// Top of the managed stack, written out prior to the state transition from
// kRunnable to kNative. Uses include to give the starting point for scanning
// a managed stack when a thread is in native code.
diff --git a/test/IntMath/IntMath.java b/test/IntMath/IntMath.java
index c43d09e..e5fa806 100644
--- a/test/IntMath/IntMath.java
+++ b/test/IntMath/IntMath.java
@@ -786,6 +786,25 @@
return res;
}
+ static long recursion_count_;
+ static void throwStackOverflow(long l) {
+ recursion_count_++;
+ throwStackOverflow(recursion_count_);
+ }
+
+ static long testStackOverflow() {
+ try {
+ throwStackOverflow(0);
+ if (recursion_count_ != 0) {
+ return recursion_count_;
+ } else {
+ return -1;
+ }
+ } catch(StackOverflowError soe) {
+ return 0;
+ }
+ }
+
public static void main(String[] args) {
boolean failure = false;
int res;
@@ -947,6 +966,14 @@
failure = true;
}
+ lres= testStackOverflow();
+ if (lres == 0) {
+ System.out.println("testStackOverflow PASSED");
+ } else {
+ System.out.println("testStackOverflow FAILED: " + lres);
+ failure = true;
+ }
+
res = manyArgs(0, 1L, 2, 3L, 4, 5L, 6, 7, 8.0, 9.0f, 10.0,
(short)11, 12, (char)13, 14, 15, (byte)-16, true, 18,
19, 20L, 21L, 22, 23, 24, 25, 26);