Commit 75d3f62b authored by Russ Cox's avatar Russ Cox

[dev.garbage] cmd/gc, runtime: add locks around print statements

Now each C printf, Go print, or Go println is guaranteed
not to be interleaved with other calls of those functions.
This should help when debugging concurrent failures.

LGTM=rlh
R=rlh
CC=golang-codereviews
https://golang.org/cl/169120043
parent 91658f93
...@@ -24,6 +24,8 @@ char *runtimeimport = ...@@ -24,6 +24,8 @@ char *runtimeimport =
"func @\"\".printslice (? any)\n" "func @\"\".printslice (? any)\n"
"func @\"\".printnl ()\n" "func @\"\".printnl ()\n"
"func @\"\".printsp ()\n" "func @\"\".printsp ()\n"
"func @\"\".printlock ()\n"
"func @\"\".printunlock ()\n"
"func @\"\".concatstring2 (? string, ? string) (? string)\n" "func @\"\".concatstring2 (? string, ? string) (? string)\n"
"func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n" "func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n"
"func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n" "func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n"
......
...@@ -1464,6 +1464,7 @@ void walk(Node *fn); ...@@ -1464,6 +1464,7 @@ void walk(Node *fn);
void walkexpr(Node **np, NodeList **init); void walkexpr(Node **np, NodeList **init);
void walkexprlist(NodeList *l, NodeList **init); void walkexprlist(NodeList *l, NodeList **init);
void walkexprlistsafe(NodeList *l, NodeList **init); void walkexprlistsafe(NodeList *l, NodeList **init);
void walkexprlistcheap(NodeList *l, NodeList **init);
void walkstmt(Node **np); void walkstmt(Node **np);
void walkstmtlist(NodeList *l); void walkstmtlist(NodeList *l);
Node* conv(Node*, Type*); Node* conv(Node*, Type*);
......
...@@ -36,6 +36,8 @@ func printeface(any) ...@@ -36,6 +36,8 @@ func printeface(any)
func printslice(any) func printslice(any)
func printnl() func printnl()
func printsp() func printsp()
func printlock()
func printunlock()
func concatstring2(string, string) string func concatstring2(string, string) string
func concatstring3(string, string, string) string func concatstring3(string, string, string) string
......
...@@ -363,6 +363,15 @@ walkexprlistsafe(NodeList *l, NodeList **init) ...@@ -363,6 +363,15 @@ walkexprlistsafe(NodeList *l, NodeList **init)
} }
} }
void
walkexprlistcheap(NodeList *l, NodeList **init)
{
for(; l; l=l->next) {
l->n = cheapexpr(l->n, init);
walkexpr(&l->n, init);
}
}
void void
walkexpr(Node **np, NodeList **init) walkexpr(Node **np, NodeList **init)
{ {
...@@ -1773,6 +1782,11 @@ walkprint(Node *nn, NodeList **init) ...@@ -1773,6 +1782,11 @@ walkprint(Node *nn, NodeList **init)
calls = nil; calls = nil;
notfirst = 0; notfirst = 0;
// Hoist all the argument evaluation up before the lock.
walkexprlistcheap(all, init);
calls = list(calls, mkcall("printlock", T, init));
for(l=all; l; l=l->next) { for(l=all; l; l=l->next) {
if(notfirst) { if(notfirst) {
calls = list(calls, mkcall("printsp", T, init)); calls = list(calls, mkcall("printsp", T, init));
...@@ -1853,6 +1867,9 @@ walkprint(Node *nn, NodeList **init) ...@@ -1853,6 +1867,9 @@ walkprint(Node *nn, NodeList **init)
if(op == OPRINTN) if(op == OPRINTN)
calls = list(calls, mkcall("printnl", T, nil)); calls = list(calls, mkcall("printnl", T, nil));
calls = list(calls, mkcall("printunlock", T, init));
typechecklist(calls, Etop); typechecklist(calls, Etop);
walkexprlist(calls, init); walkexprlist(calls, init);
......
...@@ -41,7 +41,31 @@ func snprintf(dst *byte, n int32, s *byte) { ...@@ -41,7 +41,31 @@ func snprintf(dst *byte, n int32, s *byte) {
gp.writebuf = nil gp.writebuf = nil
} }
//var debuglock mutex var debuglock mutex
// The compiler emits calls to printlock and printunlock around
// the multiple calls that implement a single Go print or println
// statement. Some of the print helpers (printsp, for example)
// call print recursively. There is also the problem of a crash
// happening during the print routines and needing to acquire
// the print lock to print information about the crash.
// For both these reasons, let a thread acquire the printlock 'recursively'.
func printlock() {
mp := getg().m
mp.printlock++
if mp.printlock == 1 {
lock(&debuglock)
}
}
func printunlock() {
mp := getg().m
mp.printlock--
if mp.printlock == 0 {
unlock(&debuglock)
}
}
// write to goroutine-local buffer if diverting output, // write to goroutine-local buffer if diverting output,
// or else standard error. // or else standard error.
...@@ -80,7 +104,7 @@ func printnl() { ...@@ -80,7 +104,7 @@ func printnl() {
// Very simple printf. Only for debugging prints. // Very simple printf. Only for debugging prints.
// Do not add to this without checking with Rob. // Do not add to this without checking with Rob.
func vprintf(str string, arg unsafe.Pointer) { func vprintf(str string, arg unsafe.Pointer) {
//lock(&debuglock); printlock()
s := bytes(str) s := bytes(str)
start := 0 start := 0
...@@ -160,7 +184,7 @@ func vprintf(str string, arg unsafe.Pointer) { ...@@ -160,7 +184,7 @@ func vprintf(str string, arg unsafe.Pointer) {
gwrite(s[start:i]) gwrite(s[start:i])
} }
//unlock(&debuglock); printunlock()
} }
func printpc(p unsafe.Pointer) { func printpc(p unsafe.Pointer) {
......
...@@ -345,6 +345,7 @@ struct M ...@@ -345,6 +345,7 @@ struct M
int32 helpgc; int32 helpgc;
bool spinning; // M is out of work and is actively looking for work bool spinning; // M is out of work and is actively looking for work
bool blocked; // M is blocked on a Note bool blocked; // M is blocked on a Note
int8 printlock;
uint32 fastrand; uint32 fastrand;
uint64 ncgocall; // number of cgo calls in total uint64 ncgocall; // number of cgo calls in total
int32 ncgo; // number of cgo calls currently in progress int32 ncgo; // number of cgo calls currently in progress
......
This diff is collapsed.
...@@ -12,6 +12,8 @@ package main ...@@ -12,6 +12,8 @@ package main
// issue 8142: lost 'addrtaken' bit on inlined variables. // issue 8142: lost 'addrtaken' bit on inlined variables.
// no inlining in this test, so just checking that non-inlined works. // no inlining in this test, so just checking that non-inlined works.
func printnl()
type T40 struct { type T40 struct {
m map[int]int m map[int]int
} }
...@@ -24,7 +26,7 @@ func newT40() *T40 { ...@@ -24,7 +26,7 @@ func newT40() *T40 {
func bad40() { func bad40() {
t := newT40() // ERROR "live at call to makemap: ret" t := newT40() // ERROR "live at call to makemap: ret"
println() // ERROR "live at call to printnl: ret" printnl() // ERROR "live at call to printnl: ret"
_ = t _ = t
} }
...@@ -32,6 +34,6 @@ func good40() { ...@@ -32,6 +34,6 @@ func good40() {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: ret" ret.m = make(map[int]int) // ERROR "live at call to makemap: ret"
t := &ret t := &ret
println() // ERROR "live at call to printnl: ret" printnl() // ERROR "live at call to printnl: ret"
_ = t _ = t
} }
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