mirror of
https://github.com/golang/go.git
synced 2025-10-20 19:43:20 +00:00
race: gc changes
This is the first part of a bigger change that adds data race detection feature: https://golang.org/cl/6456044 This change makes gc compiler instrument memory accesses when supplied with -b flag. R=rsc, nigeltao, lvd CC=golang-dev https://golang.org/cl/6497074
This commit is contained in:
parent
cf513387c3
commit
041fc8bf96
8 changed files with 467 additions and 4 deletions
|
@ -104,6 +104,10 @@ char *runtimeimport =
|
|||
"func @\"\".int64tofloat64(? int64) (? float64)\n"
|
||||
"func @\"\".uint64tofloat64(? uint64) (? float64)\n"
|
||||
"func @\"\".complex128div(@\"\".num complex128, @\"\".den complex128) (@\"\".quo complex128)\n"
|
||||
"func @\"\".racefuncenter()\n"
|
||||
"func @\"\".racefuncexit()\n"
|
||||
"func @\"\".raceread(? uintptr)\n"
|
||||
"func @\"\".racewrite(? uintptr)\n"
|
||||
"\n"
|
||||
"$$\n";
|
||||
char *unsafeimport =
|
||||
|
|
|
@ -54,6 +54,8 @@ Flags:
|
|||
disallow importing packages not marked as safe
|
||||
-V
|
||||
print the compiler version
|
||||
-b
|
||||
compile with race detection enabled
|
||||
|
||||
There are also a number of debugging flags; run the command with no arguments
|
||||
to get a usage message.
|
||||
|
|
|
@ -838,6 +838,7 @@ EXTERN Pkg* builtinpkg; // fake package for builtins
|
|||
EXTERN Pkg* gostringpkg; // fake pkg for Go strings
|
||||
EXTERN Pkg* itabpkg; // fake pkg for itab cache
|
||||
EXTERN Pkg* runtimepkg; // package runtime
|
||||
EXTERN Pkg* racepkg; // package runtime/race
|
||||
EXTERN Pkg* stringpkg; // fake package for C strings
|
||||
EXTERN Pkg* typepkg; // fake package for runtime type info
|
||||
EXTERN Pkg* weaktypepkg; // weak references to runtime type info
|
||||
|
@ -1442,3 +1443,8 @@ void zname(Biobuf *b, Sym *s, int t);
|
|||
#pragma varargck type "V" Val*
|
||||
#pragma varargck type "Y" char*
|
||||
#pragma varargck type "Z" Strlit*
|
||||
|
||||
/*
|
||||
* racewalk.c
|
||||
*/
|
||||
void racewalk(Node *fn);
|
||||
|
|
|
@ -264,7 +264,12 @@ main(int argc, char *argv[])
|
|||
print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p);
|
||||
exits(0);
|
||||
} ARGEND
|
||||
|
||||
|
||||
if(debug['b']) {
|
||||
racepkg = mkpkg(strlit("runtime/race"));
|
||||
racepkg->name = "race";
|
||||
}
|
||||
|
||||
// enable inlining. for now:
|
||||
// default: inlining on. (debug['l'] == 1)
|
||||
// -l: inlining off (debug['l'] == 0)
|
||||
|
@ -530,7 +535,7 @@ static int
|
|||
findpkg(Strlit *name)
|
||||
{
|
||||
Idir *p;
|
||||
char *q;
|
||||
char *q, *race;
|
||||
|
||||
if(islocalname(name)) {
|
||||
if(safemode)
|
||||
|
@ -568,10 +573,13 @@ findpkg(Strlit *name)
|
|||
return 1;
|
||||
}
|
||||
if(goroot != nil) {
|
||||
snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.a", goroot, goos, goarch, name);
|
||||
race = "";
|
||||
if(debug['b'])
|
||||
race = "_race";
|
||||
snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.a", goroot, goos, goarch, race, name);
|
||||
if(access(namebuf, 0) >= 0)
|
||||
return 1;
|
||||
snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.%c", goroot, goos, goarch, name, thechar);
|
||||
snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.%c", goroot, goos, goarch, race, name, thechar);
|
||||
if(access(namebuf, 0) >= 0)
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,10 @@ compile(Node *fn)
|
|||
walk(curfn);
|
||||
if(nerrors != 0)
|
||||
goto ret;
|
||||
if(debug['b'])
|
||||
racewalk(curfn);
|
||||
if(nerrors != 0)
|
||||
goto ret;
|
||||
|
||||
continpc = P;
|
||||
breakpc = P;
|
||||
|
|
431
src/cmd/gc/racewalk.c
Normal file
431
src/cmd/gc/racewalk.c
Normal file
|
@ -0,0 +1,431 @@
|
|||
// Copyright 2012 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 racewalk pass modifies the code tree for the function as follows:
|
||||
//
|
||||
// 1. It inserts a call to racefuncenter at the beginning of each function.
|
||||
// 2. It inserts a call to racefuncexit at the end of each function.
|
||||
// 3. It inserts a call to raceread before each memory read.
|
||||
// 4. It inserts a call to racewrite before each memory write.
|
||||
//
|
||||
// The rewriting is not yet complete. Certain nodes are not rewritten
|
||||
// but should be.
|
||||
|
||||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include "go.h"
|
||||
#include "opnames.h"
|
||||
|
||||
//TODO: do not instrument initialization as writes:
|
||||
// a := make([]int, 10)
|
||||
|
||||
static void racewalklist(NodeList *l, NodeList **init);
|
||||
static void racewalknode(Node **np, NodeList **init, int wr, int skip);
|
||||
static void callinstr(Node *n, NodeList **init, int wr, int skip);
|
||||
static Node* uintptraddr(Node *n);
|
||||
static Node* basenod(Node *n);
|
||||
|
||||
static const char *omitPkgs[] = {"runtime", "runtime/race", "sync", "sync/atomic"};
|
||||
|
||||
void
|
||||
racewalk(Node *fn)
|
||||
{
|
||||
int i;
|
||||
Node *nd;
|
||||
char s[1024];
|
||||
|
||||
if(myimportpath) {
|
||||
for(i=0; i<nelem(omitPkgs); i++) {
|
||||
if(strcmp(myimportpath, omitPkgs[i]) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nd = mkcall("racefuncenter", T, nil);
|
||||
fn->enter = list(fn->enter, nd);
|
||||
nd = mkcall("racefuncexit", T, nil);
|
||||
fn->exit = list(fn->exit, nd); // works fine if (!fn->exit)
|
||||
racewalklist(curfn->nbody, nil);
|
||||
|
||||
if(debug['W']) {
|
||||
snprint(s, sizeof(s), "after racewalk %S", curfn->nname->sym);
|
||||
dumplist(s, curfn->nbody);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
racewalklist(NodeList *l, NodeList **init)
|
||||
{
|
||||
NodeList *instr;
|
||||
|
||||
for(; l; l = l->next) {
|
||||
instr = nil;
|
||||
racewalknode(&l->n, &instr, 0, 0);
|
||||
if(init == nil)
|
||||
l->n->ninit = concat(l->n->ninit, instr);
|
||||
else
|
||||
*init = concat(*init, instr);
|
||||
}
|
||||
}
|
||||
|
||||
// walkexpr and walkstmt combined
|
||||
// walks the tree and adds calls to the
|
||||
// instrumentation code to top-level (statement) nodes' init
|
||||
static void
|
||||
racewalknode(Node **np, NodeList **init, int wr, int skip)
|
||||
{
|
||||
Node *n, *n1;
|
||||
|
||||
n = *np;
|
||||
|
||||
if(n == N)
|
||||
return;
|
||||
if(0)
|
||||
print("op=%s, left=[ %N ], right=[ %N ], right's type=%T, n's type=%T, n's class=%d\n",
|
||||
opnames[n->op], n->left, n->right, n->right ? n->right->type : nil, n->type, n->class);
|
||||
setlineno(n);
|
||||
|
||||
switch(n->op) {
|
||||
default:
|
||||
fatal("racewalk: unknown node type %O", n->op);
|
||||
|
||||
case OASOP:
|
||||
case OAS:
|
||||
case OAS2:
|
||||
case OAS2DOTTYPE:
|
||||
case OAS2RECV:
|
||||
case OAS2FUNC:
|
||||
case OAS2MAPR:
|
||||
racewalklist(n->ninit, init);
|
||||
racewalknode(&n->left, init, 1, 0);
|
||||
racewalknode(&n->right, init, 0, 0);
|
||||
goto ret;
|
||||
|
||||
case OBLOCK:
|
||||
// leads to crashes.
|
||||
//racewalklist(n->list, nil);
|
||||
goto ret;
|
||||
|
||||
case ODEFER:
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
goto ret;
|
||||
|
||||
case OFOR:
|
||||
racewalklist(n->ninit, nil);
|
||||
if(n->ntest != N)
|
||||
racewalklist(n->ntest->ninit, nil);
|
||||
racewalknode(&n->nincr, init, wr, 0);
|
||||
racewalklist(n->nbody, nil);
|
||||
goto ret;
|
||||
|
||||
case OIF:
|
||||
racewalklist(n->ninit, nil);
|
||||
racewalknode(&n->ntest, &n->ninit, wr, 0);
|
||||
racewalklist(n->nbody, nil);
|
||||
racewalklist(n->nelse, nil);
|
||||
goto ret;
|
||||
|
||||
case OPROC:
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
goto ret;
|
||||
|
||||
case OCALLINTER:
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
racewalklist(n->list, init);
|
||||
goto ret;
|
||||
|
||||
case OCALLFUNC:
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
racewalklist(n->ninit, init);
|
||||
racewalklist(n->list, init);
|
||||
goto ret;
|
||||
|
||||
case OCALLMETH:
|
||||
racewalklist(n->list, init);
|
||||
goto ret;
|
||||
|
||||
case ORETURN:
|
||||
racewalklist(n->list, nil);
|
||||
goto ret;
|
||||
|
||||
case OSELECT:
|
||||
// n->nlist is nil by now because this code
|
||||
// is running after walkselect
|
||||
racewalklist(n->nbody, nil);
|
||||
goto ret;
|
||||
|
||||
case OSWITCH:
|
||||
racewalklist(n->ninit, nil);
|
||||
if(n->ntest->op == OTYPESW)
|
||||
// don't bother, we have static typization
|
||||
return;
|
||||
racewalknode(&n->ntest, &n->ninit, 0, 0);
|
||||
racewalklist(n->nbody, nil);
|
||||
goto ret;
|
||||
|
||||
case OEMPTY:
|
||||
racewalklist(n->ninit, nil);
|
||||
goto ret;
|
||||
|
||||
case ONOT:
|
||||
case OMINUS:
|
||||
case OPLUS:
|
||||
case OREAL:
|
||||
case OIMAG:
|
||||
racewalknode(&n->left, init, wr, 0);
|
||||
goto ret;
|
||||
|
||||
case ODOTINTER:
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
goto ret;
|
||||
|
||||
case ODOT:
|
||||
callinstr(n, init, wr, skip);
|
||||
racewalknode(&n->left, init, 0, 1);
|
||||
goto ret;
|
||||
|
||||
case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND
|
||||
callinstr(n, init, wr, skip);
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
goto ret;
|
||||
|
||||
case OIND: // *p
|
||||
callinstr(n, init, wr, skip);
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
goto ret;
|
||||
|
||||
case OLEN:
|
||||
case OCAP:
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
if(istype(n->left->type, TMAP)) {
|
||||
// crashes on len(m[0]) or len(f())
|
||||
USED(&n1);
|
||||
/*
|
||||
n1 = nod(OADDR, n->left, N);
|
||||
n1 = conv(n1, types[TUNSAFEPTR]);
|
||||
n1 = conv(n1, ptrto(ptrto(types[TINT8])));
|
||||
n1 = nod(OIND, n1, N);
|
||||
n1 = nod(OIND, n1, N);
|
||||
typecheck(&n1, Erv);
|
||||
callinstr(n1, init, 0, skip);
|
||||
*/
|
||||
}
|
||||
goto ret;
|
||||
|
||||
case OLSH:
|
||||
case ORSH:
|
||||
case OAND:
|
||||
case OANDNOT:
|
||||
case OOR:
|
||||
case OXOR:
|
||||
case OSUB:
|
||||
case OMUL:
|
||||
case OEQ:
|
||||
case ONE:
|
||||
case OLT:
|
||||
case OLE:
|
||||
case OGE:
|
||||
case OGT:
|
||||
case OADD:
|
||||
case OCOMPLEX:
|
||||
racewalknode(&n->left, init, wr, 0);
|
||||
racewalknode(&n->right, init, wr, 0);
|
||||
goto ret;
|
||||
|
||||
case OANDAND:
|
||||
case OOROR:
|
||||
racewalknode(&n->left, init, wr, 0);
|
||||
// It requires more complex tree transformation,
|
||||
// because we don't know whether it will be executed or not.
|
||||
//racewalknode(&n->right, init, wr, 0);
|
||||
goto ret;
|
||||
|
||||
case ONAME:
|
||||
callinstr(n, init, wr, skip);
|
||||
goto ret;
|
||||
|
||||
case OCONV:
|
||||
racewalknode(&n->left, init, wr, 0);
|
||||
goto ret;
|
||||
|
||||
case OCONVNOP:
|
||||
racewalknode(&n->left, init, wr, 0);
|
||||
goto ret;
|
||||
|
||||
case ODIV:
|
||||
case OMOD:
|
||||
// TODO(dvyukov): add a test for this
|
||||
racewalknode(&n->left, init, wr, 0);
|
||||
racewalknode(&n->right, init, wr, 0);
|
||||
goto ret;
|
||||
|
||||
case OINDEX:
|
||||
if(n->left->type->etype != TSTRING)
|
||||
callinstr(n, init, wr, skip);
|
||||
if(!isfixedarray(n->left->type))
|
||||
racewalknode(&n->left, init, 0, 0);
|
||||
racewalknode(&n->right, init, 0, 0);
|
||||
goto ret;
|
||||
|
||||
case OSLICE:
|
||||
case OSLICEARR:
|
||||
// Seems to only lead to double instrumentation.
|
||||
//racewalklist(n->ninit, init);
|
||||
//racewalknode(&n->left, init, 0, 0);
|
||||
//racewalklist(n->list, init);
|
||||
goto ret;
|
||||
|
||||
case OADDR:
|
||||
racewalknode(&n->left, init, 0, 1);
|
||||
goto ret;
|
||||
|
||||
// should not appear in AST by now
|
||||
case OSEND:
|
||||
case ORECV:
|
||||
case OCLOSE:
|
||||
case ONEW:
|
||||
case OXCASE:
|
||||
case OXFALL:
|
||||
case OCASE:
|
||||
case OPANIC:
|
||||
case ORECOVER:
|
||||
yyerror("racewalk: %O must be lowered by now", n->op);
|
||||
goto ret;
|
||||
|
||||
// does not require instrumentation
|
||||
case OINDEXMAP: // implemented in runtime
|
||||
case OPRINT: // don't bother instrumenting it
|
||||
case OPRINTN: // don't bother instrumenting it
|
||||
goto ret;
|
||||
|
||||
// unimplemented
|
||||
case OCMPSTR:
|
||||
case OADDSTR:
|
||||
case OSLICESTR:
|
||||
case OAPPEND:
|
||||
case OCOPY:
|
||||
case OMAKECHAN:
|
||||
case OMAKEMAP:
|
||||
case OMAKESLICE:
|
||||
case ORUNESTR:
|
||||
case OARRAYBYTESTR:
|
||||
case OARRAYRUNESTR:
|
||||
case OSTRARRAYBYTE:
|
||||
case OSTRARRAYRUNE:
|
||||
case OCMPIFACE:
|
||||
case OARRAYLIT:
|
||||
case OMAPLIT:
|
||||
case OSTRUCTLIT:
|
||||
case OCLOSURE:
|
||||
case ODOTTYPE:
|
||||
case ODOTTYPE2:
|
||||
case OCONVIFACE:
|
||||
case OCALL:
|
||||
case OBREAK:
|
||||
case ODCL:
|
||||
case OCONTINUE:
|
||||
case OFALL:
|
||||
case OGOTO:
|
||||
case OLABEL:
|
||||
case ODCLCONST:
|
||||
case ODCLTYPE:
|
||||
case OLITERAL:
|
||||
case ORANGE:
|
||||
case OTYPE:
|
||||
case ONONAME:
|
||||
case OINDREG:
|
||||
case OCOM:
|
||||
case ODOTMETH:
|
||||
case OEFACE:
|
||||
case OITAB:
|
||||
case OEXTEND:
|
||||
case OHMUL:
|
||||
case OLROT:
|
||||
case ORROTC:
|
||||
goto ret;
|
||||
}
|
||||
|
||||
ret:
|
||||
*np = n;
|
||||
}
|
||||
|
||||
static void
|
||||
callinstr(Node *n, NodeList **init, int wr, int skip)
|
||||
{
|
||||
Node *f, *b;
|
||||
Type *t, *t1;
|
||||
int class;
|
||||
|
||||
//print("callinstr for %N [ %s ] etype=%d class=%d\n",
|
||||
// n, opnames[n->op], n->type ? n->type->etype : -1, n->class);
|
||||
|
||||
if(skip || n->type == T || n->type->etype >= TIDEAL)
|
||||
return;
|
||||
t = n->type;
|
||||
if(n->op == ONAME) {
|
||||
if(n->sym != S) {
|
||||
if(n->sym->name != nil) {
|
||||
if(strncmp(n->sym->name, "_", sizeof("_")-1) == 0)
|
||||
return;
|
||||
if(strncmp(n->sym->name, "autotmp_", sizeof("autotmp_")-1) == 0)
|
||||
return;
|
||||
if(strncmp(n->sym->name, "statictmp_", sizeof("statictmp_")-1) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t->etype == TSTRUCT) {
|
||||
for(t1=t->type; t1; t1=t1->down) {
|
||||
if(t1->sym && strncmp(t1->sym->name, "_", sizeof("_")-1)) {
|
||||
n = treecopy(n);
|
||||
f = nod(OXDOT, n, newname(t1->sym));
|
||||
typecheck(&f, Erv);
|
||||
callinstr(f, init, wr, 0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
b = basenod(n);
|
||||
class = b->class;
|
||||
// BUG: we _may_ want to instrument PAUTO sometimes
|
||||
// e.g. if we've got a local variable/method receiver
|
||||
// that has got a pointer inside. Whether it points to
|
||||
// the heap or not is impossible to know at compile time
|
||||
if((class&PHEAP) || class == PPARAMREF || class == PEXTERN
|
||||
|| b->type->etype == TARRAY || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {
|
||||
n = treecopy(n);
|
||||
f = mkcall(wr ? "racewrite" : "raceread", T, nil, uintptraddr(n));
|
||||
//typecheck(&f, Etop);
|
||||
*init = list(*init, f);
|
||||
}
|
||||
}
|
||||
|
||||
static Node*
|
||||
uintptraddr(Node *n)
|
||||
{
|
||||
Node *r;
|
||||
|
||||
r = nod(OADDR, n, N);
|
||||
r = conv(r, types[TUNSAFEPTR]);
|
||||
r = conv(r, types[TUINTPTR]);
|
||||
return r;
|
||||
}
|
||||
|
||||
static Node*
|
||||
basenod(Node *n)
|
||||
{
|
||||
for(;;) {
|
||||
if(n->op == ODOT || n->op == OPAREN) {
|
||||
n = n->left;
|
||||
continue;
|
||||
}
|
||||
if(n->op == OINDEX) {
|
||||
n = n->left;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
}
|
|
@ -934,6 +934,8 @@ dumptypestructs(void)
|
|||
|
||||
// add paths for runtime and main, which 6l imports implicitly.
|
||||
dimportpath(runtimepkg);
|
||||
if(debug['b'])
|
||||
dimportpath(racepkg);
|
||||
dimportpath(mkpkg(strlit("main")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,3 +138,9 @@ func int64tofloat64(int64) float64
|
|||
func uint64tofloat64(uint64) float64
|
||||
|
||||
func complex128div(num complex128, den complex128) (quo complex128)
|
||||
|
||||
// race detection
|
||||
func racefuncenter()
|
||||
func racefuncexit()
|
||||
func raceread(uintptr)
|
||||
func racewrite(uintptr)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue