]> Cypherpunks repositories - gostls13.git/commitdiff
race: gc changes
authorDmitriy Vyukov <dvyukov@google.com>
Tue, 2 Oct 2012 06:05:46 +0000 (10:05 +0400)
committerDmitriy Vyukov <dvyukov@google.com>
Tue, 2 Oct 2012 06:05:46 +0000 (10:05 +0400)
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

src/cmd/gc/builtin.c
src/cmd/gc/doc.go
src/cmd/gc/go.h
src/cmd/gc/lex.c
src/cmd/gc/pgen.c
src/cmd/gc/racewalk.c [new file with mode: 0644]
src/cmd/gc/reflect.c
src/cmd/gc/runtime.go

index 6354fa22c8ca0937ba3708ee7f06bfa69cabed1f..1313b3d162218f4b1b03ca8e62de1cf2d7f23cde 100644 (file)
@@ -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 =
index 8d8f8967b7147ecfd5ab97a9d8243a6fcfad25f4..fad1427339f0ae262888e6487b7ef505766827d2 100644 (file)
@@ -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.
index 326ede3063a00603846139c68fb2a7926fe13627..31fae5b19bd556e55b93635279d02a32709a194a 100644 (file)
@@ -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);
index 3ef3fcc746b2bb6f831baae4a94b571a085ffd22..6bf00161b716bad88e6d6b609044e0e056e0b74e 100644 (file)
@@ -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;
        }
index f2b75d61b665c37216682d7bc6cbd26d0eb798d3..46b763bf0900814ecd86b55a610f10174716bbd1 100644 (file)
@@ -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;
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
new file mode 100644 (file)
index 0000000..6f8597a
--- /dev/null
@@ -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;
+}
index 52f77b86f7cd3f16e72d0e1a0f0e6e3b7627d029..ad1d8d86061af5690707c6fc0ed99974641e469a 100644 (file)
@@ -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")));
        }
 }
index 28c6b44bc6026353446d36b0f4b2e138e3f5142e..cfabbb174b9189d0fad244663c718a24bd9d93a1 100644 (file)
@@ -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)