// 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. package gc import ( "cmd/compile/internal/types" "cmd/internal/src" "fmt" "strings" ) // The instrument pass modifies the code tree for instrumentation. // // For flag_race it modifies the function as follows: // // 1. It inserts a call to racefuncenterfp 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. // // For flag_msan: // // 1. It inserts a call to msanread before each memory read. // 2. It inserts a call to msanwrite before each memory write. // // The rewriting is not yet complete. Certain nodes are not rewritten // but should be. // TODO(dvyukov): do not instrument initialization as writes: // a := make([]int, 10) // Do not instrument the following packages at all, // at best instrumentation would cause infinite recursion. var omit_pkgs = []string{"runtime/internal/atomic", "runtime/internal/sys", "runtime", "runtime/race", "runtime/msan"} // Only insert racefuncenterfp/racefuncexit into the following packages. // Memory accesses in the packages are either uninteresting or will cause false positives. var norace_inst_pkgs = []string{"sync", "sync/atomic"} func ispkgin(pkgs []string) bool { if myimportpath != "" { for _, p := range pkgs { if myimportpath == p { return true } } } return false } func instrument(fn *Node) { if ispkgin(omit_pkgs) || fn.Func.Pragma&Norace != 0 { return } if !flag_race || !ispkgin(norace_inst_pkgs) { instrumentlist(fn.Nbody, nil) // nothing interesting for race detector in fn->enter instrumentlist(fn.Func.Exit, nil) } if flag_race { // nodpc is the PC of the caller as extracted by // getcallerpc. We use -widthptr(FP) for x86. // BUG: this will not work on arm. nodpc := *nodfp nodpc.Type = types.Types[TUINTPTR] nodpc.Xoffset = int64(-Widthptr) savedLineno := lineno lineno = src.NoXPos nd := mkcall("racefuncenter", nil, nil, &nodpc) fn.Func.Enter.Prepend(nd) nd = mkcall("racefuncexit", nil, nil) fn.Func.Exit.Append(nd) fn.Func.Dcl = append(fn.Func.Dcl, &nodpc) lineno = savedLineno } if Debug['W'] != 0 { s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym) dumplist(s, fn.Nbody) s = fmt.Sprintf("enter %v", fn.Func.Nname.Sym) dumplist(s, fn.Func.Enter) s = fmt.Sprintf("exit %v", fn.Func.Nname.Sym) dumplist(s, fn.Func.Exit) } } func instrumentlist(l Nodes, init *Nodes) { s := l.Slice() for i := range s { var instr Nodes instrumentnode(&s[i], &instr, 0, 0) if init == nil { s[i].Ninit.AppendNodes(&instr) } else { init.AppendNodes(&instr) } } } // walkexpr and walkstmt combined // walks the tree and adds calls to the // instrumentation code to top-level (statement) nodes' init func instrumentnode(np **Node, init *Nodes, wr int, skip int) { n := *np if n == nil { return } if Debug['w'] > 1 { Dump("instrument-before", n) } setlineno(n) if init == nil { Fatalf("instrument: bad init list") } if init == &n.Ninit { // If init == &n->ninit and n->ninit is non-nil, // instrumentnode might append it to itself. // nil it out and handle it separately before putting it back. l := n.Ninit n.Ninit.Set(nil) instrumentlist(l, nil) instrumentnode(&n, &l, wr, skip) // recurse with nil n->ninit appendinit(&n, l) *np = n return } instrumentlist(n.Ninit, nil) switch n.Op { default: Fatalf("instrument: unknown node type %v", n.Op) case OAS, OAS2FUNC: instrumentnode(&n.Left, init, 1, 0) instrumentnode(&n.Right, init, 0, 0) // can't matter case OCFUNC, OVARKILL, OVARLIVE: case OBLOCK: ls := n.List.Slice() afterCall := false for i := range ls { op := ls[i].Op // Scan past OAS nodes copying results off stack. // Those must not be instrumented, because the // instrumentation calls will smash the results. // The assignments are to temporaries, so they cannot // be involved in races and need not be instrumented. if afterCall && op == OAS && iscallret(ls[i].Right) { continue } instrumentnode(&ls[i], &ls[i].Ninit, 0, 0) afterCall = (op == OCALLFUNC || op == OCALLMETH || op == OCALLINTER) } case ODEFER: instrumentnode(&n.Left, init, 0, 0) case OPROC: instrumentnode(&n.Left, init, 0, 0) case OCALLINTER: instrumentnode(&n.Left, init, 0, 0) case OCALLFUNC: // Note that runtime.typedslicecopy is the only // assignment-like function call in the AST at this // point (between walk and SSA); since we don't // instrument it here, typedslicecopy is manually // instrumented in runtime. Calls to the write barrier // and typedmemmove are created later by SSA, so those // still appear as OAS nodes at this point. instrumentnode(&n.Left, init, 0, 0) case ONOT, OMINUS, OPLUS, OREAL, OIMAG, OCOM: instrumentnode(&n.Left, init, wr, 0) case ODOTINTER: instrumentnode(&n.Left, init, 0, 0) case ODOT: instrumentnode(&n.Left, init, 0, 1) callinstr(&n, init, wr, skip) case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND instrumentnode(&n.Left, init, 0, 0) callinstr(&n, init, wr, skip) case OIND: // *p instrumentnode(&n.Left, init, 0, 0) callinstr(&n, init, wr, skip) case OSPTR, OLEN, OCAP: instrumentnode(&n.Left, init, 0, 0) if n.Left.Type.IsMap() { n1 := nod(OCONVNOP, n.Left, nil) n1.Type = types.NewPtr(types.Types[TUINT8]) n1 = nod(OIND, n1, nil) n1 = typecheck(n1, Erv) callinstr(&n1, init, 0, skip) } case OLSH, ORSH, OAND, OANDNOT, OOR, OXOR, OSUB, OMUL, OEQ, ONE, OLT, OLE, OGE, OGT, OADD, OCOMPLEX: instrumentnode(&n.Left, init, wr, 0) instrumentnode(&n.Right, init, wr, 0) case OANDAND, OOROR: instrumentnode(&n.Left, init, wr, 0) // walk has ensured the node has moved to a location where // side effects are safe. // n->right may not be executed, // so instrumentation goes to n->right->ninit, not init. instrumentnode(&n.Right, &n.Right.Ninit, wr, 0) case ONAME: callinstr(&n, init, wr, skip) case OCONV: instrumentnode(&n.Left, init, wr, 0) case OCONVNOP: instrumentnode(&n.Left, init, wr, 0) case ODIV, OMOD: instrumentnode(&n.Left, init, wr, 0) instrumentnode(&n.Right, init, wr, 0) case OINDEX: if !n.Left.Type.IsArray() { instrumentnode(&n.Left, init, 0, 0) } else if !islvalue(n.Left) { // index of unaddressable array, like Map[k][i]. instrumentnode(&n.Left, init, wr, 0) instrumentnode(&n.Right, init, 0, 0) break } instrumentnode(&n.Right, init, 0, 0) if !n.Left.Type.IsString() { callinstr(&n, init, wr, skip) } case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR: instrumentnode(&n.Left, init, 0, 0) low, high, max := n.SliceBounds() instrumentnode(&low, init, 0, 0) instrumentnode(&high, init, 0, 0) instrumentnode(&max, init, 0, 0) n.SetSliceBounds(low, high, max) case OADDR: instrumentnode(&n.Left, init, 0, 1) // n->left is Type* which is not interesting. case OEFACE: instrumentnode(&n.Right, init, 0, 0) case OITAB, OIDATA: instrumentnode(&n.Left, init, 0, 0) case OSTRARRAYBYTETMP: instrumentnode(&n.Left, init, 0, 0) case OAS2DOTTYPE: instrumentnode(&n.Left, init, 1, 0) instrumentnode(&n.Right, init, 0, 0) case ODOTTYPE, ODOTTYPE2: instrumentnode(&n.Left, init, 0, 0) // should not appear in AST by now case OSEND, ORECV, OCLOSE, ONEW, OXCASE, OCASE, OPANIC, ORECOVER, OCONVIFACE, OCMPIFACE, OMAKECHAN, OMAKEMAP, OMAKESLICE, OCALL, OCOPY, OAPPEND, ORUNESTR, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, OINDEXMAP, // lowered to call OCMPSTR, OADDSTR, OCALLPART, // lowered to PTRLIT OCLOSURE, // lowered to PTRLIT ORANGE, // lowered to ordinary for loop OARRAYLIT, // lowered to assignments OSLICELIT, OMAPLIT, OSTRUCTLIT, OAS2, OAS2RECV, OAS2MAPR, OASOP: Fatalf("instrument: %v must be lowered by now", n.Op) case OGETG: Fatalf("instrument: OGETG can happen only in runtime which we don't instrument") case OFOR, OFORUNTIL: if n.Left != nil { instrumentnode(&n.Left, &n.Left.Ninit, 0, 0) } if n.Right != nil { instrumentnode(&n.Right, &n.Right.Ninit, 0, 0) } case OIF, OSWITCH: if n.Left != nil { instrumentnode(&n.Left, &n.Left.Ninit, 0, 0) } // just do generic traversal case OCALLMETH, ORETURN, ORETJMP, OSELECT, OEMPTY, OBREAK, OCONTINUE, OFALL, OGOTO, OLABEL: // does not require instrumentation case OPRINT, // don't bother instrumenting it OPRINTN, // don't bother instrumenting it OCHECKNIL, // always followed by a read. OCLOSUREVAR, // immutable pointer to captured variable ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT) OINDREGSP, // at this stage, only n(SP) nodes from nodarg ODCL, // declarations (without value) cannot be races ODCLCONST, ODCLTYPE, OTYPE, ONONAME, OLITERAL, OTYPESW: // ignored by code generation, do not instrument. } if n.Op != OBLOCK { // OBLOCK is handled above in a special way. instrumentlist(n.List, init) } instrumentlist(n.Nbody, nil) instrumentlist(n.Rlist, nil) *np = n } func isartificial(n *Node) bool { // compiler-emitted artificial things that we do not want to instrument, // can't possibly participate in a data race. // can't be seen by C/C++ and therefore irrelevant for msan. if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" { if n.Sym.Name == "_" { return true } // autotmp's are always local if n.IsAutoTmp() { return true } // statictmp's are read-only if strings.HasPrefix(n.Sym.Name, "statictmp_") { return true } // go.itab is accessed only by the compiler and runtime (assume safe) if n.Sym.Pkg != nil && n.Sym.Pkg.Name != "" && n.Sym.Pkg.Name == "go.itab" { return true } } return false } func callinstr(np **Node, init *Nodes, wr int, skip int) bool { n := *np //fmt.Printf("callinstr for %v [ %v ] etype=%v class=%v\n", // n, n.Op, n.Type.Etype, n.Class) if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL { return false } t := n.Type // dowidth may not have been called for PEXTERN. dowidth(t) w := t.Width if w == BADWIDTH { Fatalf("instrument: %v badwidth", t) } if w == 0 { return false // can't race on zero-sized things } if isartificial(n) { return false } b := outervalue(n) // it skips e.g. stores to ... parameter array if isartificial(b) { return false } 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 == PAUTOHEAP || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND { hasCalls := false inspect(n, func(n *Node) bool { switch n.Op { case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER: hasCalls = true } return !hasCalls }) if hasCalls { n = detachexpr(n, init) *np = n } n = treecopy(n, src.NoXPos) makeaddable(n) var f *Node if flag_msan { name := "msanread" if wr != 0 { name = "msanwrite" } f = mkcall(name, nil, init, uintptraddr(n), nodintconst(w)) } else if flag_race && t.NumComponents() > 1 { // for composite objects we have to write every address // because a write might happen to any subobject. // composites with only one element don't have subobjects, though. name := "racereadrange" if wr != 0 { name = "racewriterange" } f = mkcall(name, nil, init, uintptraddr(n), nodintconst(w)) } else if flag_race { // for non-composite objects we can write just the start // address, as any write must write the first byte. name := "raceread" if wr != 0 { name = "racewrite" } f = mkcall(name, nil, init, uintptraddr(n)) } init.Append(f) return true } return false } // makeaddable returns a node whose memory location is the // same as n, but which is addressable in the Go language // sense. // This is different from functions like cheapexpr that may make // a copy of their argument. func makeaddable(n *Node) { // The arguments to uintptraddr technically have an address but // may not be addressable in the Go sense: for example, in the case // of T(v).Field where T is a struct type and v is // an addressable value. switch n.Op { case OINDEX: if n.Left.Type.IsArray() { makeaddable(n.Left) } // Turn T(v).Field into v.Field case ODOT, OXDOT: if n.Left.Op == OCONVNOP { n.Left = n.Left.Left } makeaddable(n.Left) // nothing to do } } func uintptraddr(n *Node) *Node { r := nod(OADDR, n, nil) r.SetBounded(true) r = conv(r, types.Types[TUNSAFEPTR]) r = conv(r, types.Types[TUINTPTR]) return r } func detachexpr(n *Node, init *Nodes) *Node { addr := nod(OADDR, n, nil) l := temp(types.NewPtr(n.Type)) as := nod(OAS, l, addr) as = typecheck(as, Etop) as = walkexpr(as, init) init.Append(as) ind := nod(OIND, l, nil) ind = typecheck(ind, Erv) ind = walkexpr(ind, init) return ind } // appendinit is like addinit in subr.go // but appends rather than prepends. func appendinit(np **Node, init Nodes) { if init.Len() == 0 { return } n := *np switch n.Op { // There may be multiple refs to this node; // introduce OCONVNOP to hold init list. case ONAME, OLITERAL: n = nod(OCONVNOP, n, nil) n.Type = n.Left.Type n.SetTypecheck(1) *np = n } n.Ninit.AppendNodes(&init) n.SetHasCall(true) }