github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/racewalk.go (about) 1 // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/gc/racewalk.go 2 3 // Copyright 2012 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 package gc 8 9 import ( 10 "fmt" 11 "strings" 12 ) 13 14 // The racewalk pass modifies the code tree for the function as follows: 15 // 16 // 1. It inserts a call to racefuncenter at the beginning of each function. 17 // 2. It inserts a call to racefuncexit at the end of each function. 18 // 3. It inserts a call to raceread before each memory read. 19 // 4. It inserts a call to racewrite before each memory write. 20 // 21 // The rewriting is not yet complete. Certain nodes are not rewritten 22 // but should be. 23 24 // TODO(dvyukov): do not instrument initialization as writes: 25 // a := make([]int, 10) 26 27 // Do not instrument the following packages at all, 28 // at best instrumentation would cause infinite recursion. 29 var omit_pkgs = []string{"runtime", "runtime/race"} 30 31 // Only insert racefuncenter/racefuncexit into the following packages. 32 // Memory accesses in the packages are either uninteresting or will cause false positives. 33 var noinst_pkgs = []string{"sync", "sync/atomic"} 34 35 func ispkgin(pkgs []string) bool { 36 if myimportpath != "" { 37 for i := 0; i < len(pkgs); i++ { 38 if myimportpath == pkgs[i] { 39 return true 40 } 41 } 42 } 43 44 return false 45 } 46 47 func isforkfunc(fn *Node) bool { 48 // Special case for syscall.forkAndExecInChild. 49 // In the child, this function must not acquire any locks, because 50 // they might have been locked at the time of the fork. This means 51 // no rescheduling, no malloc calls, and no new stack segments. 52 // Race instrumentation does all of the above. 53 return myimportpath != "" && myimportpath == "syscall" && fn.Nname.Sym.Name == "forkAndExecInChild" 54 } 55 56 func racewalk(fn *Node) { 57 if ispkgin(omit_pkgs) || isforkfunc(fn) { 58 return 59 } 60 61 if !ispkgin(noinst_pkgs) { 62 racewalklist(fn.Nbody, nil) 63 64 // nothing interesting for race detector in fn->enter 65 racewalklist(fn.Func.Exit, nil) 66 } 67 68 // nodpc is the PC of the caller as extracted by 69 // getcallerpc. We use -widthptr(FP) for x86. 70 // BUG: this will not work on arm. 71 nodpc := Nod(OXXX, nil, nil) 72 73 *nodpc = *nodfp 74 nodpc.Type = Types[TUINTPTR] 75 nodpc.Xoffset = int64(-Widthptr) 76 nd := mkcall("racefuncenter", nil, nil, nodpc) 77 fn.Func.Enter = concat(list1(nd), fn.Func.Enter) 78 nd = mkcall("racefuncexit", nil, nil) 79 fn.Func.Exit = list(fn.Func.Exit, nd) 80 81 if Debug['W'] != 0 { 82 s := fmt.Sprintf("after racewalk %v", fn.Nname.Sym) 83 dumplist(s, fn.Nbody) 84 s = fmt.Sprintf("enter %v", fn.Nname.Sym) 85 dumplist(s, fn.Func.Enter) 86 s = fmt.Sprintf("exit %v", fn.Nname.Sym) 87 dumplist(s, fn.Func.Exit) 88 } 89 } 90 91 func racewalklist(l *NodeList, init **NodeList) { 92 var instr *NodeList 93 94 for ; l != nil; l = l.Next { 95 instr = nil 96 racewalknode(&l.N, &instr, 0, 0) 97 if init == nil { 98 l.N.Ninit = concat(l.N.Ninit, instr) 99 } else { 100 *init = concat(*init, instr) 101 } 102 } 103 } 104 105 // walkexpr and walkstmt combined 106 // walks the tree and adds calls to the 107 // instrumentation code to top-level (statement) nodes' init 108 func racewalknode(np **Node, init **NodeList, wr int, skip int) { 109 n := *np 110 111 if n == nil { 112 return 113 } 114 115 if Debug['w'] > 1 { 116 Dump("racewalk-before", n) 117 } 118 setlineno(n) 119 if init == nil { 120 Fatal("racewalk: bad init list") 121 } 122 if init == &n.Ninit { 123 // If init == &n->ninit and n->ninit is non-nil, 124 // racewalknode might append it to itself. 125 // nil it out and handle it separately before putting it back. 126 l := n.Ninit 127 128 n.Ninit = nil 129 racewalklist(l, nil) 130 racewalknode(&n, &l, wr, skip) // recurse with nil n->ninit 131 appendinit(&n, l) 132 *np = n 133 return 134 } 135 136 racewalklist(n.Ninit, nil) 137 138 switch n.Op { 139 default: 140 Fatal("racewalk: unknown node type %v", Oconv(int(n.Op), 0)) 141 142 case OAS, OASWB, OAS2FUNC: 143 racewalknode(&n.Left, init, 1, 0) 144 racewalknode(&n.Right, init, 0, 0) 145 goto ret 146 147 // can't matter 148 case OCFUNC, OVARKILL: 149 goto ret 150 151 case OBLOCK: 152 if n.List == nil { 153 goto ret 154 } 155 156 switch n.List.N.Op { 157 // Blocks are used for multiple return function calls. 158 // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]} 159 // We don't want to instrument between the statements because it will 160 // smash the results. 161 case OCALLFUNC, OCALLMETH, OCALLINTER: 162 racewalknode(&n.List.N, &n.List.N.Ninit, 0, 0) 163 164 var fini *NodeList 165 racewalklist(n.List.Next, &fini) 166 n.List = concat(n.List, fini) 167 168 // Ordinary block, for loop initialization or inlined bodies. 169 default: 170 racewalklist(n.List, nil) 171 } 172 173 goto ret 174 175 case ODEFER: 176 racewalknode(&n.Left, init, 0, 0) 177 goto ret 178 179 case OPROC: 180 racewalknode(&n.Left, init, 0, 0) 181 goto ret 182 183 case OCALLINTER: 184 racewalknode(&n.Left, init, 0, 0) 185 goto ret 186 187 // Instrument dst argument of runtime.writebarrier* calls 188 // as we do not instrument runtime code. 189 // typedslicecopy is instrumented in runtime. 190 case OCALLFUNC: 191 if n.Left.Sym != nil && n.Left.Sym.Pkg == Runtimepkg && (strings.HasPrefix(n.Left.Sym.Name, "writebarrier") || n.Left.Sym.Name == "typedmemmove") { 192 // Find the dst argument. 193 // The list can be reordered, so it's not necessary just the first or the second element. 194 var l *NodeList 195 for l = n.List; l != nil; l = l.Next { 196 if n.Left.Sym.Name == "typedmemmove" { 197 if l.N.Left.Xoffset == int64(Widthptr) { 198 break 199 } 200 } else { 201 if l.N.Left.Xoffset == 0 { 202 break 203 } 204 } 205 } 206 207 if l == nil { 208 Fatal("racewalk: writebarrier no arg") 209 } 210 if l.N.Right.Op != OADDR { 211 Fatal("racewalk: writebarrier bad arg") 212 } 213 callinstr(&l.N.Right.Left, init, 1, 0) 214 } 215 216 racewalknode(&n.Left, init, 0, 0) 217 goto ret 218 219 case ONOT, 220 OMINUS, 221 OPLUS, 222 OREAL, 223 OIMAG, 224 OCOM, 225 OSQRT: 226 racewalknode(&n.Left, init, wr, 0) 227 goto ret 228 229 case ODOTINTER: 230 racewalknode(&n.Left, init, 0, 0) 231 goto ret 232 233 case ODOT: 234 racewalknode(&n.Left, init, 0, 1) 235 callinstr(&n, init, wr, skip) 236 goto ret 237 238 case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND 239 racewalknode(&n.Left, init, 0, 0) 240 241 callinstr(&n, init, wr, skip) 242 goto ret 243 244 case OIND: // *p 245 racewalknode(&n.Left, init, 0, 0) 246 247 callinstr(&n, init, wr, skip) 248 goto ret 249 250 case OSPTR, OLEN, OCAP: 251 racewalknode(&n.Left, init, 0, 0) 252 if Istype(n.Left.Type, TMAP) { 253 n1 := Nod(OCONVNOP, n.Left, nil) 254 n1.Type = Ptrto(Types[TUINT8]) 255 n1 = Nod(OIND, n1, nil) 256 typecheck(&n1, Erv) 257 callinstr(&n1, init, 0, skip) 258 } 259 260 goto ret 261 262 case OLSH, 263 ORSH, 264 OLROT, 265 OAND, 266 OANDNOT, 267 OOR, 268 OXOR, 269 OSUB, 270 OMUL, 271 OHMUL, 272 OEQ, 273 ONE, 274 OLT, 275 OLE, 276 OGE, 277 OGT, 278 OADD, 279 OCOMPLEX: 280 racewalknode(&n.Left, init, wr, 0) 281 racewalknode(&n.Right, init, wr, 0) 282 goto ret 283 284 case OANDAND, OOROR: 285 racewalknode(&n.Left, init, wr, 0) 286 287 // walk has ensured the node has moved to a location where 288 // side effects are safe. 289 // n->right may not be executed, 290 // so instrumentation goes to n->right->ninit, not init. 291 racewalknode(&n.Right, &n.Right.Ninit, wr, 0) 292 293 goto ret 294 295 case ONAME: 296 callinstr(&n, init, wr, skip) 297 goto ret 298 299 case OCONV: 300 racewalknode(&n.Left, init, wr, 0) 301 goto ret 302 303 case OCONVNOP: 304 racewalknode(&n.Left, init, wr, 0) 305 goto ret 306 307 case ODIV, OMOD: 308 racewalknode(&n.Left, init, wr, 0) 309 racewalknode(&n.Right, init, wr, 0) 310 goto ret 311 312 case OINDEX: 313 if !Isfixedarray(n.Left.Type) { 314 racewalknode(&n.Left, init, 0, 0) 315 } else if !islvalue(n.Left) { 316 // index of unaddressable array, like Map[k][i]. 317 racewalknode(&n.Left, init, wr, 0) 318 319 racewalknode(&n.Right, init, 0, 0) 320 goto ret 321 } 322 323 racewalknode(&n.Right, init, 0, 0) 324 if n.Left.Type.Etype != TSTRING { 325 callinstr(&n, init, wr, skip) 326 } 327 goto ret 328 329 // Seems to only lead to double instrumentation. 330 //racewalknode(&n->left, init, 0, 0); 331 case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR: 332 goto ret 333 334 case OADDR: 335 racewalknode(&n.Left, init, 0, 1) 336 goto ret 337 338 // n->left is Type* which is not interesting. 339 case OEFACE: 340 racewalknode(&n.Right, init, 0, 0) 341 342 goto ret 343 344 case OITAB: 345 racewalknode(&n.Left, init, 0, 0) 346 goto ret 347 348 // should not appear in AST by now 349 case OSEND, 350 ORECV, 351 OCLOSE, 352 ONEW, 353 OXCASE, 354 OXFALL, 355 OCASE, 356 OPANIC, 357 ORECOVER, 358 OCONVIFACE, 359 OCMPIFACE, 360 OMAKECHAN, 361 OMAKEMAP, 362 OMAKESLICE, 363 OCALL, 364 OCOPY, 365 OAPPEND, 366 ORUNESTR, 367 OARRAYBYTESTR, 368 OARRAYRUNESTR, 369 OSTRARRAYBYTE, 370 OSTRARRAYRUNE, 371 OINDEXMAP, 372 // lowered to call 373 OCMPSTR, 374 OADDSTR, 375 ODOTTYPE, 376 ODOTTYPE2, 377 OAS2DOTTYPE, 378 OCALLPART, 379 // lowered to PTRLIT 380 OCLOSURE, // lowered to PTRLIT 381 ORANGE, // lowered to ordinary for loop 382 OARRAYLIT, // lowered to assignments 383 OMAPLIT, 384 OSTRUCTLIT, 385 OAS2, 386 OAS2RECV, 387 OAS2MAPR, 388 OASOP: 389 Yyerror("racewalk: %v must be lowered by now", Oconv(int(n.Op), 0)) 390 391 goto ret 392 393 // impossible nodes: only appear in backend. 394 case ORROTC, OEXTEND: 395 Yyerror("racewalk: %v cannot exist now", Oconv(int(n.Op), 0)) 396 goto ret 397 398 case OGETG: 399 Yyerror("racewalk: OGETG can happen only in runtime which we don't instrument") 400 goto ret 401 402 // just do generic traversal 403 case OFOR, 404 OIF, 405 OCALLMETH, 406 ORETURN, 407 ORETJMP, 408 OSWITCH, 409 OSELECT, 410 OEMPTY, 411 OBREAK, 412 OCONTINUE, 413 OFALL, 414 OGOTO, 415 OLABEL: 416 goto ret 417 418 // does not require instrumentation 419 case OPRINT, // don't bother instrumenting it 420 OPRINTN, // don't bother instrumenting it 421 OCHECKNIL, // always followed by a read. 422 OPARAM, // it appears only in fn->exit to copy heap params back 423 OCLOSUREVAR, // immutable pointer to captured variable 424 ODOTMETH, // either part of CALLMETH or CALLPART (lowered to PTRLIT) 425 OINDREG, // at this stage, only n(SP) nodes from nodarg 426 ODCL, // declarations (without value) cannot be races 427 ODCLCONST, 428 ODCLTYPE, 429 OTYPE, 430 ONONAME, 431 OLITERAL, 432 OSLICESTR, // always preceded by bounds checking, avoid double instrumentation. 433 OTYPESW: // ignored by code generation, do not instrument. 434 goto ret 435 } 436 437 ret: 438 if n.Op != OBLOCK { // OBLOCK is handled above in a special way. 439 racewalklist(n.List, init) 440 } 441 if n.Ntest != nil { 442 racewalknode(&n.Ntest, &n.Ntest.Ninit, 0, 0) 443 } 444 if n.Nincr != nil { 445 racewalknode(&n.Nincr, &n.Nincr.Ninit, 0, 0) 446 } 447 racewalklist(n.Nbody, nil) 448 racewalklist(n.Nelse, nil) 449 racewalklist(n.Rlist, nil) 450 *np = n 451 } 452 453 func isartificial(n *Node) bool { 454 // compiler-emitted artificial things that we do not want to instrument, 455 // cant' possibly participate in a data race. 456 if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" { 457 if n.Sym.Name == "_" { 458 return true 459 } 460 461 // autotmp's are always local 462 if strings.HasPrefix(n.Sym.Name, "autotmp_") { 463 return true 464 } 465 466 // statictmp's are read-only 467 if strings.HasPrefix(n.Sym.Name, "statictmp_") { 468 return true 469 } 470 471 // go.itab is accessed only by the compiler and runtime (assume safe) 472 if n.Sym.Pkg != nil && n.Sym.Pkg.Name != "" && n.Sym.Pkg.Name == "go.itab" { 473 return true 474 } 475 } 476 477 return false 478 } 479 480 func callinstr(np **Node, init **NodeList, wr int, skip int) bool { 481 n := *np 482 483 //print("callinstr for %+N [ %O ] etype=%E class=%d\n", 484 // n, n->op, n->type ? n->type->etype : -1, n->class); 485 486 if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL { 487 return false 488 } 489 t := n.Type 490 if isartificial(n) { 491 return false 492 } 493 494 b := outervalue(n) 495 496 // it skips e.g. stores to ... parameter array 497 if isartificial(b) { 498 return false 499 } 500 class := b.Class 501 502 // BUG: we _may_ want to instrument PAUTO sometimes 503 // e.g. if we've got a local variable/method receiver 504 // that has got a pointer inside. Whether it points to 505 // the heap or not is impossible to know at compile time 506 if (class&PHEAP != 0) || class == PPARAMREF || class == PEXTERN || b.Op == OINDEX || b.Op == ODOTPTR || b.Op == OIND { 507 hascalls := 0 508 foreach(n, hascallspred, &hascalls) 509 if hascalls != 0 { 510 n = detachexpr(n, init) 511 *np = n 512 } 513 514 n = treecopy(n) 515 makeaddable(n) 516 var f *Node 517 if t.Etype == TSTRUCT || Isfixedarray(t) { 518 name := "racereadrange" 519 if wr != 0 { 520 name = "racewriterange" 521 } 522 f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(t.Width)) 523 } else { 524 name := "raceread" 525 if wr != 0 { 526 name = "racewrite" 527 } 528 f = mkcall(name, nil, init, uintptraddr(n)) 529 } 530 531 *init = list(*init, f) 532 return true 533 } 534 535 return false 536 } 537 538 // makeaddable returns a node whose memory location is the 539 // same as n, but which is addressable in the Go language 540 // sense. 541 // This is different from functions like cheapexpr that may make 542 // a copy of their argument. 543 func makeaddable(n *Node) { 544 // The arguments to uintptraddr technically have an address but 545 // may not be addressable in the Go sense: for example, in the case 546 // of T(v).Field where T is a struct type and v is 547 // an addressable value. 548 switch n.Op { 549 case OINDEX: 550 if Isfixedarray(n.Left.Type) { 551 makeaddable(n.Left) 552 } 553 554 // Turn T(v).Field into v.Field 555 case ODOT, OXDOT: 556 if n.Left.Op == OCONVNOP { 557 n.Left = n.Left.Left 558 } 559 makeaddable(n.Left) 560 561 // nothing to do 562 case ODOTPTR: 563 fallthrough 564 default: 565 break 566 } 567 } 568 569 func uintptraddr(n *Node) *Node { 570 r := Nod(OADDR, n, nil) 571 r.Bounded = true 572 r = conv(r, Types[TUNSAFEPTR]) 573 r = conv(r, Types[TUINTPTR]) 574 return r 575 } 576 577 func detachexpr(n *Node, init **NodeList) *Node { 578 addr := Nod(OADDR, n, nil) 579 l := temp(Ptrto(n.Type)) 580 as := Nod(OAS, l, addr) 581 typecheck(&as, Etop) 582 walkexpr(&as, init) 583 *init = list(*init, as) 584 ind := Nod(OIND, l, nil) 585 typecheck(&ind, Erv) 586 walkexpr(&ind, init) 587 return ind 588 } 589 590 func foreachnode(n *Node, f func(*Node, interface{}), c interface{}) { 591 if n != nil { 592 f(n, c) 593 } 594 } 595 596 func foreachlist(l *NodeList, f func(*Node, interface{}), c interface{}) { 597 for ; l != nil; l = l.Next { 598 foreachnode(l.N, f, c) 599 } 600 } 601 602 func foreach(n *Node, f func(*Node, interface{}), c interface{}) { 603 foreachlist(n.Ninit, f, c) 604 foreachnode(n.Left, f, c) 605 foreachnode(n.Right, f, c) 606 foreachlist(n.List, f, c) 607 foreachnode(n.Ntest, f, c) 608 foreachnode(n.Nincr, f, c) 609 foreachlist(n.Nbody, f, c) 610 foreachlist(n.Nelse, f, c) 611 foreachlist(n.Rlist, f, c) 612 } 613 614 func hascallspred(n *Node, c interface{}) { 615 switch n.Op { 616 case OCALL, OCALLFUNC, OCALLMETH, OCALLINTER: 617 (*c.(*int))++ 618 } 619 } 620 621 // appendinit is like addinit in subr.go 622 // but appends rather than prepends. 623 func appendinit(np **Node, init *NodeList) { 624 if init == nil { 625 return 626 } 627 628 n := *np 629 switch n.Op { 630 // There may be multiple refs to this node; 631 // introduce OCONVNOP to hold init list. 632 case ONAME, OLITERAL: 633 n = Nod(OCONVNOP, n, nil) 634 635 n.Type = n.Left.Type 636 n.Typecheck = 1 637 *np = n 638 } 639 640 n.Ninit = concat(n.Ninit, init) 641 n.Ullman = UINF 642 }