Commit 041fc8bf authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

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
parent cf513387
......@@ -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;
......
// 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)
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