1
0
mirror of https://github.com/golang/go synced 2024-11-22 03:54:39 -07:00

runtime: discard SIGPROF delivered to non-Go threads.

Signal handlers are global resources but many language
environments (Go, C++ at Google, etc) assume they have sole
ownership of a particular handler.  Signal handlers in
mixed-language applications must therefore be robust against
unexpected delivery of certain signals, such as SIGPROF.

The default Go signal handler runtime·sigtramp assumes that it
will never be called on a non-Go thread, but this assumption
is violated by when linking in C++ code that spawns threads.
Specifically, the handler asserts the thread has an associated
"m" (Go scheduler).

This CL is a very simple workaround: discard SIGPROF delivered to non-Go threads.  runtime.badsignal(int32) now receives the signal number; if it returns without panicking (e.g. sig==SIGPROF) the signal is discarded.

I don't think there is any really satisfactory solution to the
problem of signal-based profiling in a mixed-language
application.  It's not only the issue of handler clobbering,
but also that a C++ SIGPROF handler called in a Go thread
can't unwind the Go stack (and vice versa).  The best we can
hope for is not crashing.

Note:
- I've ported this to all POSIX platforms, except ARM-linux which already ignores unexpected signals on m-less threads.
- I've avoided tail-calling runtime.badsignal because AFAICT the 6a/6l don't support it.
- I've avoided hoisting 'push sig' (common to both function calls) because it makes the code harder to read.
- Fixed an (apparently incorrect?) docstring.

R=iant, rsc, minux.ma
CC=golang-dev
https://golang.org/cl/6498057
This commit is contained in:
Alan Donovan 2012-09-04 14:40:49 -04:00
parent b2458ff75c
commit 532dee3842
18 changed files with 67 additions and 19 deletions

View File

@ -135,6 +135,8 @@ runtime·setsig(int32 i, void (*fn)(int32, Siginfo*, void*, G*), bool restart)
if(restart)
sa.sa_flags |= SA_RESTART;
sa.sa_mask = ~0ULL;
// TODO(adonovan): Linux manpage says "sa_restorer element is
// obsolete and should not be used". Avoid it here, and test.
sa.sa_restorer = (void*)runtime·sigreturn;
if(fn == runtime·sighandler)
fn = (void*)runtime·sigtramp;

View File

@ -213,8 +213,8 @@ TEXT runtime·sigaction(SB),7,$0
// It is called with the following arguments on the stack:
// 0(FP) "return address" - ignored
// 4(FP) actual handler
// 8(FP) siginfo style - ignored
// 12(FP) signal number
// 8(FP) signal number
// 12(FP) siginfo style
// 16(FP) siginfo
// 20(FP) context
TEXT runtime·sigtramp(SB),7,$40
@ -223,8 +223,11 @@ TEXT runtime·sigtramp(SB),7,$40
// check that m exists
MOVL m(CX), BP
CMPL BP, $0
JNE 2(PC)
JNE 5(PC)
MOVL sig+8(FP), BX
MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVL g(CX), DI

View File

@ -173,8 +173,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
JNE 2(PC)
JNE 4(PC)
MOVL DX, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVQ g(BX), R10

View File

@ -162,8 +162,11 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
JNE 2(PC)
JNE 5(PC)
MOVL signo+0(FP), BX
MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVL g(CX), DI

View File

@ -138,8 +138,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
JNE 2(PC)
JNE 4(PC)
MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVQ g(BX), R10

View File

@ -170,8 +170,11 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
JNE 2(PC)
JNE 5(PC)
MOVL sig+0(FP), BX
MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVL g(CX), DI

View File

@ -157,8 +157,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
JNE 2(PC)
JNE 4(PC)
MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVQ g(BX), R10

View File

@ -296,7 +296,8 @@ TEXT runtime·sigaltstack(SB),7,$0
TEXT runtime·sigtramp(SB),7,$24
// this might be called in external code context,
// where g and m are not set.
// first save R0, becuase cgo_load_gm will clobber it
// first save R0, because cgo_load_gm will clobber it
// TODO(adonovan): call runtime·badsignal if m=0, like other platforms?
MOVW R0, 4(R13)
MOVW cgo_load_gm(SB), R0
CMP $0, R0

View File

@ -178,8 +178,11 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
JNE 2(PC)
JNE 5(PC)
MOVL signo+0(FP), BX
MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVL g(CX), DI

View File

@ -196,8 +196,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
JNE 2(PC)
JNE 4(PC)
MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVQ g(BX), R10

View File

@ -151,8 +151,11 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
JNE 2(PC)
JNE 5(PC)
MOVL signo+0(FP), BX
MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVL g(CX), DI

View File

@ -187,8 +187,10 @@ TEXT runtime·sigtramp(SB),7,$64
// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
JNE 2(PC)
JNE 4(PC)
MOVQ DI, 0(SP)
CALL runtime·badsignal(SB)
RET
// save g
MOVQ g(BX), R10

View File

@ -502,7 +502,11 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(void)
runtime·badsignal(int32 sig)
{
runtime·write(2, badsignal, sizeof badsignal - 1);
if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
runtime·exit(1);
}

View File

@ -211,7 +211,11 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(void)
runtime·badsignal(int32 sig)
{
runtime·write(2, badsignal, sizeof badsignal - 1);
if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
runtime·exit(1);
}

View File

@ -261,7 +261,11 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(void)
runtime·badsignal(int32 sig)
{
runtime·write(2, badsignal, sizeof badsignal - 1);
if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
runtime·exit(1);
}

View File

@ -261,7 +261,11 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(void)
runtime·badsignal(int32 sig)
{
runtime·write(2, badsignal, sizeof badsignal - 1);
if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
runtime·exit(1);
}

View File

@ -234,7 +234,11 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(void)
runtime·badsignal(int32 sig)
{
runtime·write(2, badsignal, sizeof badsignal - 1);
if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
runtime.exit(1)
}

View File

@ -354,7 +354,7 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(void)
runtime·badsignal(int32 sig)
{
runtime·pwrite(2, badsignal, sizeof badsignal - 1, -1LL);
}