mirror of
https://github.com/corda/corda.git
synced 2025-03-02 12:39:20 +00:00
check for stack overflow on entry to all non-leaf methods
We now check for stack overflow in the JIT build as well as the interpreted build, throwing a StackOverflowError if the limit (currently hard-coded to 64KB, but should be easy to make configurable) is exceeded.
This commit is contained in:
parent
cac2d2cac5
commit
d18240cbd6
@ -30,7 +30,7 @@ const unsigned HeapCapacity = 768 * 1024 * 1024;
|
|||||||
// One of the advantages of a bootimage-based build is that reduces
|
// One of the advantages of a bootimage-based build is that reduces
|
||||||
// the overhead of major GCs at runtime since we can avoid scanning
|
// the overhead of major GCs at runtime since we can avoid scanning
|
||||||
// the pre-built heap image entirely. However, this only works if we
|
// the pre-built heap image entirely. However, this only works if we
|
||||||
// can ensure that no part of the heap image (with an exception noted
|
// can ensure that no part of the heap image (with exceptions noted
|
||||||
// below) ever points to runtime-allocated objects. Therefore (most)
|
// below) ever points to runtime-allocated objects. Therefore (most)
|
||||||
// references in the heap image are considered immutable, and any
|
// references in the heap image are considered immutable, and any
|
||||||
// attempt to update them at runtime will cause the process to abort.
|
// attempt to update them at runtime will cause the process to abort.
|
||||||
|
@ -45,6 +45,7 @@ class BootImage {
|
|||||||
Thunk defaultVirtual;
|
Thunk defaultVirtual;
|
||||||
Thunk native;
|
Thunk native;
|
||||||
Thunk aioob;
|
Thunk aioob;
|
||||||
|
Thunk stackOverflow;
|
||||||
Thunk table;
|
Thunk table;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,6 +76,7 @@ class BootImage {
|
|||||||
unsigned compileVirtualMethodCall;
|
unsigned compileVirtualMethodCall;
|
||||||
unsigned invokeNativeCall;
|
unsigned invokeNativeCall;
|
||||||
unsigned throwArrayIndexOutOfBoundsCall;
|
unsigned throwArrayIndexOutOfBoundsCall;
|
||||||
|
unsigned throwStackOverflowCall;
|
||||||
|
|
||||||
#define THUNK(s) unsigned s##Call;
|
#define THUNK(s) unsigned s##Call;
|
||||||
#include "thunks.cpp"
|
#include "thunks.cpp"
|
||||||
|
@ -225,7 +225,8 @@ class MyThread: public Thread {
|
|||||||
? parent->arch
|
? parent->arch
|
||||||
: makeArchitecture(m->system, useNativeFeatures)),
|
: makeArchitecture(m->system, useNativeFeatures)),
|
||||||
transition(0),
|
transition(0),
|
||||||
traceContext(0)
|
traceContext(0),
|
||||||
|
stackLimit(0)
|
||||||
{
|
{
|
||||||
arch->acquire();
|
arch->acquire();
|
||||||
}
|
}
|
||||||
@ -245,6 +246,7 @@ class MyThread: public Thread {
|
|||||||
Assembler::Architecture* arch;
|
Assembler::Architecture* arch;
|
||||||
Context* transition;
|
Context* transition;
|
||||||
TraceContext* traceContext;
|
TraceContext* traceContext;
|
||||||
|
uintptr_t stackLimit;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1118,6 +1120,7 @@ class Context {
|
|||||||
objectPoolCount(0),
|
objectPoolCount(0),
|
||||||
traceLogCount(0),
|
traceLogCount(0),
|
||||||
dirtyRoots(false),
|
dirtyRoots(false),
|
||||||
|
leaf(true),
|
||||||
eventLog(t->m->system, t->m->heap, 1024),
|
eventLog(t->m->system, t->m->heap, 1024),
|
||||||
protector(this)
|
protector(this)
|
||||||
{ }
|
{ }
|
||||||
@ -1139,6 +1142,7 @@ class Context {
|
|||||||
objectPoolCount(0),
|
objectPoolCount(0),
|
||||||
traceLogCount(0),
|
traceLogCount(0),
|
||||||
dirtyRoots(false),
|
dirtyRoots(false),
|
||||||
|
leaf(true),
|
||||||
eventLog(t->m->system, t->m->heap, 0),
|
eventLog(t->m->system, t->m->heap, 0),
|
||||||
protector(this)
|
protector(this)
|
||||||
{ }
|
{ }
|
||||||
@ -1164,6 +1168,7 @@ class Context {
|
|||||||
unsigned objectPoolCount;
|
unsigned objectPoolCount;
|
||||||
unsigned traceLogCount;
|
unsigned traceLogCount;
|
||||||
bool dirtyRoots;
|
bool dirtyRoots;
|
||||||
|
bool leaf;
|
||||||
Vector eventLog;
|
Vector eventLog;
|
||||||
MyProtector protector;
|
MyProtector protector;
|
||||||
};
|
};
|
||||||
@ -2099,6 +2104,9 @@ bootNativeThunk(MyThread* t);
|
|||||||
uintptr_t
|
uintptr_t
|
||||||
aioobThunk(MyThread* t);
|
aioobThunk(MyThread* t);
|
||||||
|
|
||||||
|
uintptr_t
|
||||||
|
stackOverflowThunk(MyThread* t);
|
||||||
|
|
||||||
uintptr_t
|
uintptr_t
|
||||||
virtualThunk(MyThread* t, unsigned index);
|
virtualThunk(MyThread* t, unsigned index);
|
||||||
|
|
||||||
@ -2611,6 +2619,14 @@ throwArrayIndexOutOfBounds(MyThread* t)
|
|||||||
unwind(t);
|
unwind(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NO_RETURN
|
||||||
|
throwStackOverflow(MyThread* t)
|
||||||
|
{
|
||||||
|
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||||
|
|
||||||
|
unwind(t);
|
||||||
|
}
|
||||||
|
|
||||||
void NO_RETURN
|
void NO_RETURN
|
||||||
throw_(MyThread* t, object o)
|
throw_(MyThread* t, object o)
|
||||||
{
|
{
|
||||||
@ -4121,6 +4137,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case invokeinterface: {
|
case invokeinterface: {
|
||||||
|
context->leaf = false;
|
||||||
|
|
||||||
uint16_t index = codeReadInt16(t, code, ip);
|
uint16_t index = codeReadInt16(t, code, ip);
|
||||||
ip += 2;
|
ip += 2;
|
||||||
|
|
||||||
@ -4162,6 +4180,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case invokespecial: {
|
case invokespecial: {
|
||||||
|
context->leaf = false;
|
||||||
|
|
||||||
uint16_t index = codeReadInt16(t, code, ip);
|
uint16_t index = codeReadInt16(t, code, ip);
|
||||||
object target = resolveMethod(t, context->method, index - 1);
|
object target = resolveMethod(t, context->method, index - 1);
|
||||||
if (UNLIKELY(t->exception)) return;
|
if (UNLIKELY(t->exception)) return;
|
||||||
@ -4179,6 +4199,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case invokestatic: {
|
case invokestatic: {
|
||||||
|
context->leaf = false;
|
||||||
|
|
||||||
uint16_t index = codeReadInt16(t, code, ip);
|
uint16_t index = codeReadInt16(t, code, ip);
|
||||||
|
|
||||||
object target = resolveMethod(t, context->method, index - 1);
|
object target = resolveMethod(t, context->method, index - 1);
|
||||||
@ -4193,6 +4215,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case invokevirtual: {
|
case invokevirtual: {
|
||||||
|
context->leaf = false;
|
||||||
|
|
||||||
uint16_t index = codeReadInt16(t, code, ip);
|
uint16_t index = codeReadInt16(t, code, ip);
|
||||||
|
|
||||||
object target = resolveMethod(t, context->method, index - 1);
|
object target = resolveMethod(t, context->method, index - 1);
|
||||||
@ -5745,7 +5769,9 @@ finish(MyThread* t, Allocator* allocator, Context* context)
|
|||||||
// parallelism (the downside being that it may end up being a waste
|
// parallelism (the downside being that it may end up being a waste
|
||||||
// of cycles if another thread compiles the same method in parallel,
|
// of cycles if another thread compiles the same method in parallel,
|
||||||
// which might be mitigated by fine-grained, per-method locking):
|
// which might be mitigated by fine-grained, per-method locking):
|
||||||
unsigned codeSize = c->compile();
|
unsigned codeSize = c->compile
|
||||||
|
(context->leaf ? 0 : stackOverflowThunk(t),
|
||||||
|
difference(&(t->stackLimit), t));
|
||||||
|
|
||||||
uintptr_t* code = static_cast<uintptr_t*>
|
uintptr_t* code = static_cast<uintptr_t*>
|
||||||
(allocator->allocate(pad(codeSize) + pad(c->poolSize()) + BytesPerWord));
|
(allocator->allocate(pad(codeSize) + pad(c->poolSize()) + BytesPerWord));
|
||||||
@ -7092,6 +7118,15 @@ invoke(Thread* thread, object method, ArgumentList* arguments)
|
|||||||
{
|
{
|
||||||
MyThread* t = static_cast<MyThread*>(thread);
|
MyThread* t = static_cast<MyThread*>(thread);
|
||||||
|
|
||||||
|
uintptr_t stackLimit = t->stackLimit;
|
||||||
|
uintptr_t stackPosition = reinterpret_cast<uintptr_t>(&t);
|
||||||
|
if (stackLimit == 0) {
|
||||||
|
t->stackLimit = stackPosition - StackSizeInBytes;
|
||||||
|
} else if (stackPosition < stackLimit) {
|
||||||
|
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned returnCode = methodReturnCode(t, method);
|
unsigned returnCode = methodReturnCode(t, method);
|
||||||
unsigned returnType = fieldType(t, returnCode);
|
unsigned returnType = fieldType(t, returnCode);
|
||||||
|
|
||||||
@ -7111,6 +7146,8 @@ invoke(Thread* thread, object method, ArgumentList* arguments)
|
|||||||
returnType);
|
returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t->stackLimit = stackLimit;
|
||||||
|
|
||||||
if (t->exception) {
|
if (t->exception) {
|
||||||
if (UNLIKELY(t->flags & Thread::UseBackupHeapFlag)) {
|
if (UNLIKELY(t->flags & Thread::UseBackupHeapFlag)) {
|
||||||
collect(t, Heap::MinorCollection);
|
collect(t, Heap::MinorCollection);
|
||||||
@ -7243,6 +7280,7 @@ class MyProcessor: public Processor {
|
|||||||
Thunk defaultVirtual;
|
Thunk defaultVirtual;
|
||||||
Thunk native;
|
Thunk native;
|
||||||
Thunk aioob;
|
Thunk aioob;
|
||||||
|
Thunk stackOverflow;
|
||||||
Thunk table;
|
Thunk table;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -7801,7 +7839,7 @@ isThunkUnsafeStack(MyProcessor::Thunk* thunk, void* ip)
|
|||||||
bool
|
bool
|
||||||
isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
|
isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
|
||||||
{
|
{
|
||||||
const unsigned NamedThunkCount = 4;
|
const unsigned NamedThunkCount = 5;
|
||||||
|
|
||||||
MyProcessor::Thunk table[NamedThunkCount + ThunkCount];
|
MyProcessor::Thunk table[NamedThunkCount + ThunkCount];
|
||||||
|
|
||||||
@ -7809,6 +7847,7 @@ isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
|
|||||||
table[1] = thunks->defaultVirtual;
|
table[1] = thunks->defaultVirtual;
|
||||||
table[2] = thunks->native;
|
table[2] = thunks->native;
|
||||||
table[3] = thunks->aioob;
|
table[3] = thunks->aioob;
|
||||||
|
table[4] = thunks->stackOverflow;
|
||||||
|
|
||||||
for (unsigned i = 0; i < ThunkCount; ++i) {
|
for (unsigned i = 0; i < ThunkCount; ++i) {
|
||||||
new (table + NamedThunkCount + i) MyProcessor::Thunk
|
new (table + NamedThunkCount + i) MyProcessor::Thunk
|
||||||
@ -8137,6 +8176,8 @@ fixupThunks(MyThread* t, BootImage* image, uint8_t* code)
|
|||||||
= thunkToThunk(image->thunks.defaultVirtual, code);
|
= thunkToThunk(image->thunks.defaultVirtual, code);
|
||||||
p->bootThunks.native = thunkToThunk(image->thunks.native, code);
|
p->bootThunks.native = thunkToThunk(image->thunks.native, code);
|
||||||
p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code);
|
p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code);
|
||||||
|
p->bootThunks.stackOverflow
|
||||||
|
= thunkToThunk(image->thunks.stackOverflow, code);
|
||||||
p->bootThunks.table = thunkToThunk(image->thunks.table, code);
|
p->bootThunks.table = thunkToThunk(image->thunks.table, code);
|
||||||
|
|
||||||
updateCall(t, LongCall, code + image->compileMethodCall,
|
updateCall(t, LongCall, code + image->compileMethodCall,
|
||||||
@ -8151,6 +8192,9 @@ fixupThunks(MyThread* t, BootImage* image, uint8_t* code)
|
|||||||
updateCall(t, LongCall, code + image->throwArrayIndexOutOfBoundsCall,
|
updateCall(t, LongCall, code + image->throwArrayIndexOutOfBoundsCall,
|
||||||
voidPointer(throwArrayIndexOutOfBounds));
|
voidPointer(throwArrayIndexOutOfBounds));
|
||||||
|
|
||||||
|
updateCall(t, LongCall, code + image->throwStackOverflowCall,
|
||||||
|
voidPointer(throwStackOverflow));
|
||||||
|
|
||||||
#define THUNK(s) \
|
#define THUNK(s) \
|
||||||
updateCall(t, LongJump, code + image->s##Call, voidPointer(s));
|
updateCall(t, LongJump, code + image->s##Call, voidPointer(s));
|
||||||
|
|
||||||
@ -8391,6 +8435,23 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p)
|
|||||||
p->thunks.aioob.length = a->endBlock(false)->resolve(0, 0);
|
p->thunks.aioob.length = a->endBlock(false)->resolve(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThunkContext stackOverflowContext(t, &zone);
|
||||||
|
|
||||||
|
{ Assembler* a = stackOverflowContext.context.assembler;
|
||||||
|
|
||||||
|
a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t));
|
||||||
|
|
||||||
|
p->thunks.stackOverflow.frameSavedOffset = a->length();
|
||||||
|
|
||||||
|
Assembler::Register thread(t->arch->thread());
|
||||||
|
a->pushFrame(1, BytesPerWord, RegisterOperand, &thread);
|
||||||
|
|
||||||
|
Assembler::Constant proc(&(stackOverflowContext.promise));
|
||||||
|
a->apply(LongCall, BytesPerWord, ConstantOperand, &proc);
|
||||||
|
|
||||||
|
p->thunks.stackOverflow.length = a->endBlock(false)->resolve(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
ThunkContext tableContext(t, &zone);
|
ThunkContext tableContext(t, &zone);
|
||||||
|
|
||||||
{ Assembler* a = tableContext.context.assembler;
|
{ Assembler* a = tableContext.context.assembler;
|
||||||
@ -8463,6 +8524,21 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p->thunks.stackOverflow.start = finish
|
||||||
|
(t, allocator, stackOverflowContext.context.assembler, "stackOverflow",
|
||||||
|
p->thunks.stackOverflow.length);
|
||||||
|
|
||||||
|
{ void* call;
|
||||||
|
stackOverflowContext.promise.listener->resolve
|
||||||
|
(reinterpret_cast<intptr_t>(voidPointer(throwStackOverflow)),
|
||||||
|
&call);
|
||||||
|
|
||||||
|
if (image) {
|
||||||
|
image->throwStackOverflowCall
|
||||||
|
= static_cast<uint8_t*>(call) - imageBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p->thunks.table.start = static_cast<uint8_t*>
|
p->thunks.table.start = static_cast<uint8_t*>
|
||||||
(allocator->allocate(p->thunks.table.length * ThunkCount));
|
(allocator->allocate(p->thunks.table.length * ThunkCount));
|
||||||
|
|
||||||
@ -8472,6 +8548,8 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p)
|
|||||||
= thunkToThunk(p->thunks.defaultVirtual, imageBase);
|
= thunkToThunk(p->thunks.defaultVirtual, imageBase);
|
||||||
image->thunks.native = thunkToThunk(p->thunks.native, imageBase);
|
image->thunks.native = thunkToThunk(p->thunks.native, imageBase);
|
||||||
image->thunks.aioob = thunkToThunk(p->thunks.aioob, imageBase);
|
image->thunks.aioob = thunkToThunk(p->thunks.aioob, imageBase);
|
||||||
|
image->thunks.stackOverflow
|
||||||
|
= thunkToThunk(p->thunks.stackOverflow, imageBase);
|
||||||
image->thunks.table = thunkToThunk(p->thunks.table, imageBase);
|
image->thunks.table = thunkToThunk(p->thunks.table, imageBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8539,6 +8617,12 @@ aioobThunk(MyThread* t)
|
|||||||
return reinterpret_cast<uintptr_t>(processor(t)->thunks.aioob.start);
|
return reinterpret_cast<uintptr_t>(processor(t)->thunks.aioob.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uintptr_t
|
||||||
|
stackOverflowThunk(MyThread* t)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<uintptr_t>(processor(t)->thunks.stackOverflow.start);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
unresolved(MyThread* t, uintptr_t methodAddress)
|
unresolved(MyThread* t, uintptr_t methodAddress)
|
||||||
{
|
{
|
||||||
|
@ -4919,8 +4919,7 @@ class BoundsCheckEvent: public Event {
|
|||||||
lengthOffset, NoRegister, 1);
|
lengthOffset, NoRegister, 1);
|
||||||
length.acquired = true;
|
length.acquired = true;
|
||||||
|
|
||||||
CodePromise* nextPromise = codePromise
|
CodePromise* nextPromise = codePromise(c, static_cast<Promise*>(0));
|
||||||
(c, static_cast<Promise*>(0));
|
|
||||||
|
|
||||||
freezeSource(c, BytesPerWord, index);
|
freezeSource(c, BytesPerWord, index);
|
||||||
|
|
||||||
@ -5649,7 +5648,7 @@ block(Context* c, Event* head)
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
compile(Context* c)
|
compile(Context* c, uintptr_t stackOverflowHandler, unsigned stackLimitOffset)
|
||||||
{
|
{
|
||||||
if (c->logicalCode[c->logicalIp]->lastEvent == 0) {
|
if (c->logicalCode[c->logicalIp]->lastEvent == 0) {
|
||||||
appendDummy(c);
|
appendDummy(c);
|
||||||
@ -5660,6 +5659,15 @@ compile(Context* c)
|
|||||||
Block* firstBlock = block(c, c->firstEvent);
|
Block* firstBlock = block(c, c->firstEvent);
|
||||||
Block* block = firstBlock;
|
Block* block = firstBlock;
|
||||||
|
|
||||||
|
if (stackOverflowHandler) {
|
||||||
|
Assembler::Register stack(c->arch->stack());
|
||||||
|
Assembler::Memory stackLimit(c->arch->thread(), stackLimitOffset);
|
||||||
|
Assembler::Constant handler(resolved(c, stackOverflowHandler));
|
||||||
|
a->apply(JumpIfGreaterOrEqual, BytesPerWord, RegisterOperand, &stack,
|
||||||
|
BytesPerWord, MemoryOperand, &stackLimit,
|
||||||
|
BytesPerWord, ConstantOperand, &handler);
|
||||||
|
}
|
||||||
|
|
||||||
a->allocateFrame(c->alignedFrameSize);
|
a->allocateFrame(c->alignedFrameSize);
|
||||||
|
|
||||||
for (Event* e = c->firstEvent; e; e = e->next) {
|
for (Event* e = c->firstEvent; e; e = e->next) {
|
||||||
@ -6406,8 +6414,8 @@ class MyCompiler: public Compiler {
|
|||||||
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
virtual void checkBounds(Operand* object, unsigned lengthOffset,
|
||||||
Operand* index, intptr_t handler)
|
Operand* index, intptr_t handler)
|
||||||
{
|
{
|
||||||
appendBoundsCheck(&c, static_cast<Value*>(object),
|
appendBoundsCheck(&c, static_cast<Value*>(object), lengthOffset,
|
||||||
lengthOffset, static_cast<Value*>(index), handler);
|
static_cast<Value*>(index), handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void store(unsigned srcSize, Operand* src, unsigned dstSize,
|
virtual void store(unsigned srcSize, Operand* src, unsigned dstSize,
|
||||||
@ -6827,8 +6835,11 @@ class MyCompiler: public Compiler {
|
|||||||
appendBarrier(&c, StoreLoadBarrier);
|
appendBarrier(&c, StoreLoadBarrier);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual unsigned compile() {
|
virtual unsigned compile(uintptr_t stackOverflowHandler,
|
||||||
return c.machineCodeSize = local::compile(&c);
|
unsigned stackLimitOffset)
|
||||||
|
{
|
||||||
|
return c.machineCodeSize = local::compile
|
||||||
|
(&c, stackOverflowHandler, stackLimitOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual unsigned poolSize() {
|
virtual unsigned poolSize() {
|
||||||
|
@ -188,7 +188,8 @@ class Compiler {
|
|||||||
virtual void storeStoreBarrier() = 0;
|
virtual void storeStoreBarrier() = 0;
|
||||||
virtual void storeLoadBarrier() = 0;
|
virtual void storeLoadBarrier() = 0;
|
||||||
|
|
||||||
virtual unsigned compile() = 0;
|
virtual unsigned compile(uintptr_t stackOverflowHandler,
|
||||||
|
unsigned stackLimitOffset) = 0;
|
||||||
virtual unsigned poolSize() = 0;
|
virtual unsigned poolSize() = 0;
|
||||||
virtual void writeTo(uint8_t* dst) = 0;
|
virtual void writeTo(uint8_t* dst) = 0;
|
||||||
|
|
||||||
|
@ -30,9 +30,6 @@ class ClassInitList;
|
|||||||
|
|
||||||
class Thread: public vm::Thread {
|
class Thread: public vm::Thread {
|
||||||
public:
|
public:
|
||||||
static const unsigned StackSizeInBytes = 64 * 1024;
|
|
||||||
static const unsigned StackSizeInWords = StackSizeInBytes / BytesPerWord;
|
|
||||||
|
|
||||||
Thread(Machine* m, object javaThread, vm::Thread* parent):
|
Thread(Machine* m, object javaThread, vm::Thread* parent):
|
||||||
vm::Thread(m, javaThread, parent),
|
vm::Thread(m, javaThread, parent),
|
||||||
ip(0),
|
ip(0),
|
||||||
@ -78,7 +75,7 @@ pushObject(Thread* t, object o)
|
|||||||
fprintf(stderr, "push object %p at %d\n", o, t->sp);
|
fprintf(stderr, "push object %p at %d\n", o, t->sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(t, t->sp + 1 < Thread::StackSizeInWords / 2);
|
assert(t, t->sp + 1 < StackSizeInWords / 2);
|
||||||
t->stack[(t->sp * 2) ] = ObjectTag;
|
t->stack[(t->sp * 2) ] = ObjectTag;
|
||||||
t->stack[(t->sp * 2) + 1] = reinterpret_cast<uintptr_t>(o);
|
t->stack[(t->sp * 2) + 1] = reinterpret_cast<uintptr_t>(o);
|
||||||
++ t->sp;
|
++ t->sp;
|
||||||
@ -91,7 +88,7 @@ pushInt(Thread* t, uint32_t v)
|
|||||||
fprintf(stderr, "push int %d at %d\n", v, t->sp);
|
fprintf(stderr, "push int %d at %d\n", v, t->sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(t, t->sp + 1 < Thread::StackSizeInWords / 2);
|
assert(t, t->sp + 1 < StackSizeInWords / 2);
|
||||||
t->stack[(t->sp * 2) ] = IntTag;
|
t->stack[(t->sp * 2) ] = IntTag;
|
||||||
t->stack[(t->sp * 2) + 1] = v;
|
t->stack[(t->sp * 2) + 1] = v;
|
||||||
++ t->sp;
|
++ t->sp;
|
||||||
@ -184,7 +181,7 @@ peekObject(Thread* t, unsigned index)
|
|||||||
index);
|
index);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(t, index < Thread::StackSizeInWords / 2);
|
assert(t, index < StackSizeInWords / 2);
|
||||||
assert(t, t->stack[index * 2] == ObjectTag);
|
assert(t, t->stack[index * 2] == ObjectTag);
|
||||||
return *reinterpret_cast<object*>(t->stack + (index * 2) + 1);
|
return *reinterpret_cast<object*>(t->stack + (index * 2) + 1);
|
||||||
}
|
}
|
||||||
@ -198,7 +195,7 @@ peekInt(Thread* t, unsigned index)
|
|||||||
index);
|
index);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(t, index < Thread::StackSizeInWords / 2);
|
assert(t, index < StackSizeInWords / 2);
|
||||||
assert(t, t->stack[index * 2] == IntTag);
|
assert(t, t->stack[index * 2] == IntTag);
|
||||||
return t->stack[(index * 2) + 1];
|
return t->stack[(index * 2) + 1];
|
||||||
}
|
}
|
||||||
@ -254,7 +251,7 @@ inline object*
|
|||||||
pushReference(Thread* t, object o)
|
pushReference(Thread* t, object o)
|
||||||
{
|
{
|
||||||
if (o) {
|
if (o) {
|
||||||
expect(t, t->sp + 1 < Thread::StackSizeInWords / 2);
|
expect(t, t->sp + 1 < StackSizeInWords / 2);
|
||||||
pushObject(t, o);
|
pushObject(t, o);
|
||||||
return reinterpret_cast<object*>(t->stack + ((t->sp - 1) * 2) + 1);
|
return reinterpret_cast<object*>(t->stack + ((t->sp - 1) * 2) + 1);
|
||||||
} else {
|
} else {
|
||||||
@ -436,7 +433,7 @@ checkStack(Thread* t, object method)
|
|||||||
+ codeMaxLocals(t, methodCode(t, method))
|
+ codeMaxLocals(t, methodCode(t, method))
|
||||||
+ FrameFootprint
|
+ FrameFootprint
|
||||||
+ codeMaxStack(t, methodCode(t, method))
|
+ codeMaxStack(t, methodCode(t, method))
|
||||||
> Thread::StackSizeInWords / 2))
|
> StackSizeInWords / 2))
|
||||||
{
|
{
|
||||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||||
}
|
}
|
||||||
@ -3136,7 +3133,7 @@ class MyProcessor: public Processor {
|
|||||||
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
|
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
|
||||||
|
|
||||||
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
|
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
|
||||||
> Thread::StackSizeInWords / 2))
|
> StackSizeInWords / 2))
|
||||||
{
|
{
|
||||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||||
return 0;
|
return 0;
|
||||||
@ -3161,7 +3158,7 @@ class MyProcessor: public Processor {
|
|||||||
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
|
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
|
||||||
|
|
||||||
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
|
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
|
||||||
> Thread::StackSizeInWords / 2))
|
> StackSizeInWords / 2))
|
||||||
{
|
{
|
||||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||||
return 0;
|
return 0;
|
||||||
@ -3185,7 +3182,7 @@ class MyProcessor: public Processor {
|
|||||||
or t->state == Thread::ExclusiveState);
|
or t->state == Thread::ExclusiveState);
|
||||||
|
|
||||||
if (UNLIKELY(t->sp + parameterFootprint(vmt, methodSpec, false)
|
if (UNLIKELY(t->sp + parameterFootprint(vmt, methodSpec, false)
|
||||||
> Thread::StackSizeInWords / 2))
|
> StackSizeInWords / 2))
|
||||||
{
|
{
|
||||||
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -56,6 +56,9 @@ const unsigned ThreadBackupHeapSizeInBytes = 2 * 1024;
|
|||||||
const unsigned ThreadBackupHeapSizeInWords
|
const unsigned ThreadBackupHeapSizeInWords
|
||||||
= ThreadBackupHeapSizeInBytes / BytesPerWord;
|
= ThreadBackupHeapSizeInBytes / BytesPerWord;
|
||||||
|
|
||||||
|
const unsigned StackSizeInBytes = 64 * 1024;
|
||||||
|
const unsigned StackSizeInWords = StackSizeInBytes / BytesPerWord;
|
||||||
|
|
||||||
const unsigned ThreadHeapPoolSize = 64;
|
const unsigned ThreadHeapPoolSize = 64;
|
||||||
|
|
||||||
const unsigned FixedFootprintThresholdInBytes
|
const unsigned FixedFootprintThresholdInBytes
|
||||||
|
29
test/StackOverflow.java
Normal file
29
test/StackOverflow.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
public class StackOverflow {
|
||||||
|
private static void test1() {
|
||||||
|
test1();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void test2() {
|
||||||
|
test3();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void test3() {
|
||||||
|
test2();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
test1();
|
||||||
|
throw new RuntimeException();
|
||||||
|
} catch (StackOverflowError e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
test2();
|
||||||
|
throw new RuntimeException();
|
||||||
|
} catch (StackOverflowError e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user