Commit 5f9a870b authored by Ian Lance Taylor's avatar Ian Lance Taylor

cmd/cgo, runtime, runtime/cgo: use cgo context function

Add support for the context function set by runtime.SetCgoTraceback.
The context function was added in CL 17761, without support.
This CL is the support.

This CL has not been tested for real C code, as a working context
function for C code requires unwind support that does not seem to exist.
I wanted to get the CL out before the freeze.

I apologize for the length of this CL.  It's mostly plumbing, but
unfortunately the plumbing is processor-specific.

Change-Id: I8ce11a0de9b3dafcc29efd2649d776e93bff0e90
Reviewed-on: https://go-review.googlesource.com/22508Reviewed-by: default avatarAustin Clements <austin@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent c717675c
...@@ -511,7 +511,6 @@ file compiled by gcc, the file x.cgo2.c: ...@@ -511,7 +511,6 @@ file compiled by gcc, the file x.cgo2.c:
void void
_cgo_be59f0f25121_Cfunc_puts(void *v) _cgo_be59f0f25121_Cfunc_puts(void *v)
{ {
_cgo_wait_runtime_init_done();
struct { struct {
char* p0; char* p0;
int r; int r;
...@@ -520,8 +519,7 @@ file compiled by gcc, the file x.cgo2.c: ...@@ -520,8 +519,7 @@ file compiled by gcc, the file x.cgo2.c:
a->r = puts((void*)a->p0); a->r = puts((void*)a->p0);
} }
It waits for Go runtime to be initialized (required for shared libraries), It extracts the arguments from the pointer to _Cfunc_puts's argument
extracts the arguments from the pointer to _Cfunc_puts's argument
frame, invokes the system C function (in this case, puts), stores the frame, invokes the system C function (in this case, puts), stores the
result in the frame, and returns. result in the frame, and returns.
...@@ -539,8 +537,8 @@ linkage to the desired libraries. The main function is provided by ...@@ -539,8 +537,8 @@ linkage to the desired libraries. The main function is provided by
_cgo_main.c: _cgo_main.c:
int main() { return 0; } int main() { return 0; }
void crosscall2(void(*fn)(void*, int), void *a, int c) { } void crosscall2(void(*fn)(void*, int, uintptr_t), void *a, int c, uintptr_t ctxt) { }
void _cgo_wait_runtime_init_done() { } uintptr_t _cgo_wait_runtime_init_done() { }
void _cgo_allocate(void *a, int c) { } void _cgo_allocate(void *a, int c) { }
void _cgo_panic(void *a, int c) { } void _cgo_panic(void *a, int c) { }
......
...@@ -50,14 +50,16 @@ func (p *Package) writeDefs() { ...@@ -50,14 +50,16 @@ func (p *Package) writeDefs() {
// Write C main file for using gcc to resolve imports. // Write C main file for using gcc to resolve imports.
fmt.Fprintf(fm, "int main() { return 0; }\n") fmt.Fprintf(fm, "int main() { return 0; }\n")
if *importRuntimeCgo { if *importRuntimeCgo {
fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c) { }\n") fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt) { }\n")
fmt.Fprintf(fm, "void _cgo_wait_runtime_init_done() { }\n") fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done() { return 0; }\n")
fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__ ctxt) { }\n")
fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n") fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n")
} else { } else {
// If we're not importing runtime/cgo, we *are* runtime/cgo, // If we're not importing runtime/cgo, we *are* runtime/cgo,
// which provides these functions. We just need a prototype. // which provides these functions. We just need a prototype.
fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int), void *a, int c);\n") fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*, int, __SIZE_TYPE__), void *a, int c, __SIZE_TYPE__ ctxt);\n")
fmt.Fprintf(fm, "void _cgo_wait_runtime_init_done();\n") fmt.Fprintf(fm, "__SIZE_TYPE__ _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fm, "void _cgo_release_context(__SIZE_TYPE__);\n")
} }
fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n") fmt.Fprintf(fm, "void _cgo_allocate(void *a, int c) { }\n")
fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n") fmt.Fprintf(fm, "void _cgo_panic(void *a, int c) { }\n")
...@@ -700,8 +702,9 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { ...@@ -700,8 +702,9 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n")
fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *, int), void *, int);\n") fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *, int, __SIZE_TYPE__), void *, int, __SIZE_TYPE__);\n")
fmt.Fprintf(fgcc, "extern void _cgo_wait_runtime_init_done();\n\n") fmt.Fprintf(fgcc, "extern __SIZE_TYPE__ _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n")
fmt.Fprintf(fgcc, "%s\n", tsanProlog) fmt.Fprintf(fgcc, "%s\n", tsanProlog)
for _, exp := range p.ExpFunc { for _, exp := range p.ExpFunc {
...@@ -803,10 +806,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { ...@@ -803,10 +806,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
} }
fmt.Fprintf(fgcch, "\nextern %s;\n", s) fmt.Fprintf(fgcch, "\nextern %s;\n", s)
fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int, __SIZE_TYPE__);\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "\n%s\n", s)
fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "{\n")
fmt.Fprintf(fgcc, "\t_cgo_wait_runtime_init_done();\n") fmt.Fprintf(fgcc, "\t__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();\n")
fmt.Fprintf(fgcc, "\t%s %v a;\n", ctype, p.packedAttribute()) fmt.Fprintf(fgcc, "\t%s %v a;\n", ctype, p.packedAttribute())
if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
fmt.Fprintf(fgcc, "\t%s r;\n", gccResult) fmt.Fprintf(fgcc, "\t%s r;\n", gccResult)
...@@ -819,8 +822,9 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { ...@@ -819,8 +822,9 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i) fmt.Fprintf(fgcc, "\ta.p%d = p%d;\n", i, i)
}) })
fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n") fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n")
fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, %d);\n", cPrefix, exp.ExpName, off) fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &a, %d, _cgo_ctxt);\n", cPrefix, exp.ExpName, off)
fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n") fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
fmt.Fprintf(fgcc, "\t_cgo_release_context(_cgo_ctxt);\n")
if gccResult != "void" { if gccResult != "void" {
if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 { if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
fmt.Fprintf(fgcc, "\treturn a.r0;\n") fmt.Fprintf(fgcc, "\treturn a.r0;\n")
...@@ -845,10 +849,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { ...@@ -845,10 +849,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgo2, "//go:cgo_export_static _cgoexp%s_%s\n", cPrefix, exp.ExpName) fmt.Fprintf(fgo2, "//go:cgo_export_static _cgoexp%s_%s\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "//go:nosplit\n") // no split stack, so no use of m or g fmt.Fprintf(fgo2, "//go:nosplit\n") // no split stack, so no use of m or g
fmt.Fprintf(fgo2, "//go:norace\n") // must not have race detector calls inserted fmt.Fprintf(fgo2, "//go:norace\n") // must not have race detector calls inserted
fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a unsafe.Pointer, n int32) {\n", cPrefix, exp.ExpName) fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a unsafe.Pointer, n int32, ctxt uintptr) {\n", cPrefix, exp.ExpName)
fmt.Fprintf(fgo2, "\tfn := %s\n", goname) fmt.Fprintf(fgo2, "\tfn := %s\n", goname)
// The indirect here is converting from a Go function pointer to a C function pointer. // The indirect here is converting from a Go function pointer to a C function pointer.
fmt.Fprintf(fgo2, "\t_cgo_runtime_cgocallback(**(**unsafe.Pointer)(unsafe.Pointer(&fn)), a, uintptr(n));\n") fmt.Fprintf(fgo2, "\t_cgo_runtime_cgocallback(**(**unsafe.Pointer)(unsafe.Pointer(&fn)), a, uintptr(n), ctxt);\n")
fmt.Fprintf(fgo2, "}\n") fmt.Fprintf(fgo2, "}\n")
fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName) fmt.Fprintf(fm, "int _cgoexp%s_%s;\n", cPrefix, exp.ExpName)
...@@ -1337,7 +1341,7 @@ func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32 ...@@ -1337,7 +1341,7 @@ func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32
func _cgo_runtime_cmalloc(uintptr) unsafe.Pointer func _cgo_runtime_cmalloc(uintptr) unsafe.Pointer
//go:linkname _cgo_runtime_cgocallback runtime.cgocallback //go:linkname _cgo_runtime_cgocallback runtime.cgocallback
func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr) func _cgo_runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr)
//go:linkname _cgoCheckPointer runtime.cgoCheckPointer //go:linkname _cgoCheckPointer runtime.cgoCheckPointer
func _cgoCheckPointer(interface{}, ...interface{}) interface{} func _cgoCheckPointer(interface{}, ...interface{}) interface{}
...@@ -1580,5 +1584,5 @@ static void GoInit(void) { ...@@ -1580,5 +1584,5 @@ static void GoInit(void) {
runtime_iscgo = 1; runtime_iscgo = 1;
} }
extern void _cgo_wait_runtime_init_done() __attribute__ ((weak)); extern __SIZE_TYPE__ _cgo_wait_runtime_init_done() __attribute__ ((weak));
` `
...@@ -612,23 +612,25 @@ noswitch: ...@@ -612,23 +612,25 @@ noswitch:
MOVL AX, ret+8(FP) MOVL AX, ret+8(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// Turn the fn into a Go func (by taking its address) and call // Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc. // cgocallback_gofunc.
TEXT runtime·cgocallback(SB),NOSPLIT,$12-12 TEXT runtime·cgocallback(SB),NOSPLIT,$16-16
LEAL fn+0(FP), AX LEAL fn+0(FP), AX
MOVL AX, 0(SP) MOVL AX, 0(SP)
MOVL frame+4(FP), AX MOVL frame+4(FP), AX
MOVL AX, 4(SP) MOVL AX, 4(SP)
MOVL framesize+8(FP), AX MOVL framesize+8(FP), AX
MOVL AX, 8(SP) MOVL AX, 8(SP)
MOVL ctxt+12(FP), AX
MOVL AX, 12(SP)
MOVL $runtime·cgocallback_gofunc(SB), AX MOVL $runtime·cgocallback_gofunc(SB), AX
CALL AX CALL AX
RET RET
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize, uintptr ctxt)
// See cgocall.go for more details. // See cgocall.go for more details.
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$12-12 TEXT ·cgocallback_gofunc(SB),NOSPLIT,$12-16
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
// If g is nil, Go did not create the current thread. // If g is nil, Go did not create the current thread.
...@@ -696,17 +698,19 @@ havem: ...@@ -696,17 +698,19 @@ havem:
// so that the traceback will seamlessly trace back into // so that the traceback will seamlessly trace back into
// the earlier calls. // the earlier calls.
// //
// In the new goroutine, 0(SP) holds the saved oldm (DX) register. // In the new goroutine, 4(SP) holds the saved oldm (DX) register.
// 4(SP) and 8(SP) are unused. // 8(SP) is unused.
MOVL m_curg(BP), SI MOVL m_curg(BP), SI
MOVL SI, g(CX) MOVL SI, g(CX)
MOVL (g_sched+gobuf_sp)(SI), DI // prepare stack as DI MOVL (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
MOVL (g_sched+gobuf_pc)(SI), BP MOVL (g_sched+gobuf_pc)(SI), BP
MOVL BP, -4(DI) MOVL BP, -4(DI)
MOVL ctxt+12(FP), CX
LEAL -(4+12)(DI), SP LEAL -(4+12)(DI), SP
MOVL DX, 0(SP) MOVL DX, 4(SP)
MOVL CX, 0(SP)
CALL runtime·cgocallbackg(SB) CALL runtime·cgocallbackg(SB)
MOVL 0(SP), DX MOVL 4(SP), DX
// Restore g->sched (== m->curg->sched) from saved values. // Restore g->sched (== m->curg->sched) from saved values.
get_tls(CX) get_tls(CX)
......
...@@ -622,23 +622,25 @@ nosave: ...@@ -622,23 +622,25 @@ nosave:
MOVL AX, ret+16(FP) MOVL AX, ret+16(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// Turn the fn into a Go func (by taking its address) and call // Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc. // cgocallback_gofunc.
TEXT runtime·cgocallback(SB),NOSPLIT,$24-24 TEXT runtime·cgocallback(SB),NOSPLIT,$32-32
LEAQ fn+0(FP), AX LEAQ fn+0(FP), AX
MOVQ AX, 0(SP) MOVQ AX, 0(SP)
MOVQ frame+8(FP), AX MOVQ frame+8(FP), AX
MOVQ AX, 8(SP) MOVQ AX, 8(SP)
MOVQ framesize+16(FP), AX MOVQ framesize+16(FP), AX
MOVQ AX, 16(SP) MOVQ AX, 16(SP)
MOVQ ctxt+24(FP), AX
MOVQ AX, 24(SP)
MOVQ $runtime·cgocallback_gofunc(SB), AX MOVQ $runtime·cgocallback_gofunc(SB), AX
CALL AX CALL AX
RET RET
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize, uintptr ctxt)
// See cgocall.go for more details. // See cgocall.go for more details.
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-24 TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-32
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
// If g is nil, Go did not create the current thread. // If g is nil, Go did not create the current thread.
...@@ -706,7 +708,7 @@ havem: ...@@ -706,7 +708,7 @@ havem:
// so that the traceback will seamlessly trace back into // so that the traceback will seamlessly trace back into
// the earlier calls. // the earlier calls.
// //
// In the new goroutine, 0(SP) holds the saved R8. // In the new goroutine, 8(SP) holds the saved R8.
MOVQ m_curg(BX), SI MOVQ m_curg(BX), SI
MOVQ SI, g(CX) MOVQ SI, g(CX)
MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI MOVQ (g_sched+gobuf_sp)(SI), DI // prepare stack as DI
...@@ -714,14 +716,16 @@ havem: ...@@ -714,14 +716,16 @@ havem:
MOVQ BX, -8(DI) MOVQ BX, -8(DI)
// Compute the size of the frame, including return PC and, if // Compute the size of the frame, including return PC and, if
// GOEXPERIMENT=framepointer, the saved based pointer // GOEXPERIMENT=framepointer, the saved based pointer
MOVQ ctxt+24(FP), BX
LEAQ fv+0(FP), AX LEAQ fv+0(FP), AX
SUBQ SP, AX SUBQ SP, AX
SUBQ AX, DI SUBQ AX, DI
MOVQ DI, SP MOVQ DI, SP
MOVQ R8, 0(SP) MOVQ R8, 8(SP)
MOVQ BX, 0(SP)
CALL runtime·cgocallbackg(SB) CALL runtime·cgocallbackg(SB)
MOVQ 0(SP), R8 MOVQ 8(SP), R8
// Compute the size of the frame again. FP and SP have // Compute the size of the frame again. FP and SP have
// completely different values here than they did above, // completely different values here than they did above,
......
...@@ -530,23 +530,25 @@ g0: ...@@ -530,23 +530,25 @@ g0:
MOVW R0, ret+8(FP) MOVW R0, ret+8(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// Turn the fn into a Go func (by taking its address) and call // Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc. // cgocallback_gofunc.
TEXT runtime·cgocallback(SB),NOSPLIT,$12-12 TEXT runtime·cgocallback(SB),NOSPLIT,$16-16
MOVW $fn+0(FP), R0 MOVW $fn+0(FP), R0
MOVW R0, 4(R13) MOVW R0, 4(R13)
MOVW frame+4(FP), R0 MOVW frame+4(FP), R0
MOVW R0, 8(R13) MOVW R0, 8(R13)
MOVW framesize+8(FP), R0 MOVW framesize+8(FP), R0
MOVW R0, 12(R13) MOVW R0, 12(R13)
MOVW ctxt+12(FP), R0
MOVW R0, 16(R13)
MOVW $runtime·cgocallback_gofunc(SB), R0 MOVW $runtime·cgocallback_gofunc(SB), R0
BL (R0) BL (R0)
RET RET
// cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback_gofunc(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// See cgocall.go for more details. // See cgocall.go for more details.
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-12 TEXT ·cgocallback_gofunc(SB),NOSPLIT,$8-16
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
// Load m and g from thread-local storage. // Load m and g from thread-local storage.
...@@ -611,17 +613,20 @@ havem: ...@@ -611,17 +613,20 @@ havem:
// so that the traceback will seamlessly trace back into // so that the traceback will seamlessly trace back into
// the earlier calls. // the earlier calls.
// //
// In the new goroutine, -8(SP) and -4(SP) are unused. // In the new goroutine, -4(SP) is unused (where SP refers to
// m->curg's SP while we're setting it up, before we've adjusted it).
MOVW m_curg(R8), R0 MOVW m_curg(R8), R0
BL setg<>(SB) BL setg<>(SB)
MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
MOVW (g_sched+gobuf_pc)(g), R5 MOVW (g_sched+gobuf_pc)(g), R5
MOVW R5, -12(R4) MOVW R5, -12(R4)
MOVW ctxt+12(FP), R0
MOVW R0, -8(R4)
MOVW $-12(R4), R13 MOVW $-12(R4), R13
BL runtime·cgocallbackg(SB) BL runtime·cgocallbackg(SB)
// Restore g->sched (== m->curg->sched) from saved values. // Restore g->sched (== m->curg->sched) from saved values.
MOVW 0(R13), R5 MOVW 4(R13), R5
MOVW R5, (g_sched+gobuf_pc)(g) MOVW R5, (g_sched+gobuf_pc)(g)
MOVW $12(R13), R4 MOVW $12(R13), R4
MOVW R4, (g_sched+gobuf_sp)(g) MOVW R4, (g_sched+gobuf_sp)(g)
......
...@@ -554,23 +554,25 @@ g0: ...@@ -554,23 +554,25 @@ g0:
MOVW R0, ret+16(FP) MOVW R0, ret+16(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// Turn the fn into a Go func (by taking its address) and call // Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc. // cgocallback_gofunc.
TEXT runtime·cgocallback(SB),NOSPLIT,$24-24 TEXT runtime·cgocallback(SB),NOSPLIT,$40-32
MOVD $fn+0(FP), R0 MOVD $fn+0(FP), R0
MOVD R0, 8(RSP) MOVD R0, 8(RSP)
MOVD frame+8(FP), R0 MOVD frame+8(FP), R0
MOVD R0, 16(RSP) MOVD R0, 16(RSP)
MOVD framesize+16(FP), R0 MOVD framesize+16(FP), R0
MOVD R0, 24(RSP) MOVD R0, 24(RSP)
MOVD ctxt+24(FP), R0
MOVD R0, 32(RSP)
MOVD $runtime·cgocallback_gofunc(SB), R0 MOVD $runtime·cgocallback_gofunc(SB), R0
BL (R0) BL (R0)
RET RET
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize, uintptr ctxt)
// See cgocall.go for more details. // See cgocall.go for more details.
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$24-24 TEXT ·cgocallback_gofunc(SB),NOSPLIT,$24-32
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
// Load g from thread-local storage. // Load g from thread-local storage.
...@@ -640,12 +642,15 @@ havem: ...@@ -640,12 +642,15 @@ havem:
// so that the traceback will seamlessly trace back into // so that the traceback will seamlessly trace back into
// the earlier calls. // the earlier calls.
// //
// In the new goroutine, -16(SP) and -8(SP) are unused. // In the new goroutine, -8(SP) is unused (where SP refers to
// m->curg's SP while we're setting it up, before we've adjusted it).
MOVD m_curg(R8), g MOVD m_curg(R8), g
BL runtime·save_g(SB) BL runtime·save_g(SB)
MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
MOVD (g_sched+gobuf_pc)(g), R5 MOVD (g_sched+gobuf_pc)(g), R5
MOVD R5, -(24+8)(R4) // maintain 16-byte SP alignment MOVD R5, -(16+8)(R4)
MOVD ctxt+24(FP), R0
MOVD R0, -(24+8)(R4) // maintain 16-byte SP alignment
MOVD $-(24+8)(R4), R0 MOVD $-(24+8)(R4), R0
MOVD R0, RSP MOVD R0, RSP
BL runtime·cgocallbackg(SB) BL runtime·cgocallbackg(SB)
......
...@@ -569,22 +569,24 @@ g0: ...@@ -569,22 +569,24 @@ g0:
MOVW R3, ret+16(FP) MOVW R3, ret+16(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// Turn the fn into a Go func (by taking its address) and call // Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc. // cgocallback_gofunc.
TEXT runtime·cgocallback(SB),NOSPLIT,$24-24 TEXT runtime·cgocallback(SB),NOSPLIT,$32-32
MOVD $fn+0(FP), R3 MOVD $fn+0(FP), R3
MOVD R3, FIXED_FRAME+0(R1) MOVD R3, FIXED_FRAME+0(R1)
MOVD frame+8(FP), R3 MOVD frame+8(FP), R3
MOVD R3, FIXED_FRAME+8(R1) MOVD R3, FIXED_FRAME+8(R1)
MOVD framesize+16(FP), R3 MOVD framesize+16(FP), R3
MOVD R3, FIXED_FRAME+16(R1) MOVD R3, FIXED_FRAME+16(R1)
MOVD ctxt+24(FP), R3
MOVD R3, FIXED_FRAME+24(R1)
MOVD $runtime·cgocallback_gofunc(SB), R12 MOVD $runtime·cgocallback_gofunc(SB), R12
MOVD R12, CTR MOVD R12, CTR
BL (CTR) BL (CTR)
RET RET
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize, uintptr ctxt)
// See cgocall.go for more details. // See cgocall.go for more details.
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24 TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
...@@ -654,12 +656,15 @@ havem: ...@@ -654,12 +656,15 @@ havem:
// so that the traceback will seamlessly trace back into // so that the traceback will seamlessly trace back into
// the earlier calls. // the earlier calls.
// //
// In the new goroutine, -16(SP) and -8(SP) are unused. // In the new goroutine, -8(SP) is unused (where SP refers to
// m->curg's SP while we're setting it up, before we've adjusted it).
MOVD m_curg(R8), g MOVD m_curg(R8), g
BL runtime·save_g(SB) BL runtime·save_g(SB)
MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
MOVD (g_sched+gobuf_pc)(g), R5 MOVD (g_sched+gobuf_pc)(g), R5
MOVD R5, -(FIXED_FRAME+16)(R4) MOVD R5, -(FIXED_FRAME+8)(R4)
MOVD ctxt+24(FP), R1
MOVD R1, -(FIXED_FRAME+16)(R4)
MOVD $-(FIXED_FRAME+16)(R4), R1 MOVD $-(FIXED_FRAME+16)(R4), R1
BL runtime·cgocallbackg(SB) BL runtime·cgocallbackg(SB)
......
...@@ -541,23 +541,25 @@ g0: ...@@ -541,23 +541,25 @@ g0:
MOVW R2, ret+16(FP) MOVW R2, ret+16(FP)
RET RET
// cgocallback(void (*fn)(void*), void *frame, uintptr framesize) // cgocallback(void (*fn)(void*), void *frame, uintptr framesize, uintptr ctxt)
// Turn the fn into a Go func (by taking its address) and call // Turn the fn into a Go func (by taking its address) and call
// cgocallback_gofunc. // cgocallback_gofunc.
TEXT runtime·cgocallback(SB),NOSPLIT,$24-24 TEXT runtime·cgocallback(SB),NOSPLIT,$32-32
MOVD $fn+0(FP), R3 MOVD $fn+0(FP), R3
MOVD R3, 8(R15) MOVD R3, 8(R15)
MOVD frame+8(FP), R3 MOVD frame+8(FP), R3
MOVD R3, 16(R15) MOVD R3, 16(R15)
MOVD framesize+16(FP), R3 MOVD framesize+16(FP), R3
MOVD R3, 24(R15) MOVD R3, 24(R15)
MOVD ctxt+24(FP), R3
MOVD R3, 32(R15)
MOVD $runtime·cgocallback_gofunc(SB), R3 MOVD $runtime·cgocallback_gofunc(SB), R3
BL (R3) BL (R3)
RET RET
// cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize) // cgocallback_gofunc(FuncVal*, void *frame, uintptr framesize, uintptr ctxt)
// See cgocall.go for more details. // See cgocall.go for more details.
TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-24 TEXT ·cgocallback_gofunc(SB),NOSPLIT,$16-32
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
// Load m and g from thread-local storage. // Load m and g from thread-local storage.
...@@ -622,12 +624,15 @@ havem: ...@@ -622,12 +624,15 @@ havem:
// so that the traceback will seamlessly trace back into // so that the traceback will seamlessly trace back into
// the earlier calls. // the earlier calls.
// //
// In the new goroutine, -16(SP) and -8(SP) are unused. // In the new goroutine, -8(SP) is unused (where SP refers to
// m->curg's SP while we're setting it up, before we've adjusted it).
MOVD m_curg(R8), g MOVD m_curg(R8), g
BL runtime·save_g(SB) BL runtime·save_g(SB)
MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4 MOVD (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
MOVD (g_sched+gobuf_pc)(g), R5 MOVD (g_sched+gobuf_pc)(g), R5
MOVD R5, -24(R4) MOVD R5, -24(R4)
MOVD ctxt+24(FP), R5
MOVD R5, -16(R4)
MOVD $-24(R4), R15 MOVD $-24(R4), R15
BL runtime·cgocallbackg(SB) BL runtime·cgocallbackg(SB)
......
...@@ -17,6 +17,7 @@ import "unsafe" ...@@ -17,6 +17,7 @@ import "unsafe"
//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create //go:linkname _cgo_sys_thread_create _cgo_sys_thread_create
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done //go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
//go:linkname _cgo_callers _cgo_callers //go:linkname _cgo_callers _cgo_callers
//go:linkname _cgo_set_context_function _cgo_set_context_function
var ( var (
_cgo_init unsafe.Pointer _cgo_init unsafe.Pointer
...@@ -26,6 +27,7 @@ var ( ...@@ -26,6 +27,7 @@ var (
_cgo_sys_thread_create unsafe.Pointer _cgo_sys_thread_create unsafe.Pointer
_cgo_notify_runtime_init_done unsafe.Pointer _cgo_notify_runtime_init_done unsafe.Pointer
_cgo_callers unsafe.Pointer _cgo_callers unsafe.Pointer
_cgo_set_context_function unsafe.Pointer
) )
// iscgo is set to true by the runtime/cgo package // iscgo is set to true by the runtime/cgo package
......
...@@ -4,10 +4,9 @@ ...@@ -4,10 +4,9 @@
#include "textflag.h" #include "textflag.h"
/* // Called by C code generated by cmd/cgo.
* void crosscall2(void (*fn)(void*, int32), void*, int32) // func crosscall2(fn func(a unsafe.Pointer, n int32, ctxt uintptr), a unsafe.Pointer, n int32, ctxt uintptr)
* Save registers and call fn with two arguments. // Saves C callee-saved registers and calls fn with three arguments.
*/
TEXT crosscall2(SB),NOSPLIT,$0 TEXT crosscall2(SB),NOSPLIT,$0
PUSHL BP PUSHL BP
MOVL SP, BP MOVL SP, BP
...@@ -15,14 +14,16 @@ TEXT crosscall2(SB),NOSPLIT,$0 ...@@ -15,14 +14,16 @@ TEXT crosscall2(SB),NOSPLIT,$0
PUSHL SI PUSHL SI
PUSHL DI PUSHL DI
SUBL $8, SP SUBL $12, SP
MOVL 20(BP), AX
MOVL AX, 8(SP)
MOVL 16(BP), AX MOVL 16(BP), AX
MOVL AX, 4(SP) MOVL AX, 4(SP)
MOVL 12(BP), AX MOVL 12(BP), AX
MOVL AX, 0(SP) MOVL AX, 0(SP)
MOVL 8(BP), AX MOVL 8(BP), AX
CALL AX CALL AX
ADDL $8, SP ADDL $12, SP
POPL DI POPL DI
POPL SI POPL SI
......
...@@ -4,72 +4,73 @@ ...@@ -4,72 +4,73 @@
#include "textflag.h" #include "textflag.h"
/* // Called by C code generated by cmd/cgo.
* void crosscall2(void (*fn)(void*, int32), void*, int32) // func crosscall2(fn func(a unsafe.Pointer, n int32, ctxt uintptr), a unsafe.Pointer, n int32, ctxt uintptr)
* Save registers and call fn with two arguments. // Saves C callee-saved registers and calls fn with three arguments.
*/
TEXT crosscall2(SB),NOSPLIT,$0 TEXT crosscall2(SB),NOSPLIT,$0
#ifndef GOOS_windows #ifndef GOOS_windows
SUBQ $0x58, SP /* keeps stack pointer 32-byte aligned */ SUBQ $0x58, SP /* keeps stack pointer 32-byte aligned */
#else #else
SUBQ $0xf8, SP /* also need to save xmm6 - xmm15 */ SUBQ $0x118, SP /* also need to save xmm6 - xmm15 */
#endif #endif
MOVQ BX, 0x10(SP) MOVQ BX, 0x18(SP)
MOVQ BP, 0x18(SP) MOVQ BP, 0x20(SP)
MOVQ R12, 0x20(SP) MOVQ R12, 0x28(SP)
MOVQ R13, 0x28(SP) MOVQ R13, 0x30(SP)
MOVQ R14, 0x30(SP) MOVQ R14, 0x38(SP)
MOVQ R15, 0x38(SP) MOVQ R15, 0x40(SP)
#ifdef GOOS_windows #ifdef GOOS_windows
// Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15 and XMM6 -- XMM15. // Win64 save RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15 and XMM6 -- XMM15.
MOVQ DI, 0x40(SP) MOVQ DI, 0x48(SP)
MOVQ SI, 0x48(SP) MOVQ SI, 0x50(SP)
MOVUPS X6, 0x50(SP) MOVUPS X6, 0x60(SP)
MOVUPS X7, 0x60(SP) MOVUPS X7, 0x70(SP)
MOVUPS X8, 0x70(SP) MOVUPS X8, 0x80(SP)
MOVUPS X9, 0x80(SP) MOVUPS X9, 0x90(SP)
MOVUPS X10, 0x90(SP) MOVUPS X10, 0xa0(SP)
MOVUPS X11, 0xa0(SP) MOVUPS X11, 0xb0(SP)
MOVUPS X12, 0xb0(SP) MOVUPS X12, 0xc0(SP)
MOVUPS X13, 0xc0(SP) MOVUPS X13, 0xd0(SP)
MOVUPS X14, 0xd0(SP) MOVUPS X14, 0xe0(SP)
MOVUPS X15, 0xe0(SP) MOVUPS X15, 0xf0(SP)
MOVQ DX, 0(SP) /* arg */ MOVQ DX, 0x0(SP) /* arg */
MOVQ R8, 8(SP) /* argsize (includes padding) */ MOVQ R8, 0x8(SP) /* argsize (includes padding) */
MOVQ R9, 0x10(SP) /* ctxt */
CALL CX /* fn */ CALL CX /* fn */
MOVQ 0x40(SP), DI MOVQ 0x48(SP), DI
MOVQ 0x48(SP), SI MOVQ 0x50(SP), SI
MOVUPS 0x50(SP), X6 MOVUPS 0x60(SP), X6
MOVUPS 0x60(SP), X7 MOVUPS 0x70(SP), X7
MOVUPS 0x70(SP), X8 MOVUPS 0x80(SP), X8
MOVUPS 0x80(SP), X9 MOVUPS 0x90(SP), X9
MOVUPS 0x90(SP), X10 MOVUPS 0xa0(SP), X10
MOVUPS 0xa0(SP), X11 MOVUPS 0xb0(SP), X11
MOVUPS 0xb0(SP), X12 MOVUPS 0xc0(SP), X12
MOVUPS 0xc0(SP), X13 MOVUPS 0xd0(SP), X13
MOVUPS 0xd0(SP), X14 MOVUPS 0xe0(SP), X14
MOVUPS 0xe0(SP), X15 MOVUPS 0xf0(SP), X15
#else #else
MOVQ SI, 0(SP) /* arg */ MOVQ SI, 0x0(SP) /* arg */
MOVQ DX, 8(SP) /* argsize (includes padding) */ MOVQ DX, 0x8(SP) /* argsize (includes padding) */
MOVQ CX, 0x10(SP) /* ctxt */
CALL DI /* fn */ CALL DI /* fn */
#endif #endif
MOVQ 0x10(SP), BX MOVQ 0x18(SP), BX
MOVQ 0x18(SP), BP MOVQ 0x20(SP), BP
MOVQ 0x20(SP), R12 MOVQ 0x28(SP), R12
MOVQ 0x28(SP), R13 MOVQ 0x30(SP), R13
MOVQ 0x30(SP), R14 MOVQ 0x38(SP), R14
MOVQ 0x38(SP), R15 MOVQ 0x40(SP), R15
#ifndef GOOS_windows #ifndef GOOS_windows
ADDQ $0x58, SP ADDQ $0x58, SP
#else #else
ADDQ $0xf8, SP ADDQ $0x118, SP
#endif #endif
RET RET
...@@ -4,21 +4,20 @@ ...@@ -4,21 +4,20 @@
#include "textflag.h" #include "textflag.h"
/* // Called by C code generated by cmd/cgo.
* void crosscall2(void (*fn)(void*, int32), void*, int32) // func crosscall2(fn func(a unsafe.Pointer, n int32, ctxt uintptr), a unsafe.Pointer, n int32, ctxt uintptr)
* Save registers and call fn with two arguments. // Saves C callee-saved registers and calls fn with three arguments.
*/
TEXT crosscall2(SB),NOSPLIT,$-4 TEXT crosscall2(SB),NOSPLIT,$-4
/* /*
* We still need to save all callee save register as before, and then * We still need to save all callee save register as before, and then
* push 2 args for fn (R1 and R2). * push 3 args for fn (R1, R2, R3).
* Also note that at procedure entry in gc world, 4(R13) will be the * Also note that at procedure entry in gc world, 4(R13) will be the
* first arg, so we must push another dummy reg (R0) for 0(R13). * first arg, so we must push another dummy reg (R0) for 0(R13).
* Additionally, runtime·load_g will clobber R0, so we need to save R0 * Additionally, runtime·load_g will clobber R0, so we need to save R0
* nevertheless. * nevertheless.
*/ */
MOVM.WP [R0, R1, R2, R4, R5, R6, R7, R8, R9, g, R11, R12, R14], (R13) MOVM.WP [R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, g, R11, R12, R14], (R13)
BL runtime·load_g(SB) BL runtime·load_g(SB)
MOVW R15, R14 // R15 is PC. MOVW R15, R14 // R15 is PC.
MOVW 0(R13), R15 MOVW 0(R13), R15
MOVM.IAW (R13), [R0, R1, R2, R4, R5, R6, R7, R8, R9, g, R11, R12, R15] MOVM.IAW (R13), [R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, g, R11, R12, R15]
...@@ -4,14 +4,13 @@ ...@@ -4,14 +4,13 @@
#include "textflag.h" #include "textflag.h"
/* // Called by C code generated by cmd/cgo.
* void crosscall2(void (*fn)(void*, int32), void*, int32) // func crosscall2(fn func(a unsafe.Pointer, n int32, ctxt uintptr), a unsafe.Pointer, n int32, ctxt uintptr)
* Save registers and call fn with two arguments. // Saves C callee-saved registers and calls fn with three arguments.
*/
TEXT crosscall2(SB),NOSPLIT,$-8 TEXT crosscall2(SB),NOSPLIT,$-8
/* /*
* We still need to save all callee save register as before, and then * We still need to save all callee save register as before, and then
* push 2 args for fn (R1 and R2). * push 3 args for fn (R1, R2, R3).
* Also note that at procedure entry in gc world, 8(RSP) will be the * Also note that at procedure entry in gc world, 8(RSP) will be the
* first arg. * first arg.
* TODO(minux): use LDP/STP here if it matters. * TODO(minux): use LDP/STP here if it matters.
...@@ -19,26 +18,27 @@ TEXT crosscall2(SB),NOSPLIT,$-8 ...@@ -19,26 +18,27 @@ TEXT crosscall2(SB),NOSPLIT,$-8
SUB $(8*24), RSP SUB $(8*24), RSP
MOVD R1, (8*1)(RSP) MOVD R1, (8*1)(RSP)
MOVD R2, (8*2)(RSP) MOVD R2, (8*2)(RSP)
MOVD R19, (8*3)(RSP) MOVD R3, (8*3)(RSP)
MOVD R20, (8*4)(RSP) MOVD R19, (8*4)(RSP)
MOVD R21, (8*5)(RSP) MOVD R20, (8*5)(RSP)
MOVD R22, (8*6)(RSP) MOVD R21, (8*6)(RSP)
MOVD R23, (8*7)(RSP) MOVD R22, (8*7)(RSP)
MOVD R24, (8*8)(RSP) MOVD R23, (8*8)(RSP)
MOVD R25, (8*9)(RSP) MOVD R24, (8*9)(RSP)
MOVD R26, (8*10)(RSP) MOVD R25, (8*10)(RSP)
MOVD R27, (8*11)(RSP) MOVD R26, (8*11)(RSP)
MOVD g, (8*12)(RSP) MOVD R27, (8*12)(RSP)
MOVD R29, (8*13)(RSP) MOVD g, (8*13)(RSP)
MOVD R30, (8*14)(RSP) MOVD R29, (8*14)(RSP)
FMOVD F8, (8*15)(RSP) MOVD R30, (8*15)(RSP)
FMOVD F9, (8*16)(RSP) FMOVD F8, (8*16)(RSP)
FMOVD F10, (8*17)(RSP) FMOVD F9, (8*17)(RSP)
FMOVD F11, (8*18)(RSP) FMOVD F10, (8*18)(RSP)
FMOVD F12, (8*19)(RSP) FMOVD F11, (8*19)(RSP)
FMOVD F13, (8*20)(RSP) FMOVD F12, (8*20)(RSP)
FMOVD F14, (8*21)(RSP) FMOVD F13, (8*21)(RSP)
FMOVD F15, (8*22)(RSP) FMOVD F14, (8*22)(RSP)
FMOVD F15, (8*23)(RSP)
MOVD R0, R19 MOVD R0, R19
...@@ -49,25 +49,26 @@ TEXT crosscall2(SB),NOSPLIT,$-8 ...@@ -49,25 +49,26 @@ TEXT crosscall2(SB),NOSPLIT,$-8
MOVD (8*1)(RSP), R1 MOVD (8*1)(RSP), R1
MOVD (8*2)(RSP), R2 MOVD (8*2)(RSP), R2
MOVD (8*3)(RSP), R19 MOVD (8*3)(RSP), R3
MOVD (8*4)(RSP), R20 MOVD (8*4)(RSP), R19
MOVD (8*5)(RSP), R21 MOVD (8*5)(RSP), R20
MOVD (8*6)(RSP), R22 MOVD (8*6)(RSP), R21
MOVD (8*7)(RSP), R23 MOVD (8*7)(RSP), R22
MOVD (8*8)(RSP), R24 MOVD (8*8)(RSP), R23
MOVD (8*9)(RSP), R25 MOVD (8*9)(RSP), R24
MOVD (8*10)(RSP), R26 MOVD (8*10)(RSP), R25
MOVD (8*11)(RSP), R27 MOVD (8*11)(RSP), R26
MOVD (8*12)(RSP), g MOVD (8*12)(RSP), R27
MOVD (8*13)(RSP), R29 MOVD (8*13)(RSP), g
MOVD (8*14)(RSP), R30 MOVD (8*14)(RSP), R29
FMOVD (8*15)(RSP), F8 MOVD (8*15)(RSP), R30
FMOVD (8*16)(RSP), F9 FMOVD (8*16)(RSP), F8
FMOVD (8*17)(RSP), F10 FMOVD (8*17)(RSP), F9
FMOVD (8*18)(RSP), F11 FMOVD (8*18)(RSP), F10
FMOVD (8*19)(RSP), F12 FMOVD (8*19)(RSP), F11
FMOVD (8*20)(RSP), F13 FMOVD (8*20)(RSP), F12
FMOVD (8*21)(RSP), F14 FMOVD (8*21)(RSP), F13
FMOVD (8*22)(RSP), F15 FMOVD (8*22)(RSP), F14
FMOVD (8*23)(RSP), F15
ADD $(8*24), RSP ADD $(8*24), RSP
RET RET
...@@ -7,11 +7,9 @@ ...@@ -7,11 +7,9 @@
#include "textflag.h" #include "textflag.h"
#include "asm_ppc64x.h" #include "asm_ppc64x.h"
/* // Called by C code generated by cmd/cgo.
* void crosscall2(void (*fn)(void*, int32), void*, int32) // func crosscall2(fn func(a unsafe.Pointer, n int32, ctxt uintptr), a unsafe.Pointer, n int32, ctxt uintptr)
* Save registers and call fn with two arguments. // Saves C callee-saved registers and calls fn with three arguments.
* crosscall2 obeys the C ABI; fn obeys the Go ABI.
*/
TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
// TODO(austin): ABI v1 (fn is probably a function descriptor) // TODO(austin): ABI v1 (fn is probably a function descriptor)
...@@ -22,7 +20,7 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 ...@@ -22,7 +20,7 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
BL saveregs2<>(SB) BL saveregs2<>(SB)
MOVDU R1, (-288-2*8-FIXED_FRAME)(R1) MOVDU R1, (-288-3*8-FIXED_FRAME)(R1)
// Initialize Go ABI environment // Initialize Go ABI environment
BL runtime·reginit(SB) BL runtime·reginit(SB)
...@@ -32,6 +30,7 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 ...@@ -32,6 +30,7 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
MOVD R3, CTR MOVD R3, CTR
MOVD R4, FIXED_FRAME+0(R1) MOVD R4, FIXED_FRAME+0(R1)
MOVD R5, FIXED_FRAME+8(R1) MOVD R5, FIXED_FRAME+8(R1)
MOVD R6, FIXED_FRAME+16(R1)
BL (CTR) BL (CTR)
ADD $(288+2*8+FIXED_FRAME), R1 ADD $(288+2*8+FIXED_FRAME), R1
......
...@@ -4,11 +4,9 @@ ...@@ -4,11 +4,9 @@
#include "textflag.h" #include "textflag.h"
/* // Called by C code generated by cmd/cgo.
* void crosscall2(void (*fn)(void*, int32), void*, int32) // func crosscall2(fn func(a unsafe.Pointer, n int32, ctxt uintptr), a unsafe.Pointer, n int32, ctxt uintptr)
* Save registers and call fn with two arguments. // Saves C callee-saved registers and calls fn with three arguments.
* crosscall2 obeys the C ABI; fn obeys the Go ABI.
*/
TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
// Start with standard C stack frame layout and linkage // Start with standard C stack frame layout and linkage
...@@ -24,14 +22,15 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 ...@@ -24,14 +22,15 @@ TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0
XOR R0, R0 XOR R0, R0
BL runtime·load_g(SB) BL runtime·load_g(SB)
// Allocate 24 bytes on the stack // Allocate 32 bytes on the stack
SUB $24, R15 SUB $32, R15
MOVD R3, 8(R15) // arg1 MOVD R3, 8(R15) // arg1
MOVW R4, 16(R15) // arg2 MOVW R4, 16(R15) // arg2
BL (R2) // fn(arg1, arg2) MOVD R5, 24(R15) // arg3
BL (R2) // fn(arg1, arg2, arg3)
ADD $24, R15 ADD $32, R15
// Restore R6-R15, F0, F2, F4 and F6 // Restore R6-R15, F0, F2, F4 and F6
LMG 48(R15), R6, R15 LMG 48(R15), R6, R15
......
...@@ -11,7 +11,7 @@ import "unsafe" ...@@ -11,7 +11,7 @@ import "unsafe"
// cgocallback is defined in runtime // cgocallback is defined in runtime
//go:linkname _runtime_cgocallback runtime.cgocallback //go:linkname _runtime_cgocallback runtime.cgocallback
func _runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr) func _runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr, uintptr)
// The declaration of crosscall2 is: // The declaration of crosscall2 is:
// void crosscall2(void (*fn)(void *, int), void *, int); // void crosscall2(void (*fn)(void *, int), void *, int);
...@@ -19,6 +19,10 @@ func _runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr) ...@@ -19,6 +19,10 @@ func _runtime_cgocallback(unsafe.Pointer, unsafe.Pointer, uintptr)
// We need to export the symbol crosscall2 in order to support // We need to export the symbol crosscall2 in order to support
// callbacks from shared libraries. This applies regardless of // callbacks from shared libraries. This applies regardless of
// linking mode. // linking mode.
//
// Compatibility note: crosscall2 actually takes four arguments, but
// it works to call it with three arguments when calling _cgo_panic.
// That is supported for backward compatibility.
//go:cgo_export_static crosscall2 //go:cgo_export_static crosscall2
//go:cgo_export_dynamic crosscall2 //go:cgo_export_dynamic crosscall2
...@@ -39,7 +43,7 @@ var _runtime_cgo_panic_internal byte ...@@ -39,7 +43,7 @@ var _runtime_cgo_panic_internal byte
//go:nosplit //go:nosplit
//go:norace //go:norace
func _cgo_panic(a unsafe.Pointer, n int32) { func _cgo_panic(a unsafe.Pointer, n int32) {
_runtime_cgocallback(unsafe.Pointer(&_runtime_cgo_panic_internal), a, uintptr(n)) _runtime_cgocallback(unsafe.Pointer(&_runtime_cgo_panic_internal), a, uintptr(n), 0)
} }
//go:cgo_import_static x_cgo_init //go:cgo_import_static x_cgo_init
...@@ -92,5 +96,13 @@ var _cgo_sys_thread_create = &x_cgo_sys_thread_create ...@@ -92,5 +96,13 @@ var _cgo_sys_thread_create = &x_cgo_sys_thread_create
var x_cgo_notify_runtime_init_done byte var x_cgo_notify_runtime_init_done byte
var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done
// Sets the traceback context function. See runtime.SetCgoTraceback.
//go:cgo_import_static x_cgo_set_context_function
//go:linkname x_cgo_set_context_function x_cgo_set_context_function
//go:linkname _cgo_set_context_function _cgo_set_context_function
var x_cgo_set_context_function byte
var _cgo_set_context_function = &x_cgo_set_context_function
//go:cgo_export_static _cgo_topofstack //go:cgo_export_static _cgo_topofstack
//go:cgo_export_dynamic _cgo_topofstack //go:cgo_export_dynamic _cgo_topofstack
// Copyright 2016 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
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
#include "libcgo.h"
// The context function, used when tracing back C calls into Go.
void (*x_cgo_context_function)(struct context_arg*);
// Sets the context function to call to record the traceback context
// when calling a Go function from C code. Called from runtime.SetCgoTraceback.
void x_cgo_set_context_function(void (*context)(struct context_arg*)) {
x_cgo_context_function = context;
}
// Releases the cgo traceback context.
void _cgo_release_context(uintptr_t ctxt) {
if (ctxt != 0 && x_cgo_context_function != nil) {
struct context_arg arg;
arg.Context = ctxt;
(*x_cgo_context_function)(&arg);
}
}
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> // strerror #include <string.h> // strerror
#include "libcgo.h"
static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER; static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER;
...@@ -24,13 +25,21 @@ x_cgo_sys_thread_create(void* (*func)(void*), void* arg) { ...@@ -24,13 +25,21 @@ x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
} }
} }
void uintptr_t
_cgo_wait_runtime_init_done() { _cgo_wait_runtime_init_done() {
pthread_mutex_lock(&runtime_init_mu); pthread_mutex_lock(&runtime_init_mu);
while (runtime_init_done == 0) { while (runtime_init_done == 0) {
pthread_cond_wait(&runtime_init_cond, &runtime_init_mu); pthread_cond_wait(&runtime_init_cond, &runtime_init_mu);
} }
pthread_mutex_unlock(&runtime_init_mu); pthread_mutex_unlock(&runtime_init_mu);
if (x_cgo_context_function != nil) {
struct context_arg arg;
arg.Context = 0;
(*x_cgo_context_function)(&arg);
return arg.Context;
}
return 0;
} }
void void
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "libcgo.h"
void void
x_cgo_sys_thread_create(void* (*func)(void*), void* arg) { x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
...@@ -11,12 +12,20 @@ x_cgo_sys_thread_create(void* (*func)(void*), void* arg) { ...@@ -11,12 +12,20 @@ x_cgo_sys_thread_create(void* (*func)(void*), void* arg) {
abort(); abort();
} }
void uintptr_t
_cgo_wait_runtime_init_done() { _cgo_wait_runtime_init_done() {
// TODO(spetrovic): implement this method. // TODO(spetrovic): implement this method.
if (x_cgo_context_function != nil) {
struct context_arg arg;
arg.Context = 0;
(*x_cgo_context_function)(&arg);
return arg.Context;
}
return 0;
} }
void void
x_cgo_notify_runtime_init_done(void* dummy) { x_cgo_notify_runtime_init_done(void* dummy) {
// TODO(spetrovic): implement this method. // TODO(spetrovic): implement this method.
} }
\ No newline at end of file
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "libcgo.h"
static volatile long runtime_init_once_gate = 0; static volatile long runtime_init_once_gate = 0;
static volatile long runtime_init_once_done = 0; static volatile long runtime_init_once_done = 0;
...@@ -66,12 +68,20 @@ _cgo_is_runtime_initialized() { ...@@ -66,12 +68,20 @@ _cgo_is_runtime_initialized() {
return status; return status;
} }
void uintptr_t
_cgo_wait_runtime_init_done() { _cgo_wait_runtime_init_done() {
_cgo_maybe_run_preinit(); _cgo_maybe_run_preinit();
while (!_cgo_is_runtime_initialized()) { while (!_cgo_is_runtime_initialized()) {
WaitForSingleObject(runtime_init_wait, INFINITE); WaitForSingleObject(runtime_init_wait, INFINITE);
} }
if (x_cgo_context_function != nil) {
struct context_arg arg;
arg.Context = 0;
(*x_cgo_context_function)(&arg);
return arg.Context;
}
return 0;
} }
void void
......
...@@ -57,8 +57,10 @@ void _cgo_sys_thread_start(ThreadStart *ts); ...@@ -57,8 +57,10 @@ void _cgo_sys_thread_start(ThreadStart *ts);
/* /*
* Waits for the Go runtime to be initialized (OS dependent). * Waits for the Go runtime to be initialized (OS dependent).
* If runtime.SetCgoTraceback is used to set a context function,
* calls the context function and returns the context value.
*/ */
void _cgo_wait_runtime_init_done(); uintptr_t _cgo_wait_runtime_init_done();
/* /*
* Call fn in the 6c world. * Call fn in the 6c world.
...@@ -84,3 +86,11 @@ void darwin_arm_init_thread_exception_port(void); ...@@ -84,3 +86,11 @@ void darwin_arm_init_thread_exception_port(void);
* Starts a mach message server processing EXC_BAD_ACCESS. * Starts a mach message server processing EXC_BAD_ACCESS.
*/ */
void darwin_arm_init_mach_exception_handler(void); void darwin_arm_init_mach_exception_handler(void);
/*
* The cgo context function. See runtime.SetCgoTraceback.
*/
struct context_arg {
uintptr_t Context;
};
extern void (*x_cgo_context_function)(struct context_arg*);
...@@ -166,7 +166,7 @@ func cfree(p unsafe.Pointer) { ...@@ -166,7 +166,7 @@ func cfree(p unsafe.Pointer) {
// Call from C back to Go. // Call from C back to Go.
//go:nosplit //go:nosplit
func cgocallbackg() { func cgocallbackg(ctxt uintptr) {
gp := getg() gp := getg()
if gp != gp.m.curg { if gp != gp.m.curg {
println("runtime: bad g in cgocallback") println("runtime: bad g in cgocallback")
...@@ -184,20 +184,43 @@ func cgocallbackg() { ...@@ -184,20 +184,43 @@ func cgocallbackg() {
savedsp := unsafe.Pointer(gp.syscallsp) savedsp := unsafe.Pointer(gp.syscallsp)
savedpc := gp.syscallpc savedpc := gp.syscallpc
exitsyscall(0) // coming out of cgo call exitsyscall(0) // coming out of cgo call
cgocallbackg1()
cgocallbackg1(ctxt)
// going back to cgo call // going back to cgo call
reentersyscall(savedpc, uintptr(savedsp)) reentersyscall(savedpc, uintptr(savedsp))
gp.m.syscall = syscall gp.m.syscall = syscall
} }
func cgocallbackg1() { func cgocallbackg1(ctxt uintptr) {
gp := getg() gp := getg()
if gp.m.needextram { if gp.m.needextram {
gp.m.needextram = false gp.m.needextram = false
systemstack(newextram) systemstack(newextram)
} }
if ctxt != 0 {
s := append(gp.cgoCtxt, ctxt)
// Now we need to set gp.cgoCtxt = s, but we could get
// a SIGPROF signal while manipulating the slice, and
// the SIGPROF handler could pick up gp.cgoCtxt while
// tracing up the stack. We need to ensure that the
// handler always sees a valid slice, so set the
// values in an order such that it always does.
p := (*slice)(unsafe.Pointer(&gp.cgoCtxt))
atomicstorep(unsafe.Pointer(&p.array), unsafe.Pointer(&s[0]))
p.cap = cap(s)
p.len = len(s)
defer func(gp *g) {
// Decrease the length of the slice by one, safely.
p := (*slice)(unsafe.Pointer(&gp.cgoCtxt))
p.len--
}(gp)
}
if gp.m.ncgo == 0 { if gp.m.ncgo == 0 {
// The C call to Go came from a thread not currently running // The C call to Go came from a thread not currently running
// any Go. In the case of -buildmode=c-archive or c-shared, // any Go. In the case of -buildmode=c-archive or c-shared,
...@@ -236,13 +259,13 @@ func cgocallbackg1() { ...@@ -236,13 +259,13 @@ func cgocallbackg1() {
// SP and the stack frame and between the stack frame and the arguments. // SP and the stack frame and between the stack frame and the arguments.
cb = (*args)(unsafe.Pointer(sp + 5*sys.PtrSize)) cb = (*args)(unsafe.Pointer(sp + 5*sys.PtrSize))
case "amd64": case "amd64":
// On amd64, stack frame is one word, plus caller PC. // On amd64, stack frame is two words, plus caller PC.
if framepointer_enabled { if framepointer_enabled {
// In this case, there's also saved BP. // In this case, there's also saved BP.
cb = (*args)(unsafe.Pointer(sp + 3*sys.PtrSize)) cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
break break
} }
cb = (*args)(unsafe.Pointer(sp + 2*sys.PtrSize)) cb = (*args)(unsafe.Pointer(sp + 3*sys.PtrSize))
case "386": case "386":
// On 386, stack frame is three words, plus caller PC. // On 386, stack frame is three words, plus caller PC.
cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize)) cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
......
...@@ -221,3 +221,14 @@ func TestCgoCrashTraceback(t *testing.T) { ...@@ -221,3 +221,14 @@ func TestCgoCrashTraceback(t *testing.T) {
} }
} }
} }
func TestCgoTracebackContext(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skipf("test does not work on %s/%s", runtime.GOOS, runtime.GOARCH)
}
got := runTestProg(t, "testprogcgo", "TracebackContext")
want := "OK\n"
if got != want {
t.Errorf("expected %q got %v", want, got)
}
}
...@@ -352,7 +352,8 @@ type g struct { ...@@ -352,7 +352,8 @@ type g struct {
gopc uintptr // pc of go statement that created this goroutine gopc uintptr // pc of go statement that created this goroutine
startpc uintptr // pc of goroutine function startpc uintptr // pc of goroutine function
racectx uintptr racectx uintptr
waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
cgoCtxt []uintptr // cgo traceback context
// Per-G GC state // Per-G GC state
......
...@@ -18,6 +18,10 @@ type Frames struct { ...@@ -18,6 +18,10 @@ type Frames struct {
// ci.callers[0] is the address of the faulting instruction // ci.callers[0] is the address of the faulting instruction
// instead of the return address of the call. // instead of the return address of the call.
wasPanic bool wasPanic bool
// Frames to return for subsequent calls to the Next method.
// Used for non-Go frames.
frames *[]Frame
} }
// Frame is the information returned by Frames for each call frame. // Frame is the information returned by Frames for each call frame.
...@@ -46,12 +50,23 @@ type Frame struct { ...@@ -46,12 +50,23 @@ type Frame struct {
// prepares to return function/file/line information. // prepares to return function/file/line information.
// Do not change the slice until you are done with the Frames. // Do not change the slice until you are done with the Frames.
func CallersFrames(callers []uintptr) *Frames { func CallersFrames(callers []uintptr) *Frames {
return &Frames{callers, false} return &Frames{callers: callers}
} }
// Next returns frame information for the next caller. // Next returns frame information for the next caller.
// If more is false, there are no more callers (the Frame value is valid). // If more is false, there are no more callers (the Frame value is valid).
func (ci *Frames) Next() (frame Frame, more bool) { func (ci *Frames) Next() (frame Frame, more bool) {
if ci.frames != nil {
// We have saved up frames to return.
f := (*ci.frames)[0]
if len(*ci.frames) == 1 {
ci.frames = nil
} else {
*ci.frames = (*ci.frames)[1:]
}
return f, ci.frames != nil || len(ci.callers) > 0
}
if len(ci.callers) == 0 { if len(ci.callers) == 0 {
ci.wasPanic = false ci.wasPanic = false
return Frame{}, false return Frame{}, false
...@@ -62,6 +77,9 @@ func (ci *Frames) Next() (frame Frame, more bool) { ...@@ -62,6 +77,9 @@ func (ci *Frames) Next() (frame Frame, more bool) {
f := FuncForPC(pc) f := FuncForPC(pc)
if f == nil { if f == nil {
ci.wasPanic = false ci.wasPanic = false
if cgoSymbolizer != nil {
return ci.cgoNext(pc, more)
}
return Frame{}, more return Frame{}, more
} }
...@@ -87,6 +105,54 @@ func (ci *Frames) Next() (frame Frame, more bool) { ...@@ -87,6 +105,54 @@ func (ci *Frames) Next() (frame Frame, more bool) {
return frame, more return frame, more
} }
// cgoNext returns frame information for pc, known to be a non-Go function,
// using the cgoSymbolizer hook.
func (ci *Frames) cgoNext(pc uintptr, more bool) (Frame, bool) {
arg := cgoSymbolizerArg{pc: pc}
callCgoSymbolizer(&arg)
if arg.file == nil && arg.funcName == nil {
// No useful information from symbolizer.
return Frame{}, more
}
var frames []Frame
for {
frames = append(frames, Frame{
PC: pc,
Func: nil,
Function: gostring(arg.funcName),
File: gostring(arg.file),
Line: int(arg.lineno),
Entry: arg.entry,
})
if arg.more == 0 {
break
}
callCgoSymbolizer(&arg)
}
// No more frames for this PC. Tell the symbolizer we are done.
// We don't try to maintain a single cgoSymbolizerArg for the
// whole use of Frames, because there would be no good way to tell
// the symbolizer when we are done.
arg.pc = 0
callCgoSymbolizer(&arg)
if len(frames) == 1 {
// Return a single frame.
return frames[0], more
}
// Return the first frame we saw and store the rest to be
// returned by later calls to Next.
rf := frames[0]
frames = frames[1:]
ci.frames = new([]Frame)
*ci.frames = frames
return rf, true
}
// NOTE: Func does not expose the actual unexported fields, because we return *Func // NOTE: Func does not expose the actual unexported fields, because we return *Func
// values to users, and we want to keep them from being able to overwrite the data // values to users, and we want to keep them from being able to overwrite the data
// with (say) *f = Func{}. // with (say) *f = Func{}.
......
// Copyright 2016 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.
// The __attribute__((weak)) used below doesn't seem to work on Windows.
// +build !windows
package main
// Test the context argument to SetCgoTraceback.
// Use fake context, traceback, and symbolizer functions.
/*
#include <stdlib.h>
#include <stdint.h>
// Use weak declarations so that we can define functions here even
// though we use //export in the Go code.
extern void tcContext(void*) __attribute__((weak));
extern void tcTraceback(void*) __attribute__((weak));
extern void tcSymbolizer(void*) __attribute__((weak));
extern void G1(void);
extern void G2(void);
static void C1() {
G1();
}
static void C2() {
G2();
}
struct cgoContextArg {
uintptr_t context;
};
struct cgoTracebackArg {
uintptr_t context;
uintptr_t* buf;
uintptr_t max;
};
struct cgoSymbolizerArg {
uintptr_t pc;
const char* file;
uintptr_t lineno;
const char* func;
uintptr_t entry;
uintptr_t more;
uintptr_t data;
};
// Global so that there is only one, weak so that //export works.
// Uses atomic adds and subtracts to catch the possibility of
// erroneous calls from multiple threads; that should be impossible in
// this test case, but we check just in case.
int contextCount __attribute__((weak));
static int getContextCount() {
return __sync_add_and_fetch(&contextCount, 0);
}
void tcContext(void* parg) {
struct cgoContextArg* arg = (struct cgoContextArg*)(parg);
if (arg->context == 0) {
arg->context = __sync_add_and_fetch(&contextCount, 1);
} else {
if (arg->context != __sync_add_and_fetch(&contextCount, 0)) {
abort();
}
__sync_sub_and_fetch(&contextCount, 1);
}
}
void tcTraceback(void* parg) {
int base, i;
struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
if (arg->context == 0) {
// This shouldn't happen in this program.
abort();
}
// Return a variable number of PC values.
base = arg->context << 8;
for (i = 0; i < arg->context; i++) {
if (i < arg->max) {
arg->buf[i] = base + i;
}
}
}
void tcSymbolizer(void *parg) {
struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(parg);
if (arg->pc == 0) {
return;
}
// Report two lines per PC returned by traceback, to test more handling.
arg->more = arg->file == NULL;
arg->file = "tracebackctxt.go";
arg->func = "cFunction";
arg->lineno = arg->pc + (arg->more << 16);
}
*/
import "C"
import (
"fmt"
"runtime"
"unsafe"
)
func init() {
register("TracebackContext", TracebackContext)
}
var tracebackOK bool
func TracebackContext() {
runtime.SetCgoTraceback(0, unsafe.Pointer(C.tcTraceback), unsafe.Pointer(C.tcContext), unsafe.Pointer(C.tcSymbolizer))
C.C1()
if got := C.getContextCount(); got != 0 {
fmt.Printf("at end contextCount == %d, expected 0\n", got)
tracebackOK = false
}
if tracebackOK {
fmt.Println("OK")
}
}
//export G1
func G1() {
C.C2()
}
//export G2
func G2() {
pc := make([]uintptr, 32)
n := runtime.Callers(0, pc)
cf := runtime.CallersFrames(pc[:n])
var frames []runtime.Frame
for {
frame, more := cf.Next()
frames = append(frames, frame)
if !more {
break
}
}
want := []struct {
function string
line int
}{
{"main.G2", 0},
{"cFunction", 0x10200},
{"cFunction", 0x200},
{"cFunction", 0x10201},
{"cFunction", 0x201},
{"main.G1", 0},
{"cFunction", 0x10100},
{"cFunction", 0x100},
{"main.TracebackContext", 0},
}
ok := true
i := 0
wantLoop:
for _, w := range want {
for ; i < len(frames); i++ {
if w.function == frames[i].Function {
if w.line != 0 && w.line != frames[i].Line {
fmt.Printf("found function %s at wrong line %#x (expected %#x)\n", w.function, frames[i].Line, w.line)
ok = false
}
i++
continue wantLoop
}
}
fmt.Printf("did not find function %s in\n", w.function)
for _, f := range frames {
fmt.Println(f)
}
ok = false
break
}
tracebackOK = ok
if got := C.getContextCount(); got != 2 {
fmt.Printf("at bottom contextCount == %d, expected 2\n", got)
tracebackOK = false
}
}
...@@ -172,6 +172,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -172,6 +172,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
frame.lr = lr0 frame.lr = lr0
} }
waspanic := false waspanic := false
cgoCtxt := gp.cgoCtxt
printing := pcbuf == nil && callback == nil printing := pcbuf == nil && callback == nil
_defer := gp._defer _defer := gp._defer
...@@ -252,6 +253,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -252,6 +253,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
sp = gp.m.curg.sched.sp sp = gp.m.curg.sched.sp
stkbarG = gp.m.curg stkbarG = gp.m.curg
stkbar = stkbarG.stkbar[stkbarG.stkbarPos:] stkbar = stkbarG.stkbar[stkbarG.stkbarPos:]
cgoCtxt = gp.m.curg.cgoCtxt
} }
frame.fp = sp + uintptr(funcspdelta(f, frame.pc, &cache)) frame.fp = sp + uintptr(funcspdelta(f, frame.pc, &cache))
if !usesLR { if !usesLR {
...@@ -413,6 +415,18 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in ...@@ -413,6 +415,18 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
n++ n++
skipped: skipped:
if f.entry == cgocallback_gofuncPC && len(cgoCtxt) > 0 {
ctxt := cgoCtxt[len(cgoCtxt)-1]
cgoCtxt = cgoCtxt[:len(cgoCtxt)-1]
// skip only applies to Go frames.
// callback != nil only used when we only care
// about Go frames.
if skip == 0 && callback == nil {
n = tracebackCgoContext(pcbuf, printing, ctxt, n, max)
}
}
waspanic = f.entry == sigpanicPC waspanic = f.entry == sigpanicPC
// Do not unwind past the bottom of the stack. // Do not unwind past the bottom of the stack.
...@@ -546,6 +560,39 @@ func getArgInfo(frame *stkframe, f *_func, needArgMap bool) (arglen uintptr, arg ...@@ -546,6 +560,39 @@ func getArgInfo(frame *stkframe, f *_func, needArgMap bool) (arglen uintptr, arg
return return
} }
// tracebackCgoContext handles tracing back a cgo context value, from
// the context argument to setCgoTraceback, for the gentraceback
// function. It returns the new value of n.
func tracebackCgoContext(pcbuf *uintptr, printing bool, ctxt uintptr, n, max int) int {
var cgoPCs [32]uintptr
cgoContextPCs(ctxt, cgoPCs[:])
var arg cgoSymbolizerArg
anySymbolized := false
for _, pc := range cgoPCs {
if pc == 0 || n >= max {
break
}
if pcbuf != nil {
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = pc
}
if printing {
if cgoSymbolizer == nil {
print("non-Go function at pc=", hex(pc), "\n")
} else {
c := printOneCgoTraceback(pc, max-n, &arg)
n += c - 1 // +1 a few lines down
anySymbolized = true
}
}
n++
}
if anySymbolized {
arg.pc = 0
callCgoSymbolizer(&arg)
}
return n
}
func printcreatedby(gp *g) { func printcreatedby(gp *g) {
// Show what created goroutine, except main goroutine (goid 1). // Show what created goroutine, except main goroutine (goid 1).
pc := gp.gopc pc := gp.gopc
...@@ -782,10 +829,11 @@ func isSystemGoroutine(gp *g) bool { ...@@ -782,10 +829,11 @@ func isSystemGoroutine(gp *g) bool {
// }; // };
// //
// If the Context field is 0, the context function is being called to // If the Context field is 0, the context function is being called to
// record the current traceback context. It should record whatever // record the current traceback context. It should record in the
// information is needed about the current point of execution to later // Context field whatever information is needed about the current
// produce a stack trace, probably the stack pointer and PC. In this // point of execution to later produce a stack trace, probably the
// case the context function will be called from C code. // stack pointer and PC. In this case the context function will be
// called from C code.
// //
// If the Context field is not 0, then it is a value returned by a // If the Context field is not 0, then it is a value returned by a
// previous call to the context function. This case is called when the // previous call to the context function. This case is called when the
...@@ -903,16 +951,18 @@ func SetCgoTraceback(version int, traceback, context, symbolizer unsafe.Pointer) ...@@ -903,16 +951,18 @@ func SetCgoTraceback(version int, traceback, context, symbolizer unsafe.Pointer)
if version != 0 { if version != 0 {
panic("unsupported version") panic("unsupported version")
} }
if context != nil {
panic("SetCgoTraceback: context function not yet implemented")
}
cgoTraceback = traceback cgoTraceback = traceback
cgoContext = context
cgoSymbolizer = symbolizer cgoSymbolizer = symbolizer
// The context function is called when a C function calls a Go
// function. As such it is only called by C code in runtime/cgo.
if _cgo_set_context_function != nil {
cgocall(_cgo_set_context_function, context)
}
} }
var cgoTraceback unsafe.Pointer var cgoTraceback unsafe.Pointer
var cgoContext unsafe.Pointer
var cgoSymbolizer unsafe.Pointer var cgoSymbolizer unsafe.Pointer
// cgoTracebackArg is the type passed to cgoTraceback. // cgoTracebackArg is the type passed to cgoTraceback.
...@@ -922,7 +972,7 @@ type cgoTracebackArg struct { ...@@ -922,7 +972,7 @@ type cgoTracebackArg struct {
max uintptr max uintptr
} }
// cgoContextArg is the type passed to cgoContext. // cgoContextArg is the type passed to the context function.
type cgoContextArg struct { type cgoContextArg struct {
context uintptr context uintptr
} }
...@@ -950,39 +1000,75 @@ func printCgoTraceback(callers *cgoCallers) { ...@@ -950,39 +1000,75 @@ func printCgoTraceback(callers *cgoCallers) {
return return
} }
call := cgocall
if panicking > 0 {
// We do not want to call into the scheduler when panicking.
call = asmcgocall
}
var arg cgoSymbolizerArg var arg cgoSymbolizerArg
for _, c := range callers { for _, c := range callers {
if c == 0 { if c == 0 {
break break
} }
arg.pc = c printOneCgoTraceback(c, 0x7fffffff, &arg)
for {
call(cgoSymbolizer, noescape(unsafe.Pointer(&arg)))
if arg.funcName != nil {
// Note that we don't print any argument
// information here, not even parentheses.
// The symbolizer must add that if
// appropriate.
println(gostringnocopy(arg.funcName))
} else {
println("non-Go function")
}
print("\t")
if arg.file != nil {
print(gostringnocopy(arg.file), ":", arg.lineno, " ")
}
print("pc=", hex(c), "\n")
if arg.more == 0 {
break
}
}
} }
arg.pc = 0 arg.pc = 0
call(cgoSymbolizer, noescape(unsafe.Pointer(&arg))) callCgoSymbolizer(&arg)
}
// printOneCgoTraceback prints the traceback of a single cgo caller.
// This can print more than one line because of inlining.
// Returns the number of frames printed.
func printOneCgoTraceback(pc uintptr, max int, arg *cgoSymbolizerArg) int {
c := 0
arg.pc = pc
for {
if c > max {
break
}
callCgoSymbolizer(arg)
if arg.funcName != nil {
// Note that we don't print any argument
// information here, not even parentheses.
// The symbolizer must add that if appropriate.
println(gostringnocopy(arg.funcName))
} else {
println("non-Go function")
}
print("\t")
if arg.file != nil {
print(gostringnocopy(arg.file), ":", arg.lineno, " ")
}
print("pc=", hex(c), "\n")
c++
if arg.more == 0 {
break
}
}
return c
}
// callCgoSymbolizer calls the cgoSymbolizer function.
func callCgoSymbolizer(arg *cgoSymbolizerArg) {
call := cgocall
if panicking > 0 || getg().m.curg != getg() {
// We do not want to call into the scheduler when panicking
// or when on the system stack.
call = asmcgocall
}
call(cgoSymbolizer, noescape(unsafe.Pointer(arg)))
}
// cgoContextPCs gets the PC values from a cgo traceback.
func cgoContextPCs(ctxt uintptr, buf []uintptr) {
if cgoTraceback == nil {
return
}
call := cgocall
if panicking > 0 || getg().m.curg != getg() {
// We do not want to call into the scheduler when panicking
// or when on the system stack.
call = asmcgocall
}
arg := cgoTracebackArg{
context: ctxt,
buf: (*uintptr)(noescape(unsafe.Pointer(&buf[0]))),
max: uintptr(len(buf)),
}
call(cgoTraceback, noescape(unsafe.Pointer(&arg)))
} }
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