github.com/lzhfromustc/gofuzz@v0.0.0-20211116160056-151b3108bbd1/runtime/myoracle.go (about) 1 package runtime 2 3 import "sync/atomic" 4 5 func init() { 6 MapChToChanInfo = make(map[interface{}]PrimInfo) 7 } 8 9 var GlobalEnableOracle = true 10 11 // during benchmark, we don't need to print bugs to stdout 12 var BoolReportBug = gogetenv("GF_BENCHMARK") != "1" 13 var BoolDelayCheck = true 14 15 var MuReportBug mutex 16 17 type PrimInfo interface { 18 Lock() 19 Unlock() 20 MapRef() map[*GoInfo]struct{} 21 AddGoroutine(*GoInfo) 22 RemoveGoroutine(*GoInfo) 23 StringDebug() string 24 SetMonitor(uint32) 25 LoadMonitor() uint32 26 } 27 28 //// Part 1.1: data struct for each channel 29 30 // ChanInfo is 1-to-1 with every channel. It tracks a list of goroutines that hold the reference to the channel 31 type ChanInfo struct { 32 Chan *hchan // Stores the channel. Can be used as ID of channel 33 IntBuffer int // The buffer capability of channel. 0 if channel is unbuffered 34 MapRefGoroutine map[*GoInfo]struct{} // Stores all goroutines that still hold reference to this channel 35 StrDebug string 36 OKToCheck bool // Disable oracle for not instrumented channels 37 BoolInSDK bool // Disable oracle for channels in SDK 38 IntFlagFoundBug int32 // Use atomic int32 operations to mark if a bug is reported 39 Mu mutex 40 SpecialFlag int8 41 Uint32Monitor uint32 // Default: 0. When a bug is based on the assumption that this primitive won't execute again, set to 1. 42 // When it is 1 and still executed, print information to withdraw bugs containing it 43 } 44 45 const ( 46 TimeTicker int8 = 1 47 ) 48 49 type OpType uint8 50 51 const ( 52 ChSend OpType = 1 53 ChRecv OpType = 2 54 ChClose OpType = 3 55 ChSelect OpType = 4 56 ) 57 58 var MapChToChanInfo map[interface{}]PrimInfo 59 var MuMapChToChanInfo mutex 60 61 //var DefaultCaseChanInfo = &ChanInfo{} 62 63 var strSDKPath string = gogetenv("GOROOT") 64 65 func PrintSDK() { 66 println(strSDKPath) 67 } 68 69 // Initialize a new ChanInfo with a given channel 70 func NewChanInfo(ch *hchan) *ChanInfo { 71 _, strFile, intLine, _ := Caller(2) 72 strLoc := strFile + ":" + Itoa(intLine) 73 newChInfo := &ChanInfo{ 74 Chan: ch, 75 IntBuffer: int(ch.dataqsiz), 76 MapRefGoroutine: make(map[*GoInfo]struct{}), 77 StrDebug: strLoc, 78 OKToCheck: false, 79 BoolInSDK: Index(strLoc, strSDKPath) < 0, 80 IntFlagFoundBug: 0, 81 SpecialFlag: 0, 82 } 83 if BoolPrintDebugInfo { 84 println("===Debug Info:") 85 println("\tMake of a new channel. The creation site is:", strLoc) 86 println("\tSDK path is:", strSDKPath, "\tBoolMakeNotInSDK is:", newChInfo.BoolInSDK) 87 } 88 AddRefGoroutine(newChInfo, CurrentGoInfo()) 89 90 // If this channel is in some special API, set special flag 91 //creationFunc := MyCaller(1) 92 if Index(strLoc, "time") >= 0 { 93 newChInfo.SpecialFlag = TimeTicker 94 } 95 96 return newChInfo 97 } 98 99 func (chInfo *ChanInfo) StringDebug() string { 100 if chInfo == nil { 101 return "" 102 } 103 return chInfo.StrDebug 104 } 105 106 func (chInfo *ChanInfo) SetMonitor(i uint32) { 107 atomic.StoreUint32(&chInfo.Uint32Monitor, i) 108 } 109 110 func (chInfo *ChanInfo) LoadMonitor() uint32 { 111 return atomic.LoadUint32(&chInfo.Uint32Monitor) 112 } 113 114 func okToCheck(c *hchan) bool { 115 if c.chInfo != nil { 116 switch c.chInfo.SpecialFlag { 117 case TimeTicker: 118 return false 119 } 120 } 121 return true 122 } 123 124 func (chInfo *ChanInfo) Lock() { 125 if chInfo == nil { 126 return 127 } 128 lock(&chInfo.Mu) 129 } 130 131 func (chInfo *ChanInfo) Unlock() { 132 if chInfo == nil { 133 return 134 } 135 unlock(&chInfo.Mu) 136 } 137 138 // Must be called with chInfo.Mu locked 139 func (chInfo *ChanInfo) MapRef() map[*GoInfo]struct{} { 140 if chInfo == nil { 141 return make(map[*GoInfo]struct{}) 142 } 143 return chInfo.MapRefGoroutine 144 } 145 146 // FindChanInfo can retrieve a initialized ChanInfo for a given channel 147 func FindChanInfo(ch interface{}) *ChanInfo { 148 lock(&MuMapChToChanInfo) 149 chInfo := MapChToChanInfo[ch] 150 unlock(&MuMapChToChanInfo) 151 if chInfo == nil { 152 return nil 153 } else { 154 return chInfo.(*ChanInfo) 155 } 156 } 157 158 func LinkChToLastChanInfo(ch interface{}) { 159 lock(&MuMapChToChanInfo) 160 primInfo := LoadLastPrimInfo() 161 MapChToChanInfo[ch] = primInfo 162 if chInfo, ok := primInfo.(*ChanInfo); ok { 163 chInfo.OKToCheck = true 164 } 165 unlock(&MuMapChToChanInfo) 166 } 167 168 // After the creation of a new channel, or at the head of a goroutine that holds a reference to a channel, 169 // or whenever a goroutine obtains a reference to a channel, call this function 170 // AddRefGoroutine links a channel with a goroutine, meaning the goroutine holds the reference to the channel 171 func AddRefGoroutine(chInfo PrimInfo, goInfo *GoInfo) { 172 if chInfo == nil || goInfo == nil { 173 return 174 } 175 chInfo.AddGoroutine(goInfo) 176 goInfo.AddPrime(chInfo) 177 } 178 179 func RemoveRefGoroutine(chInfo PrimInfo, goInfo *GoInfo) { 180 if chInfo == nil || goInfo == nil { 181 return 182 } 183 chInfo.RemoveGoroutine(goInfo) 184 goInfo.RemovePrime(chInfo) 185 } 186 187 // When this goroutine is checking bug, set goInfo.BitCheckBugAtEnd to be 1 188 func SetCurrentGoCheckBug() { 189 getg().goInfo.SetCheckBug() 190 } 191 192 func (goInfo *GoInfo) SetCheckBug() { 193 atomic.StoreUint32(&goInfo.BitCheckBugAtEnd, 1) 194 } 195 196 func (goInfo *GoInfo) SetNotCheckBug() { 197 atomic.StoreUint32(&goInfo.BitCheckBugAtEnd, 0) 198 } 199 200 // This means the goroutine mapped with goInfo holds the reference to chInfo.Chan 201 func (chInfo *ChanInfo) AddGoroutine(goInfo *GoInfo) { 202 if chInfo == nil { 203 return 204 } 205 chInfo.Lock() 206 if chInfo.MapRefGoroutine == nil { 207 chInfo.Unlock() 208 return 209 } 210 chInfo.MapRefGoroutine[goInfo] = struct{}{} 211 chInfo.Unlock() 212 } 213 214 func (chInfo *ChanInfo) RemoveGoroutine(goInfo *GoInfo) { 215 if chInfo == nil { 216 return 217 } 218 chInfo.Lock() 219 if chInfo.MapRefGoroutine == nil { 220 chInfo.Unlock() 221 return 222 } 223 delete(chInfo.MapRefGoroutine, goInfo) 224 chInfo.Unlock() 225 } 226 227 // Only when BoolDelayCheck is true, this struct is used 228 // CheckEntry contains information needed for a CheckBlockBug 229 type CheckEntry struct { 230 CS []PrimInfo 231 Uint32NeedCheck uint32 // if 0, delete this CheckEntry; if 1, check this CheckEntry 232 } 233 234 var VecCheckEntry []*CheckEntry 235 var MuCheckEntry mutex 236 var FnCheckCount = func(*uint32) {} // this is defined in gooracle/gooracle.go 237 var PtrCheckCounter *uint32 238 239 func LockCheckEntry() { 240 lock(&MuCheckEntry) 241 } 242 243 func UnlockCheckEntry() { 244 unlock(&MuCheckEntry) 245 } 246 247 func DequeueCheckEntry() *CheckEntry { 248 lock(&MuCheckEntry) 249 if len(VecCheckEntry) == 0 { 250 unlock(&MuCheckEntry) 251 return nil 252 } else { 253 result := VecCheckEntry[0] 254 VecCheckEntry = VecCheckEntry[1:] 255 unlock(&MuCheckEntry) 256 return result 257 } 258 } 259 260 func EnqueueCheckEntry(CS []PrimInfo) *CheckEntry { 261 lock(&MuCheckEntry) 262 263 if len(CS) == 1 { 264 if CS[0].StringDebug() == "/data/ziheng/shared/gotest/stubs/etcd/pkg/mod/github.com/prometheus/client_golang@v1.0.0/prometheus/registry.go:266" { 265 print() 266 } 267 } 268 269 FnCheckCount(PtrCheckCounter) 270 newCheckEntry := &CheckEntry{ 271 CS: CS, 272 Uint32NeedCheck: 1, 273 } 274 for _, entry := range VecCheckEntry { 275 if BoolCheckEntryEqual(entry, newCheckEntry) { 276 unlock(&MuCheckEntry) 277 return nil // It's OK to return nil 278 } 279 } 280 if BoolDebug { 281 if len(CS) == 1 { 282 if CS[0].StringDebug() == "/data/ziheng/shared/gotest/stubs/etcd/pkg/mod/github.com/prometheus/client_golang@v1.0.0/prometheus/registry.go:266" { 283 print() 284 } 285 } 286 print("Enqueueing:") 287 for _, c := range CS { 288 print("\t", c.StringDebug()) 289 } 290 println() 291 } 292 293 VecCheckEntry = append(VecCheckEntry, newCheckEntry) 294 unlock(&MuCheckEntry) 295 return newCheckEntry 296 } 297 298 func BoolCheckEntryEqual(a, b *CheckEntry) bool { 299 if len(a.CS) != len(b.CS) { 300 return false 301 } 302 for _, primInfo1 := range a.CS { 303 boolFound := false 304 for _, primInfo2 := range b.CS { 305 if primInfo2 == primInfo1 { 306 boolFound = true 307 break 308 } 309 } 310 if boolFound == false { 311 return false 312 } 313 } 314 return true 315 } 316 317 // A blocking bug is detected, if all goroutines that hold the reference to a channel are blocked at an operation of the channel 318 // finished is true when we are sure that CS doesn't need to be checked again 319 func CheckBlockBug(CS []PrimInfo) (finished bool) { 320 mapCS := make(map[PrimInfo]struct{}) 321 mapGS := make(map[*GoInfo]struct{}) // all goroutines that hold reference to primitives in mapCS 322 finished = false 323 324 if BoolDebug { 325 print("Checking primtives:") 326 for _, chI := range CS { 327 print("\t", chI.StringDebug()) 328 } 329 println() 330 } 331 332 for _, primI := range CS { 333 if primI == (*ChanInfo)(nil) { 334 continue 335 } 336 if chI, ok := primI.(*ChanInfo); ok { 337 if chI.OKToCheck == false { 338 return true 339 } 340 } 341 if Index(primI.StringDebug(), strSDKPath) >= 0 { 342 if BoolDebug { 343 println("Abort checking because this prim is in SDK:", primI.StringDebug()) 344 } 345 finished = true 346 return 347 } 348 primI.Lock() 349 for goInfo, _ := range primI.MapRef() { 350 mapGS[goInfo] = struct{}{} 351 } 352 primI.Unlock() 353 mapCS[primI] = struct{}{} 354 } 355 356 boolAtLeastOneBlocking := false 357 loopGS: 358 if BoolDebug { 359 println("Going through mapGS whose length is:", len(mapGS)) 360 } 361 for goInfo, _ := range mapGS { 362 if atomic.LoadUint32(&goInfo.BitCheckBugAtEnd) == 1 { // The goroutine is checking bug at the end of unit test 363 if BoolDebug { 364 println("\tGoID", goInfo.G.goid, "is checking bug at the end of unit test") 365 } 366 continue 367 } 368 lock(&goInfo.Mu) 369 if len(goInfo.VecBlockInfo) == 0 { // The goroutine is executing non-blocking operations 370 if BoolDebug { 371 println("\tGoID", goInfo.G.goid, "is executing non-blocking operations") 372 println("Aborting checking") 373 } 374 unlock(&goInfo.Mu) 375 return 376 } 377 378 boolAtLeastOneBlocking = true 379 380 if BoolDebug { 381 println("\tGoID", goInfo.G.goid, "is executing blocking operations") 382 } 383 384 for _, blockInfo := range goInfo.VecBlockInfo { // if it is blocked at select, VecBlockInfo contains multiple primitives 385 386 primI := blockInfo.Prim 387 if _, exist := mapCS[primI]; !exist { 388 if BoolDebug { 389 println("\t\tNot existing prim in CS:", blockInfo.Prim.StringDebug()) 390 } 391 mapCS[primI] = struct{}{} // update CS 392 primI.Lock() 393 for goInfo, _ := range primI.MapRef() { // update GS 394 mapGS[goInfo] = struct{}{} 395 } 396 primI.Unlock() 397 unlock(&goInfo.Mu) 398 if BoolDebug { 399 println("Goto mapGS loop again") 400 } 401 goto loopGS // since mapGS is updated, we should run this loop once again 402 } else { 403 if BoolDebug { 404 println("\t\tExisting prim in CS:", blockInfo.Prim.StringDebug()) 405 } 406 } 407 } 408 unlock(&goInfo.Mu) 409 } 410 411 if boolAtLeastOneBlocking { 412 ReportBug(mapCS) 413 finished = true 414 } 415 416 return 417 } 418 419 //func (chInfo *ChanInfo) CheckBlockBug() { 420 // if atomic.LoadInt32(&chInfo.intFlagFoundBug) != 0 { 421 // return 422 // } 423 // 424 // if chInfo.intBuffer == 0 { 425 // countRefGo := 0 // Number of goroutines that hold the reference to the channel 426 // countBlockAtThisChanGo := 0 // Number of goroutines that are blocked at an operation of this channel 427 // f := func(key interface{}, value interface{}) bool { 428 // goInfo, _ := key.(*GoInfo) 429 // 430 // boolIsBlock, _ := goInfo.IsBlockAtGivenChan(chInfo) 431 // if boolIsBlock { 432 // countBlockAtThisChanGo++ 433 // } 434 // countRefGo++ 435 // return true // continue Range 436 // } 437 // chInfo.mapRefGoroutine.Range(f) 438 // 439 // if countRefGo == countBlockAtThisChanGo { 440 // if countRefGo == 0 { 441 // // debug code 442 // countRefGo2 := 0 // Number of goroutines that hold the reference to the channel 443 // countBlockAtThisChanGo2 := 0 // Number of goroutines that are blocked at an operation of this channel 444 // f := func(key interface{}, value interface{}) bool { 445 // goInfo, _ := key.(*GoInfo) 446 // 447 // boolIsBlock, _ := goInfo.IsBlockAtGivenChan(chInfo) 448 // if boolIsBlock { 449 // countBlockAtThisChanGo2++ 450 // } 451 // countRefGo2++ 452 // return true // continue Range 453 // } 454 // chInfo.mapRefGoroutine.Range(f) 455 // fmt.Print() 456 // 457 // return 458 // } 459 // ReportBug(chInfo) 460 // atomic.AddInt32(&chInfo.intFlagFoundBug, 1) 461 // } 462 // 463 // } else { // Buffered channel 464 // if reflect.ValueOf(chInfo.Chan).Len() == chInfo.intBuffer { // Buffer is full 465 // // Check if all ref goroutines are blocked at send 466 // boolAllBlockAtSend := true 467 // countRefGo := 0 468 // countBlockAtThisChanGo := 0 469 // f := func(key interface{}, value interface{}) bool { 470 // goInfo, _ := key.(*GoInfo) 471 // 472 // boolIsBlock, strOp := goInfo.IsBlockAtGivenChan(chInfo) 473 // if boolIsBlock { 474 // countBlockAtThisChanGo++ 475 // } 476 // if strOp != Send { 477 // boolAllBlockAtSend = false 478 // } 479 // countRefGo++ 480 // return true // continue Range 481 // } 482 // chInfo.mapRefGoroutine.Range(f) 483 // 484 // if countRefGo == countBlockAtThisChanGo && boolAllBlockAtSend { 485 // ReportBug(chInfo) 486 // atomic.AddInt32(&chInfo.intFlagFoundBug, 1) 487 // } 488 // 489 // } else if reflect.ValueOf(chInfo.Chan).Len() == 0 { // Buffer is empty 490 // // Check if all ref goroutines are blocked at receive 491 // boolAllBlockAtRecv := true 492 // countRefGo := 0 493 // countBlockAtThisChanGo := 0 494 // f := func(key interface{}, value interface{}) bool { 495 // goInfo, _ := key.(*GoInfo) 496 // 497 // boolIsBlock, strOp := goInfo.IsBlockAtGivenChan(chInfo) 498 // if boolIsBlock { 499 // countBlockAtThisChanGo++ 500 // } 501 // if strOp != Recv { 502 // boolAllBlockAtRecv = false 503 // } 504 // countRefGo++ 505 // return true // continue Range 506 // } 507 // chInfo.mapRefGoroutine.Range(f) 508 // 509 // if countRefGo == countBlockAtThisChanGo && boolAllBlockAtRecv { 510 // ReportBug(chInfo) 511 // atomic.AddInt32(&chInfo.intFlagFoundBug, 1) 512 // } 513 // 514 // } else { // Buffer is not full or empty. Then it is not possible to block 515 // // do nothing 516 // } 517 // } 518 //} 519 520 func ReportBug(mapCS map[PrimInfo]struct{}) { 521 522 //for chInfo, _ := range mapCS { 523 // atomic.AddInt32(&chInfo.IntFlagFoundBug, 1) 524 //} 525 //return 526 if BoolReportBug == false { 527 return 528 } 529 lock(&MuReportBug) 530 print("-----New Blocking Bug:\n") 531 print("---Primitive location:\n") 532 for primInfo, _ := range mapCS { 533 print(primInfo.StringDebug() + "\n") 534 primInfo.SetMonitor(1) 535 } 536 print("---Primitive pointer:\n") 537 for primInfo, _ := range mapCS { 538 print(FnPointer2String(primInfo) + "\n") 539 } 540 print("-----End Bug\n") 541 unlock(&MuReportBug) 542 } 543 544 func ReportNonBlockingBug() { 545 print("-----New NonBlocking Bug:\n") 546 const size = 64 << 10 547 buf := make([]byte, size) 548 buf = buf[:Stack(buf, false)] 549 print("---Stack:\n", string(buf), "\n") 550 print("-----End Bug\n") 551 } 552 553 // Part 1.2 Data structure for waitgroup 554 555 // WgInfo is 1-to-1 with every WaitGroup. 556 type WgInfo struct { 557 WgCounter uint32 558 MapRefGoroutine map[*GoInfo]struct{} 559 StrDebug string 560 EnableOracle bool // Disable oracle for channels in SDK 561 IntFlagFoundBug int32 // Use atomic int32 operations to mark if a bug is reported 562 Mu mutex // Protects MapRefGoroutine 563 Uint32Monitor uint32 564 } 565 566 func NewWgInfo() *WgInfo { 567 _, strFile, intLine, _ := Caller(2) 568 strLoc := strFile + ":" + Itoa(intLine) 569 wg := &WgInfo{ 570 WgCounter: 0, 571 MapRefGoroutine: make(map[*GoInfo]struct{}), 572 StrDebug: strLoc, 573 EnableOracle: Index(strLoc, strSDKPath) < 0, 574 IntFlagFoundBug: 0, 575 Mu: mutex{}, 576 } 577 return wg 578 } 579 580 func (w *WgInfo) StringDebug() string { 581 if w == nil { 582 return "" 583 } 584 return w.StrDebug 585 } 586 587 func (w *WgInfo) SetMonitor(i uint32) { 588 atomic.StoreUint32(&w.Uint32Monitor, i) 589 } 590 591 func (w *WgInfo) LoadMonitor() uint32 { 592 return atomic.LoadUint32(&w.Uint32Monitor) 593 } 594 595 // FindChanInfo can retrieve a initialized ChanInfo for a given channel 596 func FindWgInfo(wg interface{}) *WgInfo { 597 lock(&MuMapChToChanInfo) 598 wgInfo := MapChToChanInfo[wg] 599 unlock(&MuMapChToChanInfo) 600 return wgInfo.(*WgInfo) 601 } 602 603 func LinkWgToLastWgInfo(wg interface{}) { 604 lock(&MuMapChToChanInfo) 605 MapChToChanInfo[wg] = LoadLastPrimInfo() 606 unlock(&MuMapChToChanInfo) 607 } 608 609 func (w *WgInfo) Lock() { 610 lock(&w.Mu) 611 } 612 613 func (w *WgInfo) Unlock() { 614 unlock(&w.Mu) 615 } 616 617 // Must be called with lock 618 func (w *WgInfo) MapRef() map[*GoInfo]struct{} { 619 return w.MapRefGoroutine 620 } 621 622 // This means the goroutine mapped with goInfo holds the reference to chInfo.Chan 623 // Must be called when chInfo.Chan.lock is held 624 func (w *WgInfo) AddGoroutine(goInfo *GoInfo) { 625 w.MapRefGoroutine[goInfo] = struct{}{} 626 } 627 628 // Must be called when chInfo.Chan.lock is held 629 func (w *WgInfo) RemoveGoroutine(goInfo *GoInfo) { 630 delete(w.MapRefGoroutine, goInfo) 631 } 632 633 func (w *WgInfo) IamBug() { 634 635 } 636 637 // Part 1.3 Data structure for mutex 638 639 // MuInfo is 1-to-1 with every sync.Mutex. 640 type MuInfo struct { 641 MapRefGoroutine map[*GoInfo]struct{} 642 StrDebug string 643 EnableOracle bool // Disable oracle for channels in SDK 644 IntFlagFoundBug int32 // Use atomic int32 operations to mark if a bug is reported 645 Mu mutex // Protects MapRefGoroutine 646 Uint32Monitor uint32 647 } 648 649 func NewMuInfo() *MuInfo { 650 _, strFile, intLine, _ := Caller(2) 651 strLoc := strFile + ":" + Itoa(intLine) 652 mu := &MuInfo{ 653 MapRefGoroutine: make(map[*GoInfo]struct{}), 654 StrDebug: strLoc, 655 EnableOracle: Index(strLoc, strSDKPath) < 0, 656 IntFlagFoundBug: 0, 657 Mu: mutex{}, 658 } 659 return mu 660 } 661 662 func (m *MuInfo) StringDebug() string { 663 if m == nil { 664 return "" 665 } 666 return m.StrDebug 667 } 668 669 func (m *MuInfo) SetMonitor(i uint32) { 670 atomic.StoreUint32(&m.Uint32Monitor, i) 671 } 672 673 func (m *MuInfo) LoadMonitor() uint32 { 674 return atomic.LoadUint32(&m.Uint32Monitor) 675 } 676 677 // FindChanInfo can retrieve a initialized ChanInfo for a given channel 678 func FindMuInfo(mu interface{}) *MuInfo { 679 lock(&MuMapChToChanInfo) 680 muInfo := MapChToChanInfo[mu] 681 unlock(&MuMapChToChanInfo) 682 return muInfo.(*MuInfo) 683 } 684 685 func LinkMuToLastMuInfo(mu interface{}) { 686 lock(&MuMapChToChanInfo) 687 MapChToChanInfo[mu] = LoadLastPrimInfo() 688 unlock(&MuMapChToChanInfo) 689 } 690 691 func (mu *MuInfo) Lock() { 692 lock(&mu.Mu) 693 } 694 695 func (mu *MuInfo) Unlock() { 696 unlock(&mu.Mu) 697 } 698 699 // Must be called with lock 700 func (mu *MuInfo) MapRef() map[*GoInfo]struct{} { 701 return mu.MapRefGoroutine 702 } 703 704 // This means the goroutine mapped with goInfo holds the reference to chInfo.Chan 705 // Must be called when chInfo.Chan.lock is held 706 func (mu *MuInfo) AddGoroutine(goInfo *GoInfo) { 707 mu.MapRefGoroutine[goInfo] = struct{}{} 708 } 709 710 // Must be called when chInfo.Chan.lock is held 711 func (mu *MuInfo) RemoveGoroutine(goInfo *GoInfo) { 712 delete(mu.MapRefGoroutine, goInfo) 713 } 714 715 // Part 1.4 Data structure for rwmutex 716 717 // RWMuInfo is 1-to-1 with every sync.RWMutex. 718 type RWMuInfo struct { 719 MapRefGoroutine map[*GoInfo]struct{} 720 StrDebug string 721 EnableOracle bool // Disable oracle for channels in SDK 722 IntFlagFoundBug int32 // Use atomic int32 operations to mark if a bug is reported 723 Mu mutex // Protects MapRefGoroutine 724 Uint32Monitor uint32 725 } 726 727 func NewRWMuInfo() *RWMuInfo { 728 _, strFile, intLine, _ := Caller(2) 729 strLoc := strFile + ":" + Itoa(intLine) 730 mu := &RWMuInfo{ 731 MapRefGoroutine: make(map[*GoInfo]struct{}), 732 StrDebug: strLoc, 733 EnableOracle: Index(strLoc, strSDKPath) < 0, 734 IntFlagFoundBug: 0, 735 Mu: mutex{}, 736 } 737 return mu 738 } 739 740 func (m *RWMuInfo) StringDebug() string { 741 if m == nil { 742 return "" 743 } 744 return m.StrDebug 745 } 746 747 func (m *RWMuInfo) SetMonitor(i uint32) { 748 atomic.StoreUint32(&m.Uint32Monitor, i) 749 } 750 751 func (m *RWMuInfo) LoadMonitor() uint32 { 752 return atomic.LoadUint32(&m.Uint32Monitor) 753 } 754 755 // FindChanInfo can retrieve a initialized ChanInfo for a given channel 756 func FindRWMuInfo(rwmu interface{}) *RWMuInfo { 757 lock(&MuMapChToChanInfo) 758 muInfo := MapChToChanInfo[rwmu] 759 unlock(&MuMapChToChanInfo) 760 return muInfo.(*RWMuInfo) 761 } 762 763 func LinkRWMuToLastRWMuInfo(rwmu interface{}) { 764 lock(&MuMapChToChanInfo) 765 MapChToChanInfo[rwmu] = LoadLastPrimInfo() 766 unlock(&MuMapChToChanInfo) 767 } 768 769 func (mu *RWMuInfo) Lock() { 770 lock(&mu.Mu) 771 } 772 773 func (mu *RWMuInfo) Unlock() { 774 unlock(&mu.Mu) 775 } 776 777 // Must be called with lock 778 func (mu *RWMuInfo) MapRef() map[*GoInfo]struct{} { 779 return mu.MapRefGoroutine 780 } 781 782 // This means the goroutine mapped with goInfo holds the reference to chInfo.Chan 783 // Must be called when chInfo.Chan.lock is held 784 func (mu *RWMuInfo) AddGoroutine(goInfo *GoInfo) { 785 mu.MapRefGoroutine[goInfo] = struct{}{} 786 } 787 788 // Must be called when chInfo.Chan.lock is held 789 func (mu *RWMuInfo) RemoveGoroutine(goInfo *GoInfo) { 790 delete(mu.MapRefGoroutine, goInfo) 791 } 792 793 // Part 1.5 Data structure for conditional variable 794 795 // CondInfo is 1-to-1 with every sync.Cond. 796 type CondInfo struct { 797 MapRefGoroutine map[*GoInfo]struct{} 798 StrDebug string 799 EnableOracle bool // Disable oracle for channels in SDK 800 IntFlagFoundBug int32 // Use atomic int32 operations to mark if a bug is reported 801 Mu mutex // Protects MapRefGoroutine 802 Uint32Monitor uint32 803 } 804 805 func NewCondInfo() *CondInfo { 806 _, strFile, intLine, _ := Caller(2) 807 strLoc := strFile + ":" + Itoa(intLine) 808 cond := &CondInfo{ 809 MapRefGoroutine: make(map[*GoInfo]struct{}), 810 StrDebug: strLoc, 811 EnableOracle: Index(strLoc, strSDKPath) < 0, 812 IntFlagFoundBug: 0, 813 Mu: mutex{}, 814 } 815 return cond 816 } 817 818 func (c *CondInfo) StringDebug() string { 819 if c == nil { 820 return "" 821 } 822 return c.StrDebug 823 } 824 825 func (c *CondInfo) SetMonitor(i uint32) { 826 atomic.StoreUint32(&c.Uint32Monitor, i) 827 } 828 829 func (c *CondInfo) LoadMonitor() uint32 { 830 return atomic.LoadUint32(&c.Uint32Monitor) 831 } 832 833 // FindChanInfo can retrieve a initialized ChanInfo for a given channel 834 func FindCondInfo(cond interface{}) *CondInfo { 835 lock(&MuMapChToChanInfo) 836 condInfo := MapChToChanInfo[cond] 837 unlock(&MuMapChToChanInfo) 838 return condInfo.(*CondInfo) 839 } 840 841 func LinkCondToLastCondInfo(cond interface{}) { 842 lock(&MuMapChToChanInfo) 843 MapChToChanInfo[cond] = LoadLastPrimInfo() 844 unlock(&MuMapChToChanInfo) 845 } 846 847 func (cond *CondInfo) Lock() { 848 lock(&cond.Mu) 849 } 850 851 func (cond *CondInfo) Unlock() { 852 unlock(&cond.Mu) 853 } 854 855 // Must be called with lock 856 func (cond *CondInfo) MapRef() map[*GoInfo]struct{} { 857 return cond.MapRefGoroutine 858 } 859 860 // This means the goroutine mapped with goInfo holds the reference to chInfo.Chan 861 // Must be called when chInfo.Chan.lock is held 862 func (cond *CondInfo) AddGoroutine(goInfo *GoInfo) { 863 cond.MapRefGoroutine[goInfo] = struct{}{} 864 } 865 866 // Must be called when chInfo.Chan.lock is held 867 func (cond *CondInfo) RemoveGoroutine(goInfo *GoInfo) { 868 delete(cond.MapRefGoroutine, goInfo) 869 } 870 871 //// Part 2.1: data struct for each goroutine 872 873 // GoInfo is 1-to-1 with each goroutine. 874 // Go language doesn't allow us to acquire the ID of a goroutine, because they want goroutines to be anonymous. 875 // Normally, Go programmers use runtime.Stack() to print all IDs of all goroutines, but this function is very inefficient 876 //, since it calls stopTheWorld() 877 // Currently we use a global atomic int64 to differentiate each goroutine, and a variable currentGo to represent each goroutine 878 // This is not a good practice because the goroutine need to pass currentGo to its every callee 879 type GoInfo struct { 880 G *g 881 VecBlockInfo []BlockInfo // Nil when normally running. When blocked at an operation of ChanInfo, store 882 // one ChanInfo and the operation. When blocked at select, store multiple ChanInfo and 883 // operation. Default in select is also also stored in map, which is DefaultCaseChanInfo 884 BitCheckBugAtEnd uint32 // 0 when normally running. 1 when this goroutine is checking bug. 885 MapPrimeInfo map[PrimInfo]struct{} // Stores all channels that this goroutine still hold reference to 886 Mu mutex // protects VecBlockInfo and MapPrimeInfo 887 } 888 889 type BlockInfo struct { 890 Prim PrimInfo 891 StrOp string 892 } 893 894 const ( 895 Send = "Send" 896 Recv = "Recv" 897 Close = "Close" 898 Select = "Select" 899 900 MuLock = "MuLock" 901 MuUnlock = "MuUnlock" 902 903 WgWait = "WgWait" 904 905 CdWait = "CdWait" 906 CdSignal = "CdSignal" 907 CdBroadcast = "CdBroadcast" 908 ) 909 910 // Initialize a GoInfo 911 func NewGoInfo(goroutine *g) *GoInfo { 912 newGoInfo := &GoInfo{ 913 G: goroutine, 914 VecBlockInfo: []BlockInfo{}, 915 MapPrimeInfo: make(map[PrimInfo]struct{}), 916 } 917 return newGoInfo 918 } 919 920 func CurrentGoInfo() *GoInfo { 921 return getg().goInfo 922 } 923 924 func StoreLastPrimInfo(chInfo PrimInfo) { 925 getg().lastPrimInfo = chInfo 926 } 927 928 func LoadLastPrimInfo() PrimInfo { 929 return getg().lastPrimInfo 930 } 931 932 func CurrentGoID() int64 { 933 return getg().goid 934 } 935 936 // This means the goroutine mapped with goInfo holds the reference to chInfo.Chan 937 func (goInfo *GoInfo) AddPrime(chInfo PrimInfo) { 938 if goInfo.MapPrimeInfo == nil { 939 goInfo.MapPrimeInfo = make(map[PrimInfo]struct{}) 940 } 941 goInfo.MapPrimeInfo[chInfo] = struct{}{} 942 } 943 944 func (goInfo *GoInfo) RemovePrime(chInfo PrimInfo) { 945 if goInfo.MapPrimeInfo != nil { 946 delete(goInfo.MapPrimeInfo, chInfo) 947 } 948 } 949 950 func CurrentGoAddCh(ch interface{}) { 951 lock(&MuMapChToChanInfo) 952 chInfo, exist := MapChToChanInfo[ch] 953 unlock(&MuMapChToChanInfo) 954 if !exist { 955 return 956 } 957 AddRefGoroutine(chInfo, CurrentGoInfo()) 958 } 959 960 // RemoveRef should be called at the end of every goroutine. It will remove goInfo from the reference list of every 961 // channel it holds the reference to 962 func (goInfo *GoInfo) RemoveAllRef() { 963 964 if goInfo.MapPrimeInfo == nil { 965 return 966 } 967 for chInfo, _ := range goInfo.MapPrimeInfo { 968 RemoveRefGoroutine(chInfo, goInfo) 969 if chInfo == nil { 970 continue 971 } 972 CS := []PrimInfo{chInfo} 973 if BoolDelayCheck { 974 EnqueueCheckEntry(CS) 975 } else { 976 CheckBlockBug(CS) 977 } 978 } 979 } 980 981 // SetBlockAt should be called before each channel operation, meaning the current goroutine is about to execute that operation 982 // Note that we check bug in this function, because it's possible for the goroutine to be blocked forever if it execute that operation 983 // For example, a channel with no buffer is held by a parent and a child. 984 // The parent has already exited, but the child is now about to send to that channel. 985 // Then now is our only chance to detect this bug, so we call CheckBlockBug() 986 func (goInfo *GoInfo) SetBlockAt(prim PrimInfo, strOp string) { 987 goInfo.VecBlockInfo = append(goInfo.VecBlockInfo, BlockInfo{ 988 Prim: prim, 989 StrOp: strOp, 990 }) 991 } 992 993 // WithdrawBlock should be called after each channel operation, meaning the current goroutine finished execution that operation 994 // If the operation is select, remember to call this function right after each case of the select 995 func (goInfo *GoInfo) WithdrawBlock(checkEntry *CheckEntry) { 996 goInfo.VecBlockInfo = []BlockInfo{} 997 if checkEntry != nil { 998 atomic.StoreUint32(&checkEntry.Uint32NeedCheck, 0) 999 } 1000 } 1001 1002 func (goInfo *GoInfo) IsBlock() (boolIsBlock bool, strOp string) { 1003 boolIsBlock, strOp = false, "" 1004 boolIsSelect := false 1005 1006 lock(&goInfo.Mu) 1007 defer unlock(&goInfo.Mu) 1008 if len(goInfo.VecBlockInfo) == 0 { 1009 return 1010 } else { 1011 boolIsBlock = true 1012 } 1013 1014 // Now we compute strOp 1015 1016 if len(goInfo.VecBlockInfo) > 1 { 1017 boolIsSelect = true 1018 } else if len(goInfo.VecBlockInfo) == 0 { 1019 print("Fatal in GoInfo.IsBlock(): goInfo.VecBlockInfo is not nil but lenth is 0\n") 1020 } 1021 1022 if boolIsSelect { 1023 strOp = Select 1024 } else { 1025 for _, blockInfo := range goInfo.VecBlockInfo { // This loop will be executed only one time, since goInfo.VecBlockInfo's len() is 1 1026 strOp = blockInfo.StrOp 1027 } 1028 } 1029 1030 return 1031 } 1032 1033 // This function checks if the goroutine mapped with goInfo is currently blocking at an operation of chInfo.Chan 1034 // If so, returns true and the string of channel operation 1035 func (goInfo *GoInfo) IsBlockAtGivenChan(chInfo *ChanInfo) (boolIsBlockAtGiven bool, strOp string) { 1036 boolIsBlockAtGiven, strOp = false, "" 1037 1038 lock(&goInfo.Mu) 1039 defer unlock(&goInfo.Mu) 1040 if goInfo.VecBlockInfo == nil { 1041 return 1042 } 1043 1044 for _, blockInfo := range goInfo.VecBlockInfo { 1045 if blockInfo.Prim == chInfo { 1046 boolIsBlockAtGiven = true 1047 strOp = blockInfo.StrOp 1048 break 1049 } 1050 } 1051 1052 return 1053 }