Commit 3be794cd authored by Rémy Oudompheng's avatar Rémy Oudompheng

cmd/gc: instrument arrays properly in race detector.

The previous implementation would only record access to
the address of the array but the memory access to the whole
memory range must be recorded instead.

R=golang-dev, dvyukov, r
CC=golang-dev
https://golang.org/cl/8053044
parent bc081248
......@@ -113,6 +113,8 @@ char *runtimeimport =
"func @\"\".racefuncexit ()\n"
"func @\"\".raceread (? uintptr)\n"
"func @\"\".racewrite (? uintptr)\n"
"func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n"
"func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n"
"\n"
"$$\n";
char *unsafeimport =
......
......@@ -439,8 +439,8 @@ static int
callinstr(Node **np, NodeList **init, int wr, int skip)
{
Node *f, *b, *n;
Type *t, *t1;
int class, res, hascalls;
Type *t;
int class, hascalls;
n = *np;
//print("callinstr for %+N [ %O ] etype=%E class=%d\n",
......@@ -451,33 +451,6 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
t = n->type;
if(isartificial(n))
return 0;
if(t->etype == TSTRUCT) {
// TODO: instrument arrays similarly.
// PARAMs w/o PHEAP are not interesting.
if(n->class == PPARAM || n->class == PPARAMOUT)
return 0;
res = 0;
hascalls = 0;
foreach(n, hascallspred, &hascalls);
if(hascalls) {
n = detachexpr(n, init);
*np = n;
}
for(t1=t->type; t1; t1=t1->down) {
if(t1->sym && strcmp(t1->sym->name, "_")) {
n = treecopy(n);
f = nod(OXDOT, n, newname(t1->sym));
f->type = t1;
if(f->type->etype == TFIELD)
f->type = f->type->type;
if(callinstr(&f, init, wr, 0)) {
typecheck(&f, Erv);
res = 1;
}
}
}
return res;
}
b = basenod(n);
// it skips e.g. stores to ... parameter array
......@@ -498,7 +471,11 @@ callinstr(Node **np, NodeList **init, int wr, int skip)
}
n = treecopy(n);
makeaddable(n);
f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n));
if(t->etype == TSTRUCT || isfixedarray(t)) {
f = mkcall(wr ? "racewriterange" : "racereadrange", T, init, uintptraddr(n),
nodintconst(t->width));
} else
f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n));
*init = list(*init, f);
return 1;
}
......
......@@ -149,3 +149,5 @@ func racefuncenter(uintptr)
func racefuncexit()
func raceread(uintptr)
func racewrite(uintptr)
func racereadrange(addr, size uintptr)
func racewriterange(addr, size uintptr)
......@@ -77,6 +77,17 @@ runtime·racewrite(uintptr addr)
}
}
#pragma textflag 7
void
runtime·racewriterange(uintptr addr, uintptr sz)
{
if(!onstack(addr)) {
m->racecall = true;
runtimerace·WriteRange(g->racectx, (void*)addr, sz, runtime·getcallerpc(&addr));
m->racecall = false;
}
}
// Called from instrumented code.
// If we split stack, getcallerpc() can return runtime·lessstack().
#pragma textflag 7
......@@ -90,6 +101,17 @@ runtime·raceread(uintptr addr)
}
}
#pragma textflag 7
void
runtime·racereadrange(uintptr addr, uintptr sz)
{
if(!onstack(addr)) {
m->racecall = true;
runtimerace·ReadRange(g->racectx, (void*)addr, sz, runtime·getcallerpc(&addr));
m->racecall = false;
}
}
// Called from runtime·racefuncenter (assembly).
#pragma textflag 7
void
......
......@@ -600,8 +600,7 @@ func TestRaceSprint(t *testing.T) {
<-ch
}
// Not implemented.
func TestRaceFailingArrayCopy(t *testing.T) {
func TestRaceArrayCopy(t *testing.T) {
ch := make(chan bool, 1)
var a [5]int
go func() {
......@@ -612,6 +611,24 @@ func TestRaceFailingArrayCopy(t *testing.T) {
<-ch
}
// Blows up a naive compiler.
func TestRaceNestedArrayCopy(t *testing.T) {
ch := make(chan bool, 1)
type (
Point32 [2][2][2][2][2]Point
Point1024 [2][2][2][2][2]Point32
Point32k [2][2][2][2][2]Point1024
Point1M [2][2][2][2][2]Point32k
)
var a, b Point1M
go func() {
a[0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1].y = 1
ch <- true
}()
a = b
<-ch
}
func TestRaceStructRW(t *testing.T) {
p := Point{0, 0}
ch := make(chan bool, 1)
......
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