Commit 64736acc authored by Alex Brainman's avatar Alex Brainman

undo CL 145150043 / 8b3d26697b8d

That was complete failure - builders are broken,
but original cl worked fine on my system.
I will need access to builders
to test this change properly.

««« original CL description
runtime: handle all windows exception

Fixes #8006.

LGTM=rsc
R=golang-codereviews, rsc
CC=golang-codereviews
https://golang.org/cl/145150043
»»»

TBR=rsc
R=golang-codereviews
CC=golang-codereviews
https://golang.org/cl/154180043
parent 17a108ba
...@@ -59,9 +59,6 @@ const ( ...@@ -59,9 +59,6 @@ const (
INFINITE = C.INFINITE INFINITE = C.INFINITE
WAIT_TIMEOUT = C.WAIT_TIMEOUT WAIT_TIMEOUT = C.WAIT_TIMEOUT
EXCEPTION_CONTINUE_EXECUTION = C.EXCEPTION_CONTINUE_EXECUTION
EXCEPTION_CONTINUE_SEARCH = C.EXCEPTION_CONTINUE_SEARCH
) )
type SystemInfo C.SYSTEM_INFO type SystemInfo C.SYSTEM_INFO
......
...@@ -32,9 +32,6 @@ enum { ...@@ -32,9 +32,6 @@ enum {
INFINITE = 0xffffffff, INFINITE = 0xffffffff,
WAIT_TIMEOUT = 0x102, WAIT_TIMEOUT = 0x102,
EXCEPTION_CONTINUE_EXECUTION = -0x1,
EXCEPTION_CONTINUE_SEARCH = 0x0,
}; };
typedef struct SystemInfo SystemInfo; typedef struct SystemInfo SystemInfo;
......
...@@ -32,9 +32,6 @@ enum { ...@@ -32,9 +32,6 @@ enum {
INFINITE = 0xffffffff, INFINITE = 0xffffffff,
WAIT_TIMEOUT = 0x102, WAIT_TIMEOUT = 0x102,
EXCEPTION_CONTINUE_EXECUTION = -0x1,
EXCEPTION_CONTINUE_SEARCH = 0x0,
}; };
typedef struct SystemInfo SystemInfo; typedef struct SystemInfo SystemInfo;
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll" #pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
#pragma dynimport runtime·SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll" #pragma dynimport runtime·SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll"
#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll" #pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll"
#pragma dynimport runtime·SetUnhandledExceptionFilter SetUnhandledExceptionFilter "kernel32.dll"
#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll" #pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll"
#pragma dynimport runtime·Sleep Sleep "kernel32.dll" #pragma dynimport runtime·Sleep Sleep "kernel32.dll"
#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll" #pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll"
...@@ -66,7 +65,6 @@ extern void *runtime·SetConsoleCtrlHandler; ...@@ -66,7 +65,6 @@ extern void *runtime·SetConsoleCtrlHandler;
extern void *runtime·SetEvent; extern void *runtime·SetEvent;
extern void *runtime·SetProcessPriorityBoost; extern void *runtime·SetProcessPriorityBoost;
extern void *runtime·SetThreadPriority; extern void *runtime·SetThreadPriority;
extern void *runtime·SetUnhandledExceptionFilter;
extern void *runtime·SetWaitableTimer; extern void *runtime·SetWaitableTimer;
extern void *runtime·Sleep; extern void *runtime·Sleep;
extern void *runtime·SuspendThread; extern void *runtime·SuspendThread;
...@@ -79,9 +77,7 @@ void *runtime·GetQueuedCompletionStatusEx; ...@@ -79,9 +77,7 @@ void *runtime·GetQueuedCompletionStatusEx;
extern uintptr runtime·externalthreadhandlerp; extern uintptr runtime·externalthreadhandlerp;
void runtime·externalthreadhandler(void); void runtime·externalthreadhandler(void);
void runtime·exceptiontramp(void); void runtime·sigtramp(void);
void runtime·firstcontinuetramp(void);
void runtime·lastcontinuetramp(void);
#pragma textflag NOSPLIT #pragma textflag NOSPLIT
uintptr uintptr
...@@ -110,28 +106,12 @@ void ...@@ -110,28 +106,12 @@ void
runtime·osinit(void) runtime·osinit(void)
{ {
void *kernel32; void *kernel32;
void *addVectoredContinueHandler = nil;
kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler; runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;
runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·exceptiontramp); runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·sigtramp);
if(kernel32 != nil)
addVectoredContinueHandler = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"AddVectoredContinueHandler");
if(addVectoredContinueHandler == nil)
// use SetUnhandledExceptionFilter if VectoredContinueHandler is unavailable.
// note: SetUnhandledExceptionFilter handler won't be called, if debugging.
runtime·stdcall1(runtime·SetUnhandledExceptionFilter, (uintptr)runtime·lastcontinuetramp);
else {
runtime·stdcall2(addVectoredContinueHandler, 1, (uintptr)runtime·firstcontinuetramp);
runtime·stdcall2(addVectoredContinueHandler, 0, (uintptr)runtime·lastcontinuetramp);
}
runtime·stdcall2(runtime·SetConsoleCtrlHandler, (uintptr)runtime·ctrlhandler, 1); runtime·stdcall2(runtime·SetConsoleCtrlHandler, (uintptr)runtime·ctrlhandler, 1);
runtime·stdcall1(runtime·timeBeginPeriod, 1); runtime·stdcall1(runtime·timeBeginPeriod, 1);
runtime·ncpu = getproccount(); runtime·ncpu = getproccount();
// Windows dynamic priority boosting assumes that a process has different types // Windows dynamic priority boosting assumes that a process has different types
...@@ -140,6 +120,7 @@ runtime·osinit(void) ...@@ -140,6 +120,7 @@ runtime·osinit(void)
// In such context dynamic priority boosting does nothing but harm, so we turn it off. // In such context dynamic priority boosting does nothing but harm, so we turn it off.
runtime·stdcall2(runtime·SetProcessPriorityBoost, -1, 1); runtime·stdcall2(runtime·SetProcessPriorityBoost, -1, 1);
kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
if(kernel32 != nil) { if(kernel32 != nil) {
runtime·GetQueuedCompletionStatusEx = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"GetQueuedCompletionStatusEx"); runtime·GetQueuedCompletionStatusEx = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"GetQueuedCompletionStatusEx");
} }
...@@ -494,14 +475,10 @@ runtime·issigpanic(uint32 code) ...@@ -494,14 +475,10 @@ runtime·issigpanic(uint32 code)
void void
runtime·initsig(void) runtime·initsig(void)
{ {
// following line keeps these functions alive at link stage // following line keeps sigtramp alive at link stage
// if there's a better way please write it here // if there's a better way please write it here
void *e = runtime·exceptiontramp; void *p = runtime·sigtramp;
void *f = runtime·firstcontinuetramp; USED(p);
void *l = runtime·lastcontinuetramp;
USED(e);
USED(f);
USED(l);
} }
uint32 uint32
......
...@@ -24,63 +24,45 @@ runtime·dumpregs(Context *r) ...@@ -24,63 +24,45 @@ runtime·dumpregs(Context *r)
runtime·printf("gs %x\n", r->SegGs); runtime·printf("gs %x\n", r->SegGs);
} }
bool // Called by sigtramp from Windows VEH handler.
runtime·isgoexception(ExceptionRecord *info, Context *r) // Return value signals whether the exception has been handled (-1)
// or should be made available to other handlers in the chain (0).
uint32
runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
{ {
bool crash;
uintptr *sp;
extern byte runtime·text[], runtime·etext[]; extern byte runtime·text[], runtime·etext[];
// Only handle exception if executing instructions in Go binary // Only handle exception if executing instructions in Go binary
// (not Windows library code). // (not Windows library code).
if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip) if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip)
return false; return 0;
if(!runtime·issigpanic(info->ExceptionCode)) if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
return false; // Make it look like a call to the signal func.
// Have to pass arguments out of band since
return true; // augmenting the stack frame would break
} // the unwinding code.
gp->sig = info->ExceptionCode;
// Called by sigtramp from Windows VEH handler. gp->sigcode0 = info->ExceptionInformation[0];
// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION) gp->sigcode1 = info->ExceptionInformation[1];
// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH). gp->sigpc = r->Eip;
uint32
runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp) // Only push runtime·sigpanic if r->eip != 0.
{ // If r->eip == 0, probably panicked because of a
uintptr *sp; // call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
if(!runtime·isgoexception(info, r)) // (Otherwise the trace will end at runtime·sigpanic and we
return EXCEPTION_CONTINUE_SEARCH; // won't get to see who faulted.)
if(r->Eip != 0) {
// Make it look like a call to the signal func. sp = (uintptr*)r->Esp;
// Have to pass arguments out of band since *--sp = r->Eip;
// augmenting the stack frame would break r->Esp = (uintptr)sp;
// the unwinding code. }
gp->sig = info->ExceptionCode; r->Eip = (uintptr)runtime·sigpanic;
gp->sigcode0 = info->ExceptionInformation[0]; return -1;
gp->sigcode1 = info->ExceptionInformation[1];
gp->sigpc = r->Eip;
// Only push runtime·sigpanic if r->eip != 0.
// If r->eip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
// (Otherwise the trace will end at runtime·sigpanic and we
// won't get to see who faulted.)
if(r->Eip != 0) {
sp = (uintptr*)r->Esp;
*--sp = r->Eip;
r->Esp = (uintptr)sp;
} }
r->Eip = (uintptr)runtime·sigpanic;
return EXCEPTION_CONTINUE_EXECUTION;
}
// lastcontinuehandler is reached, because runtime cannot handle
// current exception. lastcontinuehandler will print crash info and exit.
uint32
runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
{
bool crash;
if(runtime·panicking) // traceback already printed if(runtime·panicking) // traceback already printed
runtime·exit(2); runtime·exit(2);
...@@ -106,7 +88,7 @@ runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp) ...@@ -106,7 +88,7 @@ runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
runtime·crash(); runtime·crash();
runtime·exit(2); runtime·exit(2);
return 0; // not reached return -1; // not reached
} }
void void
......
...@@ -32,76 +32,45 @@ runtime·dumpregs(Context *r) ...@@ -32,76 +32,45 @@ runtime·dumpregs(Context *r)
runtime·printf("gs %X\n", (uint64)r->SegGs); runtime·printf("gs %X\n", (uint64)r->SegGs);
} }
bool // Called by sigtramp from Windows VEH handler.
runtime·isgoexception(ExceptionRecord *info, Context *r) // Return value signals whether the exception has been handled (-1)
// or should be made available to other handlers in the chain (0).
uint32
runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
{ {
bool crash;
uintptr *sp;
extern byte runtime·text[], runtime·etext[]; extern byte runtime·text[], runtime·etext[];
// Only handle exception if executing instructions in Go binary // Only handle exception if executing instructions in Go binary
// (not Windows library code). // (not Windows library code).
if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip) if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip)
return false; return 0;
if(!runtime·issigpanic(info->ExceptionCode)) if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
return false; // Make it look like a call to the signal func.
// Have to pass arguments out of band since
return true; // augmenting the stack frame would break
} // the unwinding code.
gp->sig = info->ExceptionCode;
// Called by sigtramp from Windows VEH handler. gp->sigcode0 = info->ExceptionInformation[0];
// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION) gp->sigcode1 = info->ExceptionInformation[1];
// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH). gp->sigpc = r->Rip;
uint32
runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp) // Only push runtime·sigpanic if r->rip != 0.
{ // If r->rip == 0, probably panicked because of a
uintptr *sp; // call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
if(!runtime·isgoexception(info, r)) // (Otherwise the trace will end at runtime·sigpanic and we
return EXCEPTION_CONTINUE_SEARCH; // won't get to see who faulted.)
if(r->Rip != 0) {
// Make it look like a call to the signal func. sp = (uintptr*)r->Rsp;
// Have to pass arguments out of band since *--sp = r->Rip;
// augmenting the stack frame would break r->Rsp = (uintptr)sp;
// the unwinding code. }
gp->sig = info->ExceptionCode; r->Rip = (uintptr)runtime·sigpanic;
gp->sigcode0 = info->ExceptionInformation[0]; return -1;
gp->sigcode1 = info->ExceptionInformation[1];
gp->sigpc = r->Rip;
// Only push runtime·sigpanic if r->rip != 0.
// If r->rip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
// (Otherwise the trace will end at runtime·sigpanic and we
// won't get to see who faulted.)
if(r->Rip != 0) {
sp = (uintptr*)r->Rsp;
*--sp = r->Rip;
r->Rsp = (uintptr)sp;
} }
r->Rip = (uintptr)runtime·sigpanic;
return EXCEPTION_CONTINUE_EXECUTION;
}
// It seems Windows searches ContinueHandler's list even
// if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
// firstcontinuehandler will stop that search,
// if exceptionhandler did the same earlier.
uint32
runtime·firstcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
{
USED(gp);
if(!runtime·isgoexception(info, r))
return EXCEPTION_CONTINUE_SEARCH;
return EXCEPTION_CONTINUE_EXECUTION;
}
// lastcontinuehandler is reached, because runtime cannot handle
// current exception. lastcontinuehandler will print crash info and exit.
uint32
runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
{
bool crash;
if(runtime·panicking) // traceback already printed if(runtime·panicking) // traceback already printed
runtime·exit(2); runtime·exit(2);
...@@ -128,7 +97,7 @@ runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp) ...@@ -128,7 +97,7 @@ runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
runtime·crash(); runtime·crash();
runtime·exit(2); runtime·exit(2);
return 0; // not reached return -1; // not reached
} }
void void
......
...@@ -73,7 +73,6 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0 ...@@ -73,7 +73,6 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0
// Called by Windows as a Vectored Exception Handler (VEH). // Called by Windows as a Vectored Exception Handler (VEH).
// First argument is pointer to struct containing // First argument is pointer to struct containing
// exception record and context pointers. // exception record and context pointers.
// Handler function is stored in AX.
// Return 0 for 'not handled', -1 for handled. // Return 0 for 'not handled', -1 for handled.
TEXT runtime·sigtramp(SB),NOSPLIT,$0-0 TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVL ptrs+0(FP), CX MOVL ptrs+0(FP), CX
...@@ -85,8 +84,6 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0 ...@@ -85,8 +84,6 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVL SI, 20(SP) MOVL SI, 20(SP)
MOVL DI, 24(SP) MOVL DI, 24(SP)
MOVL AX, SI // save handler address
// find g // find g
get_tls(DX) get_tls(DX)
CMPL DX, $0 CMPL DX, $0
...@@ -126,10 +123,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0 ...@@ -126,10 +123,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
sigtramp_g0: sigtramp_g0:
MOVL 0(CX), BX // ExceptionRecord* MOVL 0(CX), BX // ExceptionRecord*
MOVL 4(CX), CX // Context* MOVL 4(CX), CX // Context*
// call sighandler(ExceptionRecord*, Context*, G*)
MOVL BX, 0(SP) MOVL BX, 0(SP)
MOVL CX, 4(SP) MOVL CX, 4(SP)
MOVL DX, 8(SP) MOVL DX, 8(SP)
CALL SI // call handler CALL runtime·sighandler(SB)
// AX is set to report result back to Windows // AX is set to report result back to Windows
MOVL 12(SP), AX MOVL 12(SP), AX
...@@ -151,18 +149,6 @@ done: ...@@ -151,18 +149,6 @@ done:
// RET 4 (return and pop 4 bytes parameters) // RET 4 (return and pop 4 bytes parameters)
BYTE $0xC2; WORD $4 BYTE $0xC2; WORD $4
RET // unreached; make assembler happy RET // unreached; make assembler happy
TEXT runtime·exceptiontramp(SB),NOSPLIT,$0
MOVL $runtime·exceptionhandler(SB), AX
JMP runtime·sigtramp(SB)
TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0
// is never called
INT $3
TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0
MOVL $runtime·lastcontinuehandler(SB), AX
JMP runtime·sigtramp(SB)
TEXT runtime·ctrlhandler(SB),NOSPLIT,$0 TEXT runtime·ctrlhandler(SB),NOSPLIT,$0
PUSHL $runtime·ctrlhandler1(SB) PUSHL $runtime·ctrlhandler1(SB)
......
...@@ -99,7 +99,6 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0 ...@@ -99,7 +99,6 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0
// Called by Windows as a Vectored Exception Handler (VEH). // Called by Windows as a Vectored Exception Handler (VEH).
// First argument is pointer to struct containing // First argument is pointer to struct containing
// exception record and context pointers. // exception record and context pointers.
// Handler function is stored in AX.
// Return 0 for 'not handled', -1 for handled. // Return 0 for 'not handled', -1 for handled.
TEXT runtime·sigtramp(SB),NOSPLIT,$0-0 TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
// CX: PEXCEPTION_POINTERS ExceptionInfo // CX: PEXCEPTION_POINTERS ExceptionInfo
...@@ -117,8 +116,6 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0 ...@@ -117,8 +116,6 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
MOVQ R14, 32(SP) MOVQ R14, 32(SP)
MOVQ R15, 88(SP) MOVQ R15, 88(SP)
MOVQ AX, R15 // save handler address
// find g // find g
get_tls(DX) get_tls(DX)
CMPQ DX, $0 CMPQ DX, $0
...@@ -160,10 +157,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0 ...@@ -160,10 +157,11 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
sigtramp_g0: sigtramp_g0:
MOVQ 0(CX), BX // ExceptionRecord* MOVQ 0(CX), BX // ExceptionRecord*
MOVQ 8(CX), CX // Context* MOVQ 8(CX), CX // Context*
// call sighandler(ExceptionRecord*, Context*, G*)
MOVQ BX, 0(SP) MOVQ BX, 0(SP)
MOVQ CX, 8(SP) MOVQ CX, 8(SP)
MOVQ DX, 16(SP) MOVQ DX, 16(SP)
CALL R15 // call handler CALL runtime·sighandler(SB)
// AX is set to report result back to Windows // AX is set to report result back to Windows
MOVL 24(SP), AX MOVL 24(SP), AX
...@@ -189,18 +187,6 @@ done: ...@@ -189,18 +187,6 @@ done:
RET RET
TEXT runtime·exceptiontramp(SB),NOSPLIT,$0
MOVQ $runtime·exceptionhandler(SB), AX
JMP runtime·sigtramp(SB)
TEXT runtime·firstcontinuetramp(SB),NOSPLIT,$0-0
MOVQ $runtime·firstcontinuehandler(SB), AX
JMP runtime·sigtramp(SB)
TEXT runtime·lastcontinuetramp(SB),NOSPLIT,$0-0
MOVQ $runtime·lastcontinuehandler(SB), AX
JMP runtime·sigtramp(SB)
TEXT runtime·ctrlhandler(SB),NOSPLIT,$8 TEXT runtime·ctrlhandler(SB),NOSPLIT,$8
MOVQ CX, 16(SP) // spill MOVQ CX, 16(SP) // spill
MOVQ $runtime·ctrlhandler1(SB), CX MOVQ $runtime·ctrlhandler1(SB), CX
......
...@@ -494,42 +494,3 @@ func TestOutputDebugString(t *testing.T) { ...@@ -494,42 +494,3 @@ func TestOutputDebugString(t *testing.T) {
p := syscall.StringToUTF16Ptr("testing OutputDebugString") p := syscall.StringToUTF16Ptr("testing OutputDebugString")
d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p))) d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
} }
func TestRaiseException(t *testing.T) {
o := executeTest(t, raiseExceptionSource, nil)
if strings.Contains(o, "RaiseException should not return") {
t.Fatalf("RaiseException did not crash program: %v", o)
}
if !strings.Contains(o, "Exception 0xbad") {
t.Fatalf("No stack trace: %v", o)
}
}
const raiseExceptionSource = `
package main
import "syscall"
func main() {
const EXCEPTION_NONCONTINUABLE = 1
mod := syscall.MustLoadDLL("kernel32.dll")
proc := mod.MustFindProc("RaiseException")
proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
println("RaiseException should not return")
}
`
func TestZeroDivisionException(t *testing.T) {
o := executeTest(t, zeroDivisionExceptionSource, nil)
if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
t.Fatalf("No stack trace: %v", o)
}
}
const zeroDivisionExceptionSource = `
package main
func main() {
x := 1
y := 0
z := x / y
println(z)
}
`
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment