aggr = "gobuf";
else if(streq(fields.p[1], "WinCall"))
aggr = "wincall";
+ else if(streq(fields.p[1], "SEH"))
+ aggr = "seh";
}
if(hasprefix(lines.p[i], "}"))
aggr = nil;
--- /dev/null
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build cgo
+
+package runtime_test
+
+import (
+ "testing"
+)
+
+func TestCgoCrashHandler(t *testing.T) {
+ testCrashHandler(t, &crashTest{Cgo: true})
+}
--- /dev/null
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+ "text/template"
+)
+
+type crashTest struct {
+ Cgo bool
+}
+
+// This test is a separate program, because it is testing
+// both main (m0) and non-main threads (m).
+
+func testCrashHandler(t *testing.T, ct *crashTest) {
+ st := template.Must(template.New("crashSource").Parse(crashSource))
+
+ dir, err := ioutil.TempDir("", "go-build")
+ if err != nil {
+ t.Fatalf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ src := filepath.Join(dir, "main.go")
+ f, err := os.Create(src)
+ if err != nil {
+ t.Fatalf("failed to create %v: %v", src, err)
+ }
+ err = st.Execute(f, ct)
+ if err != nil {
+ f.Close()
+ t.Fatalf("failed to execute template: %v", err)
+ }
+ f.Close()
+
+ got, err := exec.Command("go", "run", src).CombinedOutput()
+ if err != nil {
+ t.Fatalf("program exited with error: %v\n%v", err, string(got))
+ }
+ want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
+ if string(got) != string(want) {
+ t.Fatalf("expected %q, but got %q", string(want), string(got))
+ }
+}
+
+func TestCrashHandler(t *testing.T) {
+ testCrashHandler(t, &crashTest{Cgo: false})
+}
+
+const crashSource = `
+package main
+
+import (
+ "fmt"
+ "runtime"
+)
+
+{{if .Cgo}}
+import "C"
+{{end}}
+
+func test(name string) {
+ defer func() {
+ if x := recover(); x != nil {
+ fmt.Printf(" recovered")
+ }
+ fmt.Printf(" done\n")
+ }()
+ fmt.Printf("%s:", name)
+ var s *string
+ _ = *s
+ fmt.Print("SHOULD NOT BE HERE")
+}
+
+func testInNewThread(name string) {
+ c := make(chan bool)
+ go func() {
+ runtime.LockOSThread()
+ test(name)
+ c <- true
+ }()
+ <-c
+}
+
+func main() {
+ runtime.LockOSThread()
+ test("main")
+ testInNewThread("new-thread")
+ testInNewThread("second-new-thread")
+ test("main-again")
+}
+`
byte *runtime·compilecallback(Eface fn, bool cleanstack);
void *runtime·callbackasm(void);
+void runtime·install_exception_handler(void);
+
// TODO(brainman): should not need those
#define NSIG 65
void
runtime·mstart(void)
{
+ // It is used by windows-386 only. Unfortunately, seh needs
+ // to be located on os stack, and mstart runs on os stack
+ // for both m0 and m.
+ SEH seh;
+
if(g != m->g0)
runtime·throw("bad runtime·mstart");
// so other calls can reuse this stack space.
runtime·gosave(&m->g0->sched);
m->g0->sched.pc = (void*)-1; // make sure it is never used
+ m->seh = &seh;
runtime·asminit();
runtime·minit();
runtime·initsig();
schedule(nil);
+
+ // TODO(brainman): This point is never reached, because scheduler
+ // does not release os threads at the moment. But once this path
+ // is enabled, we must remove our seh here.
}
// When running with cgo, we call libcgo_thread_start
// license that can be found in the LICENSE file.
TEXT _rt0_386_windows(SB),7,$0
- // Set up SEH frame for bootstrap m
- PUSHL $runtime·sigtramp(SB)
- PUSHL 0(FS)
- MOVL SP, 0(FS)
-
JMP _rt0_386(SB)
DATA runtime·iswindows(SB)/4, $1
typedef struct Complex64 Complex64;
typedef struct Complex128 Complex128;
typedef struct WinCall WinCall;
+typedef struct SEH SEH;
typedef struct Timers Timers;
typedef struct Timer Timer;
typedef struct GCStats GCStats;
#ifdef GOOS_windows
void* thread; // thread handle
#endif
+ SEH* seh;
uintptr end[];
};
uintptr r2;
uintptr err; // error number
};
+struct SEH
+{
+ void* prev;
+ void* handler;
+};
#ifdef GOOS_windows
enum {
MOVL newm+4(SP), CX // m
MOVL m_g0(CX), DX // g
- // Set up SEH frame
- PUSHL $runtime·sigtramp(SB)
- PUSHL 0(FS)
- MOVL SP, 0(FS)
-
// Layout new m scheduler stack on os stack.
MOVL SP, AX
MOVL AX, g_stackbase(DX)
CALL runtime·mstart(SB)
- // Pop SEH frame
- MOVL 0(FS), SP
- POPL 0(FS)
- POPL CX
-
RET
// uint32 tstart_stdcall(M *newm);
MOVL address+4(FP), CX
MOVL CX, 0x14(FS)
RET
+
+// void install_exception_handler()
+TEXT runtime·install_exception_handler(SB),7,$0
+ get_tls(CX)
+ MOVL m(CX), CX // m
+
+ // Set up SEH frame
+ MOVL m_seh(CX), DX
+ MOVL $runtime·sigtramp(SB), AX
+ MOVL AX, seh_handler(DX)
+ MOVL 0(FS), AX
+ MOVL AX, seh_prev(DX)
+
+ // Install it
+ MOVL DX, 0(FS)
+
+ RET
// Someday the convention will be D is always cleared.
CLD
- CALL runtime·setstacklimits(SB)
CALL runtime·stackcheck(SB) // clobbers AX,CX
CALL runtime·mstart(SB)
// set tls base to DI
TEXT runtime·settls(SB),7,$0
- CALL runtime·setstacklimits(SB)
MOVQ DI, 0x28(GS)
RET
+
+// void install_exception_handler()
+TEXT runtime·install_exception_handler(SB),7,$0
+ CALL runtime·setstacklimits(SB)
+ RET
void
runtime·minit(void)
{
+ runtime·install_exception_handler();
}
int64