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
This commit is contained in:
Rémy Oudompheng 2013-06-14 11:14:45 +02:00
parent bc08124866
commit 3be794cdc2
5 changed files with 52 additions and 32 deletions

View File

@ -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 =

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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)