├── .circleci
└── config.yml
├── .gitignore
├── LICENSE
├── README.md
├── cmd
└── chronos
│ └── main.go
├── domain
├── BlockState.go
├── Context.go
├── DeferFunction.go
├── FunctionState.go
├── GuardedAccess.go
├── Lockset.go
└── VectorClock.go
├── go.mod
├── go.sum
├── output
└── warnings.go
├── pointerAnalysis
└── PointerAnalysis.go
├── ssaPureUtils
├── General.go
├── Locks.go
└── Structs.go
├── ssaUtils
├── CFG.go
├── Functions.go
├── Functions_test.go
├── Locks.go
├── Packages.go
├── PreProcess.go
├── testdata
│ ├── Functions
│ │ ├── Defer
│ │ │ ├── DeferredLockAndUnlockIfBranch
│ │ │ │ └── prog1.go
│ │ │ ├── NestedDeferWithLockAndUnlock
│ │ │ │ └── prog1.go
│ │ │ └── NestedDeferWithLockAndUnlockAndGoroutine
│ │ │ │ └── prog1.go
│ │ ├── ForLoops
│ │ │ ├── ForLoopLockInsideLoop
│ │ │ │ └── prog1.go
│ │ │ ├── ForLoopLockOutsideLoop
│ │ │ │ └── prog1.go
│ │ │ ├── NestedForLoopWithRace
│ │ │ │ └── prog1.go
│ │ │ ├── WhileLoop
│ │ │ │ └── prog1.go
│ │ │ └── WhileLoopWithoutHeader
│ │ │ │ └── prog1.go
│ │ ├── General
│ │ │ ├── DataRaceGoto
│ │ │ │ └── prog1.go
│ │ │ ├── DataRaceMap
│ │ │ │ └── prog1.go
│ │ │ ├── DataRaceNestedSameFunction
│ │ │ │ └── prog1.go
│ │ │ ├── DataRaceProperty
│ │ │ │ └── prog1.go
│ │ │ ├── DataRaceRecursion
│ │ │ │ └── prog1.go
│ │ │ ├── DataRaceShadowedErr
│ │ │ │ └── prog1.go
│ │ │ ├── DataRaceWithOnlyAlloc
│ │ │ │ └── prog1.go
│ │ │ ├── DataRaceWithSameFunction
│ │ │ │ └── prog1.go
│ │ │ ├── ElseIf
│ │ │ │ └── prog1.go
│ │ │ ├── NestedFunctions
│ │ │ │ └── prog1.go
│ │ │ ├── RecursionWithGoroutine
│ │ │ │ └── prog1.go
│ │ │ ├── RecursionWithRecover
│ │ │ │ └── prog1.go
│ │ │ ├── Simple
│ │ │ │ └── prog1.go
│ │ │ └── StructMethod
│ │ │ │ └── prog1.go
│ │ ├── Interfaces
│ │ │ ├── DataRaceIceCreamMaker
│ │ │ │ └── prog1.go
│ │ │ ├── InterfaceWithLock
│ │ │ │ └── prog1.go
│ │ │ └── NestedInterface
│ │ │ │ └── prog1.go
│ │ ├── LocksAndUnlocks
│ │ │ ├── Lock
│ │ │ │ └── prog1.go
│ │ │ ├── LockAndUnlock
│ │ │ │ └── prog1.go
│ │ │ ├── LockAndUnlockIfBranch
│ │ │ │ └── prog1.go
│ │ │ ├── LockInBothBranches
│ │ │ │ └── prog1.go
│ │ │ ├── LockInsideGoroutine
│ │ │ │ └── prog1.go
│ │ │ ├── MultipleLocksNoRace
│ │ │ │ └── prog1.go
│ │ │ ├── MultipleLocksRace
│ │ │ │ └── prog1.go
│ │ │ ├── NestedConditionWithLockInAllBranches
│ │ │ │ └── prog1.go
│ │ │ └── NestedLockInStruct
│ │ │ │ └── prog1.go
│ │ └── PointerAnalysis
│ │ │ └── DataRaceInterfaceOverChannel
│ │ │ └── prog1.go
│ └── Preprocess
│ │ ├── EvenLockAndUnlockAndLockComesLater
│ │ └── EvenLockAndUnlockAndLockComesLater.go
│ │ ├── LockAndUnlockInDifferentBranches
│ │ └── LockAndUnlockInDifferentBranches.go
│ │ ├── LockInAStruct
│ │ └── LockInAStruct.go
│ │ ├── LockInEmbeddedStruct
│ │ └── LockInEmbeddedStruct.go
│ │ ├── MutexInterface
│ │ └── MutexInterface.go
│ │ ├── NestedFunctionWithLock
│ │ └── NestedFunctionWithLock.go
│ │ ├── NestedFunctionWithLockButThenUnlock
│ │ └── NestedFunctionWithLockButThenUnlock.go
│ │ ├── RecursionNestedFunctionWithLock
│ │ └── RecursionNestedFunctionWithLock.go
│ │ ├── UnevenLockAndUnlockAndLockComesLater
│ │ └── UnevenLockAndUnlockAndLockComesLater.go
│ │ ├── functionWithEvenLockAndDeferUnlock
│ │ └── functionWithEvenLockAndDeferUnlock.go
│ │ ├── functionWithEvenLockAndUnlock
│ │ └── functionWithEvenLockAndUnlock.go
│ │ ├── functionWithLock
│ │ └── functionWithLock.go
│ │ ├── functionWithUnevenLockAndUnlock
│ │ └── functionWithUnevenLockAndUnlock.go
│ │ ├── functionWithUnlock
│ │ └── functionWithUnlock.go
│ │ └── functionWithoutLocks
│ │ └── functionWithoutLocks.go
└── testutils.go
├── tests
├── stdlib_runner_test.go
└── testdata
│ ├── stdlib
│ ├── RaceNestedArrayCopy
│ │ └── prog1.go
│ ├── TestNoRaceAddrExpr
│ │ └── prog1.go
│ ├── TestNoRaceAsFunc4
│ │ └── prog1.go
│ ├── TestNoRaceCase
│ │ └── prog1.go
│ ├── TestNoRaceComp
│ │ └── prog1.go
│ ├── TestNoRaceEnoughRegisters
│ │ └── prog1.go
│ ├── TestNoRaceFuncArgsRW
│ │ └── prog1.go
│ ├── TestNoRaceHeapReallocation
│ │ └── prog1.go
│ ├── TestNoRaceIntRWClosures
│ │ └── prog1.go
│ ├── TestNoRaceMethodThunk
│ │ └── prog1.go
│ ├── TestNoRaceMethodValue
│ │ └── prog1.go
│ ├── TestNoRacePlus
│ │ └── prog1.go
│ ├── TestNoRaceRangeIssue5446
│ │ └── prog1.go
│ ├── TestNoRaceSelect4
│ │ └── prog1.go
│ ├── TestNoRaceStackPushPop
│ │ └── prog1.go
│ ├── TestNoRaceStructFieldRW1
│ │ └── prog1.go
│ ├── TestNoRaceStructFieldRW2
│ │ └── prog1.go
│ ├── TestNoRaceTinyAlloc
│ │ └── prog1.go
│ ├── TestRaceAddrExpr
│ │ └── prog1.go
│ ├── TestRaceAnd
│ │ └── prog1.go
│ ├── TestRaceAnd2
│ │ └── prog1.go
│ ├── TestRaceAppendCapRW
│ │ └── prog1.go
│ ├── TestRaceAppendLenRW
│ │ └── prog1.go
│ ├── TestRaceAppendRW
│ │ └── prog1.go
│ ├── TestRaceArrayCopy
│ │ └── prog1.go
│ ├── TestRaceArrayInit
│ │ └── prog1.go
│ ├── TestRaceAsFunc1
│ │ └── prog1.go
│ ├── TestRaceAsFunc2
│ │ └── prog1.go
│ ├── TestRaceAsFunc3
│ │ └── prog1.go
│ ├── TestRaceBlockAs
│ │ └── prog1.go
│ ├── TestRaceCaseBody
│ │ └── prog1.go
│ ├── TestRaceCaseCondition
│ │ └── prog1.go
│ ├── TestRaceCaseCondition2
│ │ └── prog1.go
│ ├── TestRaceCaseFallthrough
│ │ └── prog1.go
│ ├── TestRaceCaseIssue6418
│ │ └── prog1.go
│ ├── TestRaceCaseType
│ │ └── prog1.go
│ ├── TestRaceCaseTypeBody
│ │ └── prog1.go
│ ├── TestRaceCaseTypeIssue5890
│ │ └── prog1.go
│ ├── TestRaceComp2
│ │ └── prog1.go
│ ├── TestRaceComplement
│ │ └── prog1.go
│ ├── TestRaceComplex128WW
│ │ └── prog1.go
│ ├── TestRaceDeferArg
│ │ └── prog1.go
│ ├── TestRaceDeferArg2
│ │ └── prog1.go
│ ├── TestRaceDiv
│ │ └── prog1.go
│ ├── TestRaceDivConst
│ │ └── prog1.go
│ ├── TestRaceEfaceConv
│ │ └── prog1.go
│ ├── TestRaceEfaceWW
│ │ └── prog1.go
│ ├── TestRaceEmptyInterface
│ │ └── prog1.go
│ ├── TestRaceEmptyInterface2
│ │ └── prog1.go
│ ├── TestRaceError
│ │ └── prog1.go
│ ├── TestRaceFloat64WW
│ │ └── prog1.go
│ ├── TestRaceForIncr
│ │ └── prog1.go
│ ├── TestRaceForInit
│ │ └── prog1.go
│ ├── TestRaceForTest
│ │ └── prog1.go
│ ├── TestRaceFuncArgsRW
│ │ └── prog1.go
│ ├── TestRaceFuncArgument
│ │ └── prog1.go
│ ├── TestRaceFuncArgument2
│ │ └── prog1.go
│ ├── TestRaceFuncCall
│ │ └── prog1.go
│ ├── TestRaceFuncItself
│ │ └── prog1.go
│ ├── TestRaceFuncVariableRW
│ │ └── prog1.go
│ ├── TestRaceFuncVariableWW
│ │ └── prog1.go
│ ├── TestRaceIfaceCmp
│ │ └── prog1.go
│ ├── TestRaceIfaceCmpNil
│ │ └── prog1.go
│ ├── TestRaceIfaceConv
│ │ └── prog1.go
│ ├── TestRaceIfaceWW
│ │ └── prog1.go
│ ├── TestRaceInt32RWClosures
│ │ └── prog1.go
│ ├── TestRaceIntRWClosures
│ │ └── prog1.go
│ ├── TestRaceIntRWGlobalFuncs
│ │ └── prog1.go
│ ├── TestRaceInterCall
│ │ └── prog1.go
│ ├── TestRaceInterCall2
│ │ └── prog1.go
│ ├── TestRaceIntptrRW
│ │ └── prog1.go
│ ├── TestRaceIssue5564
│ │ └── prog1.go
│ ├── TestRaceIssue5567
│ │ └── prog1.go
│ ├── TestRaceMapInit
│ │ └── prog1.go
│ ├── TestRaceMapInit2
│ │ └── prog1.go
│ ├── TestRaceMethodCall
│ │ └── prog1.go
│ ├── TestRaceMethodCall2
│ │ └── prog1.go
│ ├── TestRaceMethodThunk2
│ │ └── prog1.go
│ ├── TestRaceMethodThunk3
│ │ └── prog1.go
│ ├── TestRaceMethodValue
│ │ └── prog1.go
│ ├── TestRaceMethodValue2
│ │ └── prog1.go
│ ├── TestRaceMethodValue3
│ │ └── prog1.go
│ ├── TestRaceMod
│ │ └── prog1.go
│ ├── TestRaceModConst
│ │ └── prog1.go
│ ├── TestRaceNestedStruct
│ │ └── prog1.go
│ ├── TestRaceOr
│ │ └── prog1.go
│ ├── TestRaceOr2
│ │ └── prog1.go
│ ├── TestRacePanicArg
│ │ └── prog1.go
│ ├── TestRacePlus
│ │ └── prog1.go
│ ├── TestRacePlus2
│ │ └── prog1.go
│ ├── TestRaceRotate
│ │ └── prog1.go
│ ├── TestRaceRune
│ │ └── prog1.go
│ ├── TestRaceSelect1
│ │ └── prog1.go
│ ├── TestRaceSelect2
│ │ └── prog1.go
│ ├── TestRaceSelect3
│ │ └── prog1.go
│ ├── TestRaceSelect4
│ │ └── prog1.go
│ ├── TestRaceSelect5
│ │ └── prog1.go
│ ├── TestRaceSliceSlice
│ │ └── prog1.go
│ ├── TestRaceSliceSlice2
│ │ └── prog1.go
│ ├── TestRaceSliceString
│ │ └── prog1.go
│ ├── TestRaceSprint
│ │ └── prog1.go
│ ├── TestRaceStringPtrRW
│ │ └── prog1.go
│ ├── TestRaceStringRW
│ │ └── prog1.go
│ ├── TestRaceStructFieldRW1
│ │ └── prog1.go
│ ├── TestRaceStructFieldRW2
│ │ └── prog1.go
│ ├── TestRaceStructFieldRW3
│ │ └── prog1.go
│ ├── TestRaceStructInd
│ │ └── prog1.go
│ ├── TestRaceStructInit
│ │ └── prog1.go
│ ├── TestRaceTLS
│ │ └── prog1.go
│ ├── TestRaceTypeAssert
│ │ └── prog1.go
│ ├── TestRaceUnaddressableMapLen
│ │ └── prog1.go
│ └── TestRaceUnsafePtrRW
│ │ └── prog1.go
│ └── stdlibNoSuccess
│ ├── TestNoRaceAnd
│ └── prog1.go
│ ├── TestNoRaceBlank
│ └── prog1.go
│ ├── TestNoRaceCaseFallthrough
│ └── prog1.go
│ ├── TestNoRaceEmptyStruct
│ └── prog1.go
│ ├── TestNoRaceForIncr
│ └── prog1.go
│ ├── TestNoRaceForInit
│ └── prog1.go
│ ├── TestNoRaceFuncUnlock
│ └── prog1.go
│ ├── TestNoRaceOr
│ └── prog1.go
│ ├── TestNoRaceSelect1
│ └── prog1.go
│ ├── TestNoRaceSelect2
│ └── prog1.go
│ ├── TestNoRaceSelect3
│ └── prog1.go
│ ├── TestNoRaceSelect5
│ └── prog1.go
│ ├── TestNoRaceShortCalc
│ └── prog1.go
│ ├── TestNoRaceShortCalc2
│ └── prog1.go
│ ├── TestRaceAppendSliceStruct
│ └── prog1.go
│ ├── TestRaceHeapParam
│ └── prog1.go
│ ├── TestRaceIndirection
│ └── prog1.go
│ ├── TestRaceMethodThunk
│ └── prog1.go
│ ├── TestRaceMethodThunk4
│ └── prog1.go
│ ├── TestRacePanic
│ └── prog1.go
│ ├── TestRaceRange
│ └── prog1.go
│ ├── TestRaceSliceStruct
│ └── prog1.go
│ └── TestRaceStructRW
│ └── prog1.go
└── utils
├── DoubleKeyMap.go
├── counter.go
├── set.go
├── stacks
├── basicBlockStack.go
├── callCommonStack.go
├── functionStack.go
└── intStack.go
└── util.go
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Golang CircleCI 2.0 configuration file
2 | version: 2
3 | jobs:
4 | build:
5 | docker:
6 | - image: circleci/golang:1.15
7 | working_directory: /go/src/github.com/amit-davidson/Chronos
8 | steps:
9 | - checkout
10 | - run:
11 | name: Install dependencies
12 | command: |
13 | go get -v -t -d ./...
14 | - run:
15 | name: Run tests
16 | command: |
17 | go test -v ./...
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.a
3 | *.so
4 | .idea
5 | .vscode
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Amit Davidson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chronos
2 |
3 |
4 |
5 |
6 |
7 | [](http://golang.org)
8 | [](http://golang.org)
9 | [](https://lbesson.mit-license.org/)
10 | [](http://makeapullrequest.com)
11 | [](https://app.circleci.com/pipelines/github/amit-davidson/Chronos)
12 |
13 | Chronos is a static race detector for the Go language written in Go.
14 |
15 | ## Quick Start:
16 |
17 | Download the package
18 |
19 | ```
20 | go get -v github.com/amit-davidson/Chronos/cmd/chronos
21 | ```
22 |
23 | Pass the entry point
24 |
25 | ```
26 | chronos --file --mod
27 | ```
28 |
29 | Help
30 |
31 | ```
32 | Usage of ./chronos:
33 | --file string
34 | The file containing the entry point of the program
35 | --mod string
36 | Absolute or relative path to the module where the search should be performed. Should end in the format:{VCS}/{organization}/{package}. Packages outside this path are excluded rom the search.
37 | ```
38 |
39 | ## Example:
40 |
41 |
42 |
43 |
44 |
45 |
46 | ## Features:
47 |
48 | Support:
49 |
50 | - Detects races on pointers passed around the program.
51 | - Analysis of conditional branches, nested functions, interfaces, select, gotos, defers, for loops and recursions.
52 | - Synchronization using mutex and goroutines starts.
53 |
54 | Limitations:
55 |
56 | - Big programs and external packages. (Due to stack overflow)
57 | - Synchronization using channels, waitgroups, once, cond and atomic.
58 |
59 | ## Chronos vs go race:
60 |
61 | Chronos successfully reports cases where go race fails thanks to it's static nature. Mostly because data races appear in unexpected production workloads, which are hard to produce in dev.
62 | In addition, go race is having trouble with short programs where without contrived synchronization the program may exit too quickly.
63 |
64 | In contrast, Chronos managed to report only 244/403 = 60.5% of go race test cases. This can be explained by Chronos partial support with Go's features so this number will increase in the future.
65 | Also, it lacked due to his static nature where context/path sensitivity was required.
66 |
67 | Therefore, I suggest using both according the strengths and weaknesses of each of the race detectors.
68 |
69 | ## Credits:
70 |
71 | Jan Wen, J., Jhala, R., & Lerner, S. (n.d.). [RELAY: Static Race Detection on Millions of Lines of Code](https://cseweb.ucsd.edu/~lerner/papers/relay.pdf)
72 | Colin J. Fidge (February 1988). [Timestamps in Message-Passing Systems That Preserve the Partial Ordering"](http://zoo.cs.yale.edu/classes/cs426/2012/lab/bib/fidge88timestamps.pdf)
73 |
74 | ## More examples:
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/cmd/chronos/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "github.com/amit-davidson/Chronos/domain"
7 | "github.com/amit-davidson/Chronos/output"
8 | "github.com/amit-davidson/Chronos/pointerAnalysis"
9 | "github.com/amit-davidson/Chronos/ssaUtils"
10 | "github.com/amit-davidson/Chronos/utils"
11 | "golang.org/x/tools/go/ssa"
12 | "os"
13 | )
14 |
15 | func main() {
16 | defaultFile := flag.String("file", "", "The file containing the entry point of the program")
17 | defaultModulePath := flag.String("mod", "", "PPath to the module where the search should be performed. Path to module can be relative or absolute but must contain the format:{VCS}/{organization}/{package}. Packages outside this path are excluded rom the search.")
18 | flag.Parse()
19 | if *defaultFile == "" {
20 | fmt.Printf("Please provide a file to load\n")
21 | os.Exit(1)
22 | }
23 | if *defaultModulePath == "" {
24 | fmt.Printf("Please provide a path to the module. path to module can be relative or absolute but must contain the format:{VCS}/{organization}/{package}.\n")
25 | os.Exit(1)
26 | }
27 | domain.GoroutineCounter = utils.NewCounter()
28 | domain.GuardedAccessCounter = utils.NewCounter()
29 | domain.PosIDCounter = utils.NewCounter()
30 |
31 | ssaProg, ssaPkg, err := ssaUtils.LoadPackage(*defaultFile, *defaultModulePath)
32 | if err != nil {
33 | fmt.Printf("Failed loading with the following error:%s\n", err)
34 | os.Exit(1)
35 | }
36 | entryFunc := ssaPkg.Func("main")
37 | err = ssaUtils.InitPreProcess(ssaProg, *defaultModulePath)
38 | if err != nil {
39 | fmt.Print(err)
40 | os.Exit(1)
41 | }
42 |
43 | entryCallCommon := ssa.CallCommon{Value: entryFunc}
44 | functionState := ssaUtils.HandleCallCommon(domain.NewEmptyContext(), &entryCallCommon, entryFunc.Pos())
45 | conflictingGAs, err := pointerAnalysis.Analysis(ssaPkg, functionState.GuardedAccesses)
46 | if err != nil {
47 | fmt.Printf("Error in analysis:%s\n", err)
48 | os.Exit(1)
49 | }
50 | err = output.GenerateError(conflictingGAs, ssaProg)
51 | if err != nil {
52 | fmt.Printf("Error in generating errors:%s\n", err)
53 | os.Exit(1)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/domain/BlockState.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import "github.com/amit-davidson/Chronos/utils/stacks"
4 |
5 | type BlockState struct {
6 | GuardedAccesses []*GuardedAccess
7 | Lockset *Lockset
8 | DeferredFunctions *stacks.CallCommonStack
9 | }
10 |
11 | func GetEmptyBlockState() *BlockState {
12 | return &BlockState{
13 | GuardedAccesses: make([]*GuardedAccess, 0),
14 | Lockset: NewLockset(),
15 | DeferredFunctions: stacks.NewCallCommonStack(),
16 | }
17 | }
18 |
19 | func CreateBlockState(ga []*GuardedAccess, ls *Lockset, df *stacks.CallCommonStack) *BlockState {
20 | return &BlockState{
21 | GuardedAccesses: ga,
22 | Lockset: ls,
23 | DeferredFunctions: df,
24 | }
25 | }
26 |
27 | // AddFunctionCallState is used to add the state of a function call to the blocks total state when iterating through it.
28 | // shouldMergeLockset is used depending if the call was using a goroutine or not.
29 | func (existingBlock *BlockState) AddFunctionCallState(newBlock *BlockState, shouldMergeLockset bool) {
30 | for _, guardedAccess := range newBlock.GuardedAccesses {
31 | guardedAccess.Lockset.UpdateWithPrevLockset(existingBlock.Lockset)
32 |
33 | }
34 | existingBlock.GuardedAccesses = append(existingBlock.GuardedAccesses, newBlock.GuardedAccesses...)
35 | if shouldMergeLockset {
36 | existingBlock.Lockset.UpdateWithNewLockSet(newBlock.Lockset.Locks, newBlock.Lockset.Unlocks)
37 | }
38 | }
39 |
40 | // MergeChildBlock merges child block with it's parent in append-like fashion.
41 | // A -> B
42 | // Will Merge B unto A
43 | func (existingBlock *BlockState) MergeChildBlock(newBlock *BlockState) {
44 | for _, guardedAccess := range newBlock.GuardedAccesses {
45 | guardedAccess.Lockset.UpdateWithPrevLockset(existingBlock.Lockset)
46 | }
47 | existingBlock.GuardedAccesses = append(existingBlock.GuardedAccesses, newBlock.GuardedAccesses...)
48 | existingBlock.DeferredFunctions.MergeStacks(newBlock.DeferredFunctions)
49 | existingBlock.Lockset.UpdateWithNewLockSet(newBlock.Lockset.Locks, newBlock.Lockset.Unlocks)
50 | }
51 |
52 | // MergeSiblingBlock merges sibling blocks in merge-like fashion.
53 | // A -> B
54 | // -> C
55 | // Will Merge B and C
56 | func (existingBlock *BlockState) MergeSiblingBlock(newBlock *BlockState) {
57 | existingGAs := make(map[int]*GuardedAccess, len(existingBlock.GuardedAccesses))
58 | for _, ga := range existingBlock.GuardedAccesses {
59 | existingGAs[ga.ID] = ga
60 | }
61 |
62 | for _, newGA := range newBlock.GuardedAccesses {
63 | if existingGA, ok := existingGAs[newGA.ID]; !ok {
64 | existingBlock.GuardedAccesses = append(existingBlock.GuardedAccesses, newGA)
65 | } else {
66 | existingGA.Lockset.MergeSiblingLockset(newGA.Lockset)
67 | }
68 | }
69 |
70 | existingBlock.Lockset.MergeSiblingLockset(newBlock.Lockset)
71 | }
72 |
73 | func (existingBlock *BlockState) Copy() *BlockState {
74 | newFunctionState := &BlockState{}
75 | newFunctionState.Lockset = existingBlock.Lockset.Copy()
76 | for _, ga := range existingBlock.GuardedAccesses {
77 | newFunctionState.GuardedAccesses = append(newFunctionState.GuardedAccesses, ga.Copy())
78 | }
79 | newFunctionState.DeferredFunctions = existingBlock.DeferredFunctions
80 | return newFunctionState
81 | }
82 |
--------------------------------------------------------------------------------
/domain/Context.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/utils/stacks"
5 | )
6 |
7 | // Flow context
8 | type Context struct {
9 | GoroutineID int
10 | Clock VectorClock
11 | StackTrace *stacks.IntStackWithMap
12 | }
13 |
14 | func NewEmptyContext() *Context {
15 | return &Context{
16 | Clock: VectorClock{},
17 | GoroutineID: GoroutineCounter.GetNext(),
18 | StackTrace: stacks.NewEmptyIntStackWithMap(),
19 | }
20 | }
21 |
22 | func NewGoroutineExecutionState(state *Context) *Context {
23 | state.Increment()
24 | return &Context{
25 | Clock: state.Clock.Copy(),
26 | GoroutineID: GoroutineCounter.GetNext(),
27 | StackTrace: state.StackTrace.Copy(),
28 | }
29 | }
30 |
31 | func (gs *Context) Increment() {
32 | gs.Clock[gs.GoroutineID] += 1
33 | }
34 |
35 | func (gs *Context) MayConcurrent(state *Context) bool {
36 | timestampAidA := gs.Clock.Get(gs.GoroutineID)
37 | timestampAidB := state.Clock.Get(gs.GoroutineID)
38 | timestampBidA := gs.Clock.Get(state.GoroutineID)
39 | timestampBidB := state.Clock.Get(state.GoroutineID)
40 | isBefore := timestampAidA <= timestampAidB && timestampBidA < timestampBidB
41 | isAfter := timestampBidB <= timestampBidA && timestampAidB < timestampAidA
42 | return !(isBefore || isAfter)
43 | }
44 |
45 | func (gs *Context) Copy() *Context {
46 | return &Context{
47 | GoroutineID: gs.GoroutineID,
48 | Clock: gs.Clock.Copy(),
49 | StackTrace: gs.StackTrace.Copy(),
50 | }
51 | }
52 |
53 | func (gs *Context) CopyWithoutMap() *Context {
54 | return &Context{
55 | GoroutineID: gs.GoroutineID,
56 | Clock: gs.Clock.Copy(),
57 | StackTrace: stacks.NewIntStackWithMap(*gs.StackTrace.GetItems().Copy(), nil),
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/domain/DeferFunction.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import "golang.org/x/tools/go/ssa"
4 |
5 | type DeferFunction struct {
6 | Function *ssa.CallCommon
7 | BlockIndex int
8 | }
9 |
--------------------------------------------------------------------------------
/domain/FunctionState.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/utils"
5 | "github.com/amit-davidson/Chronos/utils/stacks"
6 | )
7 |
8 | var GoroutineCounter *utils.Counter
9 | var GuardedAccessCounter *utils.Counter
10 | var PosIDCounter *utils.Counter
11 |
12 | type FunctionState struct {
13 | GuardedAccesses []*GuardedAccess
14 | Lockset *Lockset
15 | }
16 |
17 | func GetFunctionState() *FunctionState {
18 | return &FunctionState{
19 | GuardedAccesses: make([]*GuardedAccess, 0),
20 | Lockset: NewLockset(),
21 | }
22 | }
23 |
24 | func CreateFunctionState(ga []*GuardedAccess, ls *Lockset) *FunctionState {
25 | return &FunctionState{
26 | GuardedAccesses: ga,
27 | Lockset: ls,
28 | }
29 | }
30 |
31 | // AddContextToFunction adds flow specific context data
32 | func (fs *FunctionState) AddContextToFunction(context *Context) {
33 | for _, ga := range fs.GuardedAccesses {
34 | ga.ID = GuardedAccessCounter.GetNext()
35 | ga.State.GoroutineID = context.GoroutineID
36 | context.Increment()
37 |
38 | relativePos := ga.State.StackTrace.Iter()[ga.PosToRemove+1:]
39 | tmpContext := context.CopyWithoutMap()
40 | tmpContext.StackTrace.GetItems().MergeStacks((*stacks.IntStack)(&relativePos))
41 | ga.State.StackTrace = tmpContext.StackTrace
42 | ga.State.Clock = tmpContext.Clock
43 | }
44 | }
45 |
46 | // RemoveContextFromFunction strips any context related data from the guarded access fields. It nullifies id, goroutine id,
47 | // clock and removes from the guarded access the prefix that matches the context path. This way, other flows can take
48 | // the guarded access and add relevant data.
49 | func (fs *FunctionState) RemoveContextFromFunction() {
50 | gas := make([]*GuardedAccess, 0, len(fs.GuardedAccesses))
51 | for i := range fs.GuardedAccesses {
52 | ga := fs.GuardedAccesses[i].ShallowCopy()
53 | ga.PosToRemove++
54 | gas = append(gas, ga)
55 | }
56 | fs.GuardedAccesses = gas
57 | }
58 |
59 | func (fs *FunctionState) Copy() *FunctionState {
60 | newFunctionState := GetFunctionState()
61 | newFunctionState.Lockset = fs.Lockset.Copy()
62 | for _, ga := range fs.GuardedAccesses {
63 | newFunctionState.GuardedAccesses = append(newFunctionState.GuardedAccesses, ga.Copy())
64 | }
65 | return newFunctionState
66 | }
67 |
--------------------------------------------------------------------------------
/domain/GuardedAccess.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/ssaPureUtils"
5 | "go/token"
6 | "golang.org/x/tools/go/ssa"
7 | )
8 |
9 | type OpKind int
10 |
11 | const (
12 | GuardAccessRead OpKind = iota
13 | GuardAccessWrite
14 | )
15 |
16 | func (op OpKind) String() string {
17 | switch op {
18 | case GuardAccessRead:
19 | return "Read"
20 | case GuardAccessWrite:
21 | return "Write"
22 | default:
23 | return "Unknown op type"
24 | }
25 | }
26 |
27 | type GuardedAccess struct {
28 | *PosData
29 | *FlowData
30 | }
31 |
32 | type PosData struct {
33 | PosID int // guarded accesses of the same function share the same PosID. It's used to mark the same guarded access in different flows.
34 | Pos token.Pos
35 | OpKind OpKind
36 | Value ssa.Value
37 | }
38 |
39 | type FlowData struct {
40 | PosToRemove int
41 | ID int // ID depends on the flow, which means it's unique.
42 | State *Context
43 | Lockset *Lockset
44 | }
45 |
46 | func (ga *FlowData) Copy() *FlowData {
47 | return &FlowData{
48 | ID: ga.ID,
49 | PosToRemove: ga.PosToRemove,
50 | Lockset: ga.Lockset.Copy(),
51 | State: ga.State.CopyWithoutMap(),
52 | }
53 | }
54 |
55 | func (ga *GuardedAccess) Copy() *GuardedAccess {
56 | return &GuardedAccess{
57 | PosData: ga.PosData,
58 | FlowData: ga.FlowData.Copy(),
59 | }
60 | }
61 |
62 | func (ga *GuardedAccess) ShallowCopy() *GuardedAccess {
63 | return &GuardedAccess{
64 | PosData: ga.PosData,
65 | FlowData: ga.FlowData.Copy(),
66 | }
67 | }
68 |
69 | func (ga *GuardedAccess) Intersects(gaToCompare *GuardedAccess) bool {
70 | if ga.ID == gaToCompare.ID || ga.State.GoroutineID == gaToCompare.State.GoroutineID {
71 | return true
72 | }
73 | if ga.OpKind == GuardAccessRead && gaToCompare.OpKind == GuardAccessRead {
74 | return true
75 | }
76 |
77 | if ssaPureUtils.FilterStructs(ga.Value, gaToCompare.Value) {
78 | return true
79 | }
80 |
81 | for lockA := range ga.Lockset.Locks {
82 | for lockB := range gaToCompare.Lockset.Locks {
83 | if lockA == lockB {
84 | return true
85 | }
86 | }
87 | }
88 | return false
89 | }
90 |
91 | func (ga *GuardedAccess) IsConflicting(gaToCompare *GuardedAccess) bool {
92 | return !ga.Intersects(gaToCompare) && ga.State.MayConcurrent(gaToCompare.State)
93 | }
94 |
95 | func AddGuardedAccess(pos token.Pos, value ssa.Value, kind OpKind, lockset *Lockset, context *Context) *GuardedAccess {
96 | context.Increment()
97 | return &GuardedAccess{
98 | PosData: &PosData{
99 | PosID: PosIDCounter.GetNext(),
100 | Pos: pos,
101 | OpKind: kind,
102 | Value: value,
103 | },
104 | FlowData: &FlowData{
105 | ID: GuardedAccessCounter.GetNext(),
106 | Lockset: lockset.Copy(),
107 | State: context.CopyWithoutMap(),
108 | },
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/domain/Lockset.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import (
4 | "go/token"
5 |
6 | "golang.org/x/tools/go/ssa"
7 | )
8 |
9 | type locksLastUse map[token.Pos]*ssa.CallCommon
10 |
11 | type Lockset struct {
12 | Locks locksLastUse
13 | Unlocks locksLastUse
14 | }
15 |
16 | func NewLockset() *Lockset {
17 | return &Lockset{
18 | Locks: make(locksLastUse),
19 | Unlocks: make(locksLastUse),
20 | }
21 | }
22 |
23 | // The three following methods handle updating the lockset the same way. By recording each lock state (locked/unlocked)
24 | // at the current point. It means that if a mutex was unlocked at some point but later was locked again, then it's latest
25 | // status is locked, and the unlock status is removed. The difference between each algorithm is the context used.
26 | //
27 | // UpdateWithNewLockSet updates the lockset and expects newLocks, newUnlocks to contain the most up to date status of the
28 | // mutex and update accordingly.
29 | func (ls *Lockset) UpdateWithNewLockSet(newLocks, newUnlocks locksLastUse) {
30 | for lockName, lock := range newLocks {
31 | ls.Locks[lockName] = lock
32 | }
33 | for unlockName := range newUnlocks {
34 | delete(ls.Locks, unlockName)
35 | }
36 | for unlockName, unlock := range newUnlocks {
37 | ls.Unlocks[unlockName] = unlock
38 | }
39 | for lockName := range newLocks {
40 | if _, ok := ls.Locks[lockName]; ok {
41 | delete(ls.Unlocks, lockName)
42 | }
43 | }
44 | }
45 |
46 | // UpdateWithPrevLockset works the same as UpdateWithNewLockSet but expects prevLS to contain an earlier version of the
47 | // status of the locks.
48 | func (ls *Lockset) UpdateWithPrevLockset(prevLS *Lockset) {
49 | for lockName, lock := range prevLS.Locks {
50 | _, okLock := ls.Locks[lockName] // We check to see the lock doesn't exist to not override it with old reference of this lock
51 | _, okUnlock := ls.Unlocks[lockName]
52 | if !okLock && !okUnlock {
53 | ls.Locks[lockName] = lock
54 | }
55 | }
56 |
57 | for lockName, lock := range prevLS.Unlocks {
58 | _, okLock := ls.Locks[lockName] // We check to see the unlock doesn't exist to not override it with old reference of this unlock
59 | _, okUnlock := ls.Unlocks[lockName]
60 | if !okLock && !okUnlock {
61 | ls.Unlocks[lockName] = lock
62 | }
63 | }
64 | }
65 |
66 | // MergeSiblingLockset is called when merging different paths of the control flow graph. The mutex status should be
67 | // merged and not appended. Because Locks is a must set, for a lock to appear in the result, an intersect
68 | // between the branches' lockset is performed to make sure the lock appears in all branches. Unlock is a may set, so a
69 | // union is applied since it's sufficient to have an unlock at least in one of the branches.
70 | func (ls *Lockset) MergeSiblingLockset(locksetToMerge *Lockset) {
71 | ls.Locks.intersect(locksetToMerge.Locks)
72 | ls.Unlocks.union(locksetToMerge.Unlocks)
73 |
74 | for unlockName := range ls.Unlocks {
75 | // If there's a lock in one branch and an unlock in second, then unlock wins
76 | delete(ls.Locks, unlockName)
77 | }
78 | }
79 |
80 | func (ls *Lockset) Copy() *Lockset {
81 | newLs := &Lockset{}
82 | newLocks := make(locksLastUse, len(ls.Locks))
83 | for key, value := range ls.Locks {
84 | newLocks[key] = value
85 | }
86 | newLs.Locks = newLocks
87 | newUnlocks := make(locksLastUse, len(ls.Unlocks))
88 | for key, value := range ls.Unlocks {
89 | newUnlocks[key] = value
90 | }
91 | newLs.Unlocks = newUnlocks
92 | return newLs
93 | }
94 |
95 | func (ls *locksLastUse) intersect(newLS locksLastUse) {
96 | found := false
97 | for a := range *ls {
98 | for b := range newLS {
99 | if a == b {
100 | found = true
101 | }
102 | }
103 | if !found {
104 | delete(*ls, a)
105 | }
106 | }
107 | }
108 |
109 | func (ls *locksLastUse) union(newLS locksLastUse) {
110 | for b := range newLS {
111 | (*ls)[b] = newLS[b]
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/domain/VectorClock.go:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import (
4 | "math"
5 | )
6 |
7 | type VectorClock map[int]int
8 |
9 | func (vc VectorClock) MergeClocks(clockToMerge VectorClock) {
10 | for goroutine, existingTimestamp := range clockToMerge {
11 | newTimestamp := vc.Get(goroutine) // 0 as the default value, or the actual value
12 | vc[goroutine] = int(math.Max(float64(existingTimestamp), float64(newTimestamp)))
13 | }
14 | }
15 |
16 | // Get returns clock for provided goroutine ID.
17 | // If no such goroutine or clock is not initialized,
18 | // then returns a zero value.
19 | func (vc VectorClock) Get(id int) int {
20 | if vc == nil {
21 | return 0
22 | }
23 | var ts, _ = vc[id]
24 | return ts
25 | }
26 |
27 | func (vc VectorClock) Copy() VectorClock {
28 | copiedClock := make(map[int]int, len(vc))
29 | for goroutine, timestamp := range vc {
30 | copiedClock[goroutine] = timestamp
31 | }
32 | return copiedClock
33 | }
34 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/amit-davidson/Chronos
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/stretchr/testify v1.6.1
7 | golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca
8 | )
9 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
6 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
7 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
8 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
9 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
10 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
11 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
12 | golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
13 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
14 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
16 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
17 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
18 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
19 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
20 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
21 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
22 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
23 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
24 | golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca h1:pvScuB+UnCGDas2naNKUOXruM08MjwVcEdaweeynIqQ=
25 | golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
26 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
27 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
28 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
29 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
30 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
31 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
32 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
33 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
34 |
--------------------------------------------------------------------------------
/output/warnings.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "fmt"
5 | "github.com/amit-davidson/Chronos/domain"
6 | "github.com/amit-davidson/Chronos/pointerAnalysis"
7 | "github.com/amit-davidson/Chronos/ssaUtils"
8 | "github.com/amit-davidson/Chronos/utils"
9 | "golang.org/x/tools/go/ssa"
10 | "strings"
11 | "unicode"
12 | )
13 |
14 | const (
15 | spacePrefixCount = 8
16 | )
17 |
18 | func GenerateError(conflictingGAs [][]*domain.GuardedAccess, prog *ssa.Program) error {
19 | if len(conflictingGAs) == 0 {
20 | print("No data races found\n")
21 | return nil
22 | }
23 | filteredDuplicates := pointerAnalysis.FilterDuplicates(conflictingGAs)
24 | messages := make([]string, 0)
25 | for _, conflict := range filteredDuplicates {
26 | label, err := getMessage(conflict[0], conflict[1], prog)
27 | if err != nil {
28 | return err
29 | }
30 | messages = append(messages, label)
31 | }
32 | print(messages[0])
33 | for _, message := range messages[1:] {
34 | print("=========================\n")
35 | print(message)
36 | }
37 |
38 | return nil
39 | }
40 |
41 | func getMessage(guardedAccessA, guardedAccessB *domain.GuardedAccess, prog *ssa.Program) (string, error) {
42 | message := "Potential race condition:\n"
43 | messageA, err := getMessageByLine(guardedAccessA, prog)
44 | if err != nil {
45 | return "", err
46 | }
47 | messageB, err := getMessageByLine(guardedAccessB, prog)
48 | if err != nil {
49 | return "", err
50 | }
51 | message += fmt.Sprintf(" %s:\n%s\n \n %s:\n%s \n", "Access1", messageA, "Access2", messageB)
52 | return message, nil
53 | }
54 |
55 | func getMessageByLine(guardedAccessA *domain.GuardedAccess, prog *ssa.Program) (string, error) {
56 | message := ""
57 | posA := prog.Fset.Position(guardedAccessA.Pos)
58 | lineA, err := utils.ReadLineByNumber(posA.Filename, posA.Line)
59 | trimmedA := strings.TrimLeftFunc(lineA, unicode.IsSpace)
60 | message += strings.Repeat(" ", spacePrefixCount) + trimmedA
61 |
62 | removedSpaces := len(lineA) - len(trimmedA)
63 | posToAddArrow := posA.Column - removedSpaces
64 | message += "\n" + strings.Repeat(" ", posToAddArrow+spacePrefixCount-1) + "^" + "\n"
65 | stackA := ssaUtils.GetStackTrace(prog, guardedAccessA)
66 | if err != nil {
67 | return "", err
68 | }
69 | stackA += posA.String()
70 | message += stackA
71 | return message, nil
72 | }
73 |
--------------------------------------------------------------------------------
/pointerAnalysis/PointerAnalysis.go:
--------------------------------------------------------------------------------
1 | package pointerAnalysis
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/domain"
5 | "github.com/amit-davidson/Chronos/utils"
6 | "go/token"
7 | "golang.org/x/tools/go/pointer"
8 | "golang.org/x/tools/go/ssa"
9 | )
10 |
11 |
12 | // Analysis starts by mapping between positions of the guard accesses (values inside) to the guard accesses themselves.
13 | // Then it analyzes all the values inside values inside, and check if some of the values might alias each other. If so,
14 | // the positions for those values are merged. After all positions were merged, the algorithm runs and check if for a
15 | // given value (identified by a pos in the map) there are two guarded accesses that might conflict - W&W/R&W from two
16 | // different goroutines.
17 | // map1 : A->ga1, ga2, ga3
18 | // B->ga4, ga5, ga6
19 | // C->ga7, ga8, ga9
20 | // D->ga10, ga11, ga12
21 | // Now that we know that B may point to A, we add it to it
22 | // map1 : A->ga1, ga2, ga3, ga4, ga5, ga6
23 | // C->ga7, ga8, ga9
24 | // D->ga10, ga11, ga12
25 | // And if A may point to D, then
26 | // map1 : C->ga7, ga8, ga9
27 | // D->ga10, ga11, ga12, ga1, ga2, ga3, ga4, ga5, ga6
28 | // And then for pos all the guarded accesses are compared to see if data races might exist
29 |
30 | func Analysis(pkg *ssa.Package, accesses []*domain.GuardedAccess) ([][]*domain.GuardedAccess, error) {
31 | config := &pointer.Config{
32 | Mains: []*ssa.Package{pkg},
33 | }
34 |
35 | positionsToGuardAccesses := map[token.Pos][]*domain.GuardedAccess{}
36 | for _, guardedAccess := range accesses {
37 | if guardedAccess.Pos.IsValid() && pointer.CanPoint(guardedAccess.Value.Type()) {
38 | config.AddQuery(guardedAccess.Value)
39 | // Multiple instructions for the same variable for example write and multiple reads
40 | positionsToGuardAccesses[guardedAccess.Value.Pos()] = append(positionsToGuardAccesses[guardedAccess.Value.Pos()], guardedAccess)
41 | }
42 | }
43 |
44 | result, err := pointer.Analyze(config)
45 | if err != nil {
46 | return nil, err // internal error in pointer analysis
47 | }
48 |
49 | // Join instructions of variables that may point to each other.
50 | for v, l := range result.Queries {
51 | for _, label := range l.PointsTo().Labels() {
52 | allocPos := label.Value().Pos()
53 | queryPos := v.Pos()
54 | if allocPos == queryPos {
55 | continue
56 | }
57 | positionsToGuardAccesses[allocPos] = append(positionsToGuardAccesses[allocPos], positionsToGuardAccesses[queryPos]...)
58 | }
59 | }
60 | conflictingGA := make([][]*domain.GuardedAccess, 0)
61 | for _, guardedAccesses := range positionsToGuardAccesses {
62 | for _, guardedAccessA := range guardedAccesses {
63 | for _, guardedAccessB := range guardedAccesses {
64 | if guardedAccessA.IsConflicting(guardedAccessB) {
65 | conflictingGA = append(conflictingGA, []*domain.GuardedAccess{guardedAccessA, guardedAccessB})
66 | }
67 | }
68 | }
69 | }
70 | return conflictingGA, nil
71 | }
72 |
73 | func FilterDuplicates(conflictingGAs [][]*domain.GuardedAccess) [][]*domain.GuardedAccess {
74 | foundDataRaces := utils.NewDoubleKeyMap() // To avoid reporting on the same pair of positions more then once. Can happen if for the same place we read and then write.
75 | nonDuplicatesGAs := make([][]*domain.GuardedAccess, 0)
76 | for _, conflict := range conflictingGAs {
77 | isExist := foundDataRaces.IsExist(conflict[0].Pos, conflict[1].Pos)
78 | if !isExist {
79 | foundDataRaces.Add(conflict[0].Pos, conflict[1].Pos)
80 | nonDuplicatesGAs = append(nonDuplicatesGAs, conflict)
81 | }
82 | }
83 | return nonDuplicatesGAs
84 | }
85 |
--------------------------------------------------------------------------------
/ssaPureUtils/General.go:
--------------------------------------------------------------------------------
1 | package ssaPureUtils
2 |
3 | import (
4 | "go/token"
5 | "golang.org/x/tools/go/ssa"
6 | )
7 |
8 |
9 | func GetMutexPos(value ssa.Value) token.Pos {
10 | val, ok := GetField(value)
11 | if !ok {
12 | return value.Pos()
13 | }
14 | obj := GetUnderlyingObjectFromField(val)
15 | return obj.Pos()
16 | }
--------------------------------------------------------------------------------
/ssaPureUtils/Locks.go:
--------------------------------------------------------------------------------
1 | package ssaPureUtils
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/utils"
5 | "golang.org/x/tools/go/ssa"
6 | )
7 |
8 | func IsLock(call *ssa.Function) bool {
9 | return utils.IsCallTo(call, "(*sync.Mutex).Lock")
10 | }
11 |
12 |
13 | func IsUnlock(call *ssa.Function) bool {
14 | return utils.IsCallTo(call, "(*sync.Mutex).Unlock")
15 | }
16 |
--------------------------------------------------------------------------------
/ssaPureUtils/Structs.go:
--------------------------------------------------------------------------------
1 | package ssaPureUtils
2 |
3 | import (
4 | "go/types"
5 | "golang.org/x/tools/go/ssa"
6 | )
7 |
8 | func GetField(value ssa.Value) (*ssa.FieldAddr, bool) {
9 | fieldAddr, ok := value.(*ssa.FieldAddr)
10 | return fieldAddr, ok
11 | }
12 |
13 | func GetUnderlyingObjectFromField(fieldAddr *ssa.FieldAddr) *types.Var {
14 | return fieldAddr.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(fieldAddr.Field)
15 | }
16 |
17 | func FilterStructs(valueA, valueB ssa.Value) bool {
18 | fieldAddrA, okA := GetField(valueA)
19 | fieldAddrB, okB := GetField(valueB)
20 |
21 | isBothField := okA && okB
22 | if isBothField {
23 | fieldA := GetUnderlyingObjectFromField(fieldAddrA)
24 | fieldB := GetUnderlyingObjectFromField(fieldAddrB)
25 | if fieldA != fieldB { // If same struct but different fields
26 | return true
27 | }
28 | }
29 |
30 | return false
31 | }
32 |
--------------------------------------------------------------------------------
/ssaUtils/CFG.go:
--------------------------------------------------------------------------------
1 | package ssaUtils
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/domain"
5 | "github.com/amit-davidson/Chronos/utils/stacks"
6 | "golang.org/x/tools/go/ssa"
7 | )
8 |
9 | type CFG struct {
10 | visitedBlocksStack *stacks.BlockMap
11 |
12 | ComputedBlocks map[int]*domain.BlockState
13 | ComputedDeferBlocks map[int]*domain.BlockState
14 | }
15 |
16 | func newCFG() *CFG {
17 | return &CFG{
18 | visitedBlocksStack: stacks.NewBlockMap(),
19 | ComputedBlocks: make(map[int]*domain.BlockState),
20 | ComputedDeferBlocks: make(map[int]*domain.BlockState),
21 | }
22 | }
23 |
24 | // CalculateFunctionState works by traversing the tree in a DFS way, similar to the flow of the function when it'll run.
25 | // It calculates the state of the regular flow of block first, then adds the state of any succeeding blocks in the tree,
26 | // and finally the block's defer state if exist.
27 | // The function uses two way to aggregate the states between blocks. If the blocks are adjacent (siblings) to each
28 | // other, (resulted from a branch) then a merge mechanism is used. If one block is below the other, then an append is
29 | // performed.
30 | func (cfg *CFG) CalculateFunctionState(context *domain.Context, block *ssa.BasicBlock) *domain.BlockState {
31 | cfg.visitedBlocksStack.Add(block)
32 | defer cfg.visitedBlocksStack.Remove(block)
33 | cfg.calculateBlockState(context, block)
34 |
35 | // Regular flow
36 | blockState := cfg.ComputedBlocks[block.Index]
37 |
38 | // recursion
39 | var branchState *domain.BlockState
40 | for _, nextBlock := range block.Succs {
41 | // if it's a cycle we skip it
42 | if cfg.visitedBlocksStack.Contains(nextBlock.Index) {
43 | continue
44 | }
45 |
46 | retBlockState := cfg.CalculateFunctionState(context, nextBlock)
47 | if branchState == nil {
48 | branchState = retBlockState.Copy()
49 | } else {
50 | branchState.MergeSiblingBlock(retBlockState)
51 | }
52 | }
53 |
54 | if branchState != nil {
55 | blockState.MergeChildBlock(branchState)
56 | }
57 |
58 | // Defer
59 | if deferState, ok := cfg.ComputedDeferBlocks[block.Index]; ok {
60 | blockState.MergeChildBlock(deferState)
61 | }
62 | return blockState
63 | }
64 |
65 | func (cfg *CFG) calculateBlockState(context *domain.Context, block *ssa.BasicBlock) {
66 | if _, ok := cfg.ComputedBlocks[block.Index]; !ok {
67 | cfg.ComputedBlocks[block.Index] = GetBlockSummary(context, block)
68 | deferedFunctions := cfg.ComputedBlocks[block.Index].DeferredFunctions
69 | if deferedFunctions.Len() > 0 {
70 | cfg.ComputedDeferBlocks[block.Index] = cfg.runDefers(context, deferedFunctions)
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/ssaUtils/Functions.go:
--------------------------------------------------------------------------------
1 | package ssaUtils
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/domain"
5 | "github.com/amit-davidson/Chronos/ssaPureUtils"
6 | "github.com/amit-davidson/Chronos/utils/stacks"
7 | "go/token"
8 | "go/types"
9 | "golang.org/x/tools/go/ssa"
10 | "strings"
11 | )
12 |
13 | var functionsCache = make(map[*types.Signature]*domain.FunctionState)
14 |
15 | func HandleCallCommon(context *domain.Context, callCommon *ssa.CallCommon, pos token.Pos) *domain.BlockState {
16 | funcState := domain.GetEmptyBlockState()
17 |
18 | // if we already visited this path, it means we're (probably) in a recursion so we return to avoid infinite loop
19 | if context.StackTrace.Contains(int(pos)) {
20 | return funcState
21 | }
22 |
23 | context.StackTrace.Push(int(pos))
24 | defer context.StackTrace.Pop()
25 |
26 | if callCommon.IsInvoke() {
27 | impls := GetMethodImplementations(callCommon.Value.Type().Underlying(), callCommon.Method)
28 | if len(impls) > 0 {
29 | funcState = HandleFunction(context, impls[0])
30 | for _, impl := range impls[1:] {
31 | funcstateRet := HandleFunction(context, impl)
32 | funcState.MergeSiblingBlock(funcstateRet)
33 | }
34 | }
35 | return funcState
36 | }
37 |
38 | switch call := callCommon.Value.(type) {
39 | case *ssa.Builtin:
40 | HandleBuiltin(funcState, context, callCommon)
41 | return funcState
42 | case *ssa.MakeClosure:
43 | fn := callCommon.Value.(*ssa.MakeClosure).Fn.(*ssa.Function)
44 | funcStateRet := HandleFunction(context, fn)
45 | return funcStateRet
46 | case *ssa.Function:
47 | if ssaPureUtils.IsLock(call) {
48 | AddLock(funcState, callCommon, false)
49 | return funcState
50 | }
51 | if ssaPureUtils.IsUnlock(call) {
52 | AddLock(funcState, callCommon, true)
53 | return funcState
54 | }
55 |
56 | var blockStateRet *domain.BlockState
57 | sig := callCommon.Signature()
58 | if cachedFunctionState, ok := functionsCache[sig]; ok {
59 | copiedState := cachedFunctionState.Copy() // Copy to avoid override cached item
60 | copiedState.AddContextToFunction(context)
61 | blockStateRet = domain.CreateBlockState(copiedState.GuardedAccesses, copiedState.Lockset, stacks.NewCallCommonStack())
62 | } else {
63 | blockStateRet = HandleFunction(context, call)
64 | fs := domain.CreateFunctionState(blockStateRet.GuardedAccesses, blockStateRet.Lockset)
65 | fs.RemoveContextFromFunction()
66 | functionsCache[sig] = fs
67 | }
68 | return blockStateRet
69 |
70 | case ssa.Instruction:
71 | HandleInstruction(funcState, context, call)
72 | return funcState
73 | }
74 | return funcState
75 | }
76 |
77 | func HandleBuiltin(functionState *domain.BlockState, context *domain.Context, call *ssa.CallCommon) {
78 | callCommon := call.Value.(*ssa.Builtin)
79 | args := call.Args
80 | switch name := callCommon.Name(); name {
81 | case "delete":
82 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessWrite, functionState.Lockset, context))
83 | case "cap", "len":
84 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessRead, functionState.Lockset, context))
85 | case "append":
86 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[1], domain.GuardAccessRead, functionState.Lockset, context))
87 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessWrite, functionState.Lockset, context))
88 | case "copy":
89 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[0], domain.GuardAccessRead, functionState.Lockset, context))
90 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, domain.AddGuardedAccess(call.Pos(), args[1], domain.GuardAccessWrite, functionState.Lockset, context))
91 | }
92 | }
93 |
94 | func HandleInstruction(functionState *domain.BlockState, context *domain.Context, ins ssa.Instruction) {
95 | switch call := ins.(type) {
96 | case *ssa.UnOp:
97 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
98 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
99 | case *ssa.Field:
100 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call, domain.GuardAccessRead, functionState.Lockset, context)
101 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
102 | case *ssa.FieldAddr:
103 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call, domain.GuardAccessRead, functionState.Lockset, context)
104 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
105 | case *ssa.Index:
106 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
107 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
108 | case *ssa.IndexAddr:
109 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
110 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
111 | case *ssa.Lookup:
112 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
113 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
114 | case *ssa.Panic:
115 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
116 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
117 | case *ssa.Range:
118 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
119 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
120 | case *ssa.TypeAssert:
121 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
122 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
123 | case *ssa.BinOp:
124 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.X, domain.GuardAccessRead, functionState.Lockset, context)
125 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
126 | guardedAccess = domain.AddGuardedAccess(call.Pos(), call.Y, domain.GuardAccessRead, functionState.Lockset, context)
127 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
128 | case *ssa.If:
129 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.Cond, domain.GuardAccessRead, functionState.Lockset, context)
130 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
131 | case *ssa.MapUpdate:
132 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.Map, domain.GuardAccessWrite, functionState.Lockset, context)
133 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
134 | guardedAccess = domain.AddGuardedAccess(call.Pos(), call.Value, domain.GuardAccessRead, functionState.Lockset, context)
135 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
136 | case *ssa.Store:
137 | guardedAccess := domain.AddGuardedAccess(call.Pos(), call.Val, domain.GuardAccessRead, functionState.Lockset, context)
138 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
139 | guardedAccess = domain.AddGuardedAccess(call.Pos(), call.Addr, domain.GuardAccessWrite, functionState.Lockset, context)
140 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
141 | case *ssa.Return:
142 | for _, retValue := range call.Results {
143 | guardedAccess := domain.AddGuardedAccess(call.Pos(), retValue, domain.GuardAccessRead, functionState.Lockset, context)
144 | functionState.GuardedAccesses = append(functionState.GuardedAccesses, guardedAccess)
145 | }
146 | }
147 | }
148 |
149 | func GetBlockSummary(context *domain.Context, block *ssa.BasicBlock) *domain.BlockState {
150 | funcState := domain.GetEmptyBlockState()
151 | for _, ins := range block.Instrs {
152 | switch call := ins.(type) {
153 | case *ssa.Call:
154 | callCommon := call.Common()
155 | funcStateRet := HandleCallCommon(context, callCommon, callCommon.Pos())
156 | funcState.AddFunctionCallState(funcStateRet, true)
157 | case *ssa.Go:
158 | callCommon := call.Common()
159 | newState := domain.NewGoroutineExecutionState(context)
160 | funcStateRet := HandleCallCommon(newState, callCommon, callCommon.Pos())
161 | funcState.AddFunctionCallState(funcStateRet, false)
162 | case *ssa.Defer:
163 | callCommon := call.Common()
164 | funcState.DeferredFunctions.Push(callCommon)
165 | default:
166 | HandleInstruction(funcState, context, ins)
167 | }
168 | }
169 | return funcState
170 | }
171 |
172 | func (cfg *CFG) runDefers(context *domain.Context, defers *stacks.CallCommonStack) *domain.BlockState {
173 | calculatedState := domain.GetEmptyBlockState()
174 | defersItems := defers.GetItems()
175 | for i := len(defersItems) - 1; i >= 0; i-- {
176 | deferFunction := defersItems[i]
177 | if deferFunction == nil {
178 | break
179 | }
180 | retState := HandleCallCommon(context, deferFunction, deferFunction.Pos())
181 | calculatedState.MergeChildBlock(retState)
182 | }
183 | return calculatedState
184 |
185 | }
186 |
187 | func HandleFunction(context *domain.Context, fn *ssa.Function) *domain.BlockState {
188 | funcState := domain.GetEmptyBlockState()
189 | if fn.Pkg == nil {
190 | return funcState
191 | }
192 | pkgName := fn.Pkg.Pkg.Path() // Used to guard against entering standard library packages
193 | if !strings.Contains(pkgName, GlobalModuleName) {
194 | return funcState
195 | }
196 |
197 | // regular
198 | if fn.Blocks == nil { // External function
199 | return funcState
200 | }
201 | cfg := newCFG()
202 | calculatedState := cfg.CalculateFunctionState(context, fn.Blocks[0])
203 | return calculatedState
204 | }
205 |
--------------------------------------------------------------------------------
/ssaUtils/Functions_test.go:
--------------------------------------------------------------------------------
1 | package ssaUtils
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/amit-davidson/Chronos/domain"
7 | "github.com/amit-davidson/Chronos/pointerAnalysis"
8 | "github.com/stretchr/testify/assert"
9 | "github.com/stretchr/testify/require"
10 | "golang.org/x/tools/go/ssa"
11 | )
12 |
13 | func Test_HandleFunction_DeferredLockAndUnlockIfBranch(t *testing.T) {
14 | f, _ := LoadMain(t, "./testdata/Functions/Defer/DeferredLockAndUnlockIfBranch/prog1.go")
15 | ctx := domain.NewEmptyContext()
16 | state := HandleFunction(ctx, f)
17 | assert.Len(t, state.Lockset.Locks, 0)
18 | assert.Len(t, state.Lockset.Unlocks, 1)
19 |
20 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
21 | if !IsGARead(ga) {
22 | return false
23 | }
24 | val, ok := ga.Value.(*ssa.Const)
25 | if !ok {
26 | return false
27 | }
28 | if val.Int64() != 5 {
29 | return false
30 | }
31 | return true
32 | })
33 | assert.Len(t, foundGA.Lockset.Locks, 0)
34 |
35 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
36 | if !IsGARead(ga) {
37 | return false
38 | }
39 | val, ok := ga.Value.(*ssa.Const)
40 | if !ok {
41 | return false
42 | }
43 | if val.Int64() != 6 {
44 | return false
45 | }
46 | return true
47 | })
48 | assert.Len(t, foundGA.Lockset.Locks, 0)
49 | }
50 |
51 | func Test_HandleFunction_NestedDeferWithLockAndUnlock(t *testing.T) {
52 | f, _ := LoadMain(t, "./testdata/Functions/Defer/NestedDeferWithLockAndUnlock/prog1.go")
53 | ctx := domain.NewEmptyContext()
54 | state := HandleFunction(ctx, f)
55 | assert.Len(t, state.Lockset.Locks, 1)
56 | assert.Len(t, state.Lockset.Unlocks, 0)
57 |
58 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
59 | if !IsGARead(ga) {
60 | return false
61 | }
62 | val, ok := ga.Value.(*ssa.Const)
63 | if !ok {
64 | return false
65 | }
66 | if val.Int64() != 6 {
67 | return false
68 | }
69 | return true
70 | })
71 | assert.Len(t, foundGA.Lockset.Locks, 1)
72 | }
73 |
74 | func Test_HandleFunction_NestedDeferWithLockAndUnlockAndGoroutine(t *testing.T) {
75 | f, _ := LoadMain(t, "./testdata/Functions/Defer/NestedDeferWithLockAndUnlockAndGoroutine/prog1.go")
76 | ctx := domain.NewEmptyContext()
77 | state := HandleFunction(ctx, f)
78 | assert.Len(t, state.Lockset.Locks, 0)
79 | assert.Len(t, state.Lockset.Unlocks, 1)
80 |
81 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
82 | if !IsGARead(ga) {
83 | return false
84 | }
85 | val, ok := ga.Value.(*ssa.Const)
86 | if !ok {
87 | return false
88 | }
89 | if val.Int64() != 6 {
90 | return false
91 | }
92 | return true
93 | })
94 | assert.Len(t, foundGA.Lockset.Locks, 1)
95 |
96 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
97 | if !IsGARead(ga) {
98 | return false
99 | }
100 | val, ok := ga.Value.(*ssa.Const)
101 | if !ok {
102 | return false
103 | }
104 | if val.Int64() != 7 {
105 | return false
106 | }
107 | return true
108 | })
109 | assert.Len(t, foundGA.Lockset.Locks, 1)
110 | }
111 |
112 | func Test_HandleFunction_ForLoopLockInsideLoop(t *testing.T) {
113 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/ForLoopLockInsideLoop/prog1.go")
114 | ctx := domain.NewEmptyContext()
115 | state := HandleFunction(ctx, f)
116 | assert.Len(t, state.Lockset.Locks, 0)
117 |
118 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
119 | if !IsGARead(ga) {
120 | return false
121 | }
122 | val, ok := ga.Value.(*ssa.Const)
123 | if !ok {
124 | return false
125 | }
126 | if GetConstString(val) != "b" {
127 | return false
128 | }
129 | return true
130 | })
131 | assert.Len(t, foundGA.Lockset.Locks, 0)
132 | assert.Len(t, foundGA.Lockset.Unlocks, 0)
133 |
134 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
135 | if !IsGARead(ga) {
136 | return false
137 | }
138 | val, ok := ga.Value.(*ssa.Const)
139 | if !ok {
140 | return false
141 | }
142 | if GetConstString(val) != "c" {
143 | return false
144 | }
145 | return true
146 | })
147 | assert.Len(t, foundGA.Lockset.Locks, 1)
148 | assert.Len(t, foundGA.Lockset.Unlocks, 0)
149 | }
150 |
151 | func Test_HandleFunction_ForLoopLockOutsideLoop(t *testing.T) {
152 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/ForLoopLockOutsideLoop/prog1.go")
153 | ctx := domain.NewEmptyContext()
154 | state := HandleFunction(ctx, f)
155 | assert.Len(t, state.Lockset.Locks, 1)
156 |
157 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
158 | if !IsGARead(ga) {
159 | return false
160 | }
161 | val, ok := ga.Value.(*ssa.Const)
162 | if !ok {
163 | return false
164 | }
165 | if GetConstString(val) != "b" {
166 | return false
167 | }
168 | return true
169 | })
170 | assert.Len(t, foundGA.Lockset.Locks, 0)
171 | assert.Len(t, foundGA.Lockset.Unlocks, 0)
172 |
173 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
174 | if !IsGARead(ga) {
175 | return false
176 | }
177 | val, ok := ga.Value.(*ssa.Const)
178 | if !ok {
179 | return false
180 | }
181 | if GetConstString(val) != "c" {
182 | return false
183 | }
184 | return true
185 | })
186 | assert.Len(t, foundGA.Lockset.Locks, 1)
187 | assert.Len(t, foundGA.Lockset.Unlocks, 0)
188 | }
189 |
190 | func Test_HandleFunction_NestedForLoopWithRace(t *testing.T) {
191 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/NestedForLoopWithRace/prog1.go")
192 | ctx := domain.NewEmptyContext()
193 | state := HandleFunction(ctx, f)
194 | assert.Len(t, state.Lockset.Locks, 0)
195 | assert.Len(t, state.Lockset.Unlocks, 0)
196 |
197 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
198 | if !IsGARead(ga) {
199 | return false
200 | }
201 | val, ok := ga.Value.(*ssa.Const)
202 | if !ok {
203 | return false
204 | }
205 | if GetConstString(val) != "a" {
206 | return false
207 | }
208 | return true
209 | })
210 | stateA := ga.State
211 | assert.Len(t, ga.Lockset.Locks, 0)
212 | assert.Len(t, ga.Lockset.Unlocks, 0)
213 |
214 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
215 | if !IsGARead(ga) {
216 | return false
217 | }
218 | val, ok := ga.Value.(*ssa.Const)
219 | if !ok {
220 | return false
221 | }
222 | return GetConstString(val) == "b"
223 | })
224 | stateB := ga.State
225 | assert.Len(t, ga.Lockset.Locks, 0)
226 | assert.Len(t, ga.Lockset.Unlocks, 0)
227 | assert.True(t, stateA.MayConcurrent(stateB))
228 |
229 | }
230 |
231 | func Test_HandleFunction_WhileLoop(t *testing.T) {
232 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/WhileLoop/prog1.go")
233 | ctx := domain.NewEmptyContext()
234 | state := HandleFunction(ctx, f)
235 | assert.Len(t, state.Lockset.Locks, 0)
236 | assert.Len(t, state.Lockset.Unlocks, 0)
237 |
238 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
239 | if !IsGARead(ga) {
240 | return false
241 | }
242 | _, ok := ga.Value.(*ssa.Alloc)
243 | if !ok {
244 | return false
245 | }
246 | pName := ga.Value.Parent().Name()
247 | return pName != "main"
248 | })
249 | stateA := ga.State
250 | assert.Len(t, ga.Lockset.Locks, 0)
251 | assert.Len(t, ga.Lockset.Unlocks, 0)
252 |
253 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
254 | if !IsGARead(ga) {
255 | return false
256 | }
257 | val, ok := ga.Value.(*ssa.FreeVar)
258 | if !ok {
259 | return false
260 | }
261 | return val.Name() == "x"
262 | })
263 | stateB := ga.State
264 | assert.Len(t, ga.Lockset.Locks, 0)
265 | assert.Len(t, ga.Lockset.Unlocks, 0)
266 | assert.True(t, stateA.MayConcurrent(stateB))
267 |
268 | }
269 |
270 | func Test_HandleFunction_WhileLoopWithoutHeader(t *testing.T) {
271 | f, _ := LoadMain(t, "./testdata/Functions/ForLoops/WhileLoopWithoutHeader/prog1.go")
272 | ctx := domain.NewEmptyContext()
273 | state := HandleFunction(ctx, f)
274 | assert.Len(t, state.Lockset.Locks, 0)
275 | assert.Len(t, state.Lockset.Unlocks, 0)
276 |
277 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
278 | if !IsGARead(ga) {
279 | return false
280 | }
281 | _, ok := ga.Value.(*ssa.Alloc)
282 | if !ok {
283 | return false
284 | }
285 | pName := ga.Value.Parent().Name()
286 | return pName != "main"
287 | })
288 | stateA := ga.State
289 | assert.Len(t, ga.Lockset.Locks, 1)
290 | assert.Len(t, ga.Lockset.Unlocks, 0)
291 |
292 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
293 | if !IsGARead(ga) {
294 | return false
295 | }
296 | val, ok := ga.Value.(*ssa.FreeVar)
297 | if !ok {
298 | return false
299 | }
300 | if val.Name() != "x" {
301 | return false
302 | }
303 | return true
304 | })
305 | stateB := ga.State
306 | assert.Len(t, ga.Lockset.Locks, 0)
307 | assert.Len(t, ga.Lockset.Unlocks, 0)
308 | assert.True(t, stateA.MayConcurrent(stateB))
309 | }
310 |
311 | func Test_HandleFunction_DataRaceIceCreamMaker(t *testing.T) {
312 | f, _ := LoadMain(t, "./testdata/Functions/Interfaces/DataRaceIceCreamMaker/prog1.go")
313 | ctx := domain.NewEmptyContext()
314 | state := HandleFunction(ctx, f)
315 | assert.Len(t, state.Lockset.Locks, 0)
316 | assert.Len(t, state.Lockset.Unlocks, 0)
317 |
318 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
319 | if !IsGARead(ga) {
320 | return false
321 | }
322 | val, ok := ga.Value.(*ssa.Const)
323 | if !ok {
324 | return false
325 | }
326 | if GetConstString(val) != "Ben" {
327 | return false
328 | }
329 | return true
330 | })
331 | stateA := ga.State
332 | assert.Len(t, ga.Lockset.Locks, 0)
333 | assert.Len(t, ga.Lockset.Unlocks, 0)
334 |
335 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
336 | if !IsGARead(ga) {
337 | return false
338 | }
339 | val, ok := ga.Value.(*ssa.Const)
340 | if !ok {
341 | return false
342 | }
343 | if GetConstString(val) != "1" {
344 | return false
345 | }
346 | return true
347 | })
348 | stateB := ga.State
349 | assert.Len(t, ga.Lockset.Locks, 0)
350 | assert.Len(t, ga.Lockset.Unlocks, 0)
351 | assert.True(t, stateA.MayConcurrent(stateB))
352 | }
353 |
354 | func Test_HandleFunction_InterfaceWithLock(t *testing.T) {
355 | f, _ := LoadMain(t, "./testdata/Functions/Interfaces/InterfaceWithLock/prog1.go")
356 | ctx := domain.NewEmptyContext()
357 | state := HandleFunction(ctx, f)
358 | assert.Len(t, state.Lockset.Locks, 1)
359 | assert.Len(t, state.Lockset.Unlocks, 0)
360 |
361 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
362 | if !IsGARead(ga) {
363 | return false
364 | }
365 | val, ok := ga.Value.(*ssa.Const)
366 | if !ok {
367 | return false
368 | }
369 | if GetConstString(val) != "Ben" {
370 | return false
371 | }
372 | return true
373 | })
374 | assert.Len(t, ga.Lockset.Locks, 1)
375 | assert.Len(t, ga.Lockset.Unlocks, 0)
376 |
377 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
378 | if !IsGARead(ga) {
379 | return false
380 | }
381 | val, ok := ga.Value.(*ssa.Const)
382 | if !ok {
383 | return false
384 | }
385 | if GetConstString(val) != "Jerry" {
386 | return false
387 | }
388 | return true
389 | })
390 | assert.Len(t, ga.Lockset.Locks, 1)
391 | assert.Len(t, ga.Lockset.Unlocks, 0)
392 |
393 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
394 | if !IsGARead(ga) {
395 | return false
396 | }
397 | val, ok := ga.Value.(*ssa.Const)
398 | if !ok {
399 | return false
400 | }
401 | if GetConstString(val) != "1" {
402 | return false
403 | }
404 | return true
405 | })
406 | assert.Len(t, ga.Lockset.Locks, 1)
407 | assert.Len(t, ga.Lockset.Unlocks, 0)
408 | }
409 |
410 | func Test_HandleFunction_NestedInterface(t *testing.T) {
411 | f, _ := LoadMain(t, "./testdata/Functions/Interfaces/NestedInterface/prog1.go")
412 | ctx := domain.NewEmptyContext()
413 | state := HandleFunction(ctx, f)
414 | assert.Len(t, state.Lockset.Locks, 0)
415 | assert.Len(t, state.Lockset.Unlocks, 0)
416 |
417 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
418 | if !IsGARead(ga) {
419 | return false
420 | }
421 | val, ok := ga.Value.(*ssa.Const)
422 | if !ok {
423 | return false
424 | }
425 | if GetConstString(val) != "Jerry" {
426 | return false
427 | }
428 | return true
429 | })
430 | assert.Len(t, ga.Lockset.Locks, 0)
431 | assert.Len(t, ga.Lockset.Unlocks, 0)
432 | }
433 |
434 | func Test_HandleFunction_Lock(t *testing.T) {
435 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/Lock/prog1.go")
436 | ctx := domain.NewEmptyContext()
437 | state := HandleFunction(ctx, f)
438 | assert.Len(t, state.Lockset.Locks, 1)
439 | assert.Len(t, state.Lockset.Unlocks, 0)
440 |
441 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
442 | if !IsGARead(ga) {
443 | return false
444 | }
445 | val, ok := ga.Value.(*ssa.Const)
446 | if !ok {
447 | return false
448 | }
449 | if val.Int64() != 5 {
450 | return false
451 | }
452 | return true
453 | })
454 | assert.Len(t, ga.Lockset.Locks, 1)
455 | assert.Len(t, ga.Lockset.Unlocks, 0)
456 | }
457 |
458 | func Test_HandleFunction_LockAndUnlock(t *testing.T) {
459 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockAndUnlock/prog1.go")
460 | ctx := domain.NewEmptyContext()
461 | state := HandleFunction(ctx, f)
462 | assert.Len(t, state.Lockset.Locks, 0)
463 | assert.Len(t, state.Lockset.Unlocks, 1)
464 |
465 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
466 | if !IsGARead(ga) {
467 | return false
468 | }
469 | val, ok := ga.Value.(*ssa.Const)
470 | if !ok {
471 | return false
472 | }
473 | if val.Int64() != 5 {
474 | return false
475 | }
476 | return true
477 | })
478 | assert.Len(t, ga.Lockset.Locks, 0)
479 | assert.Len(t, ga.Lockset.Unlocks, 1)
480 | }
481 |
482 | func Test_HandleFunction_LockAndUnlockIfBranch(t *testing.T) {
483 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockAndUnlockIfBranch/prog1.go")
484 | ctx := domain.NewEmptyContext()
485 | state := HandleFunction(ctx, f)
486 | assert.Len(t, state.Lockset.Locks, 0)
487 | assert.Len(t, state.Lockset.Unlocks, 1)
488 |
489 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
490 | if !IsGARead(ga) {
491 | return false
492 | }
493 | val, ok := ga.Value.(*ssa.Const)
494 | if !ok {
495 | return false
496 | }
497 | if val.Int64() != 5 {
498 | return false
499 | }
500 | return true
501 | })
502 | assert.Len(t, foundGA.Lockset.Locks, 0)
503 |
504 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
505 | if !IsGARead(ga) {
506 | return false
507 | }
508 | val, ok := ga.Value.(*ssa.Const)
509 | if !ok {
510 | return false
511 | }
512 | if val.Int64() != 6 {
513 | return false
514 | }
515 | return true
516 | })
517 | assert.Len(t, foundGA.Lockset.Locks, 0)
518 | }
519 |
520 | func Test_HandleFunction_LockInBothBranches(t *testing.T) {
521 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockInBothBranches/prog1.go")
522 | ctx := domain.NewEmptyContext()
523 | state := HandleFunction(ctx, f)
524 | assert.Len(t, state.Lockset.Locks, 1)
525 | assert.Len(t, state.Lockset.Unlocks, 0)
526 |
527 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
528 | if !IsGARead(ga) {
529 | return false
530 | }
531 | val, ok := ga.Value.(*ssa.Const)
532 | if !ok {
533 | return false
534 | }
535 | if val.Int64() != 5 {
536 | return false
537 | }
538 | return true
539 | })
540 | assert.Len(t, foundGA.Lockset.Locks, 1)
541 | }
542 |
543 | func Test_HandleFunction_LockInsideGoroutine(t *testing.T) {
544 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/LockInsideGoroutine/prog1.go")
545 | ctx := domain.NewEmptyContext()
546 | state := HandleFunction(ctx, f)
547 | assert.Len(t, state.Lockset.Locks, 0)
548 | assert.Len(t, state.Lockset.Unlocks, 0)
549 |
550 | foundGA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
551 | if !IsGARead(ga) {
552 | return false
553 | }
554 | _, ok := ga.Value.(*ssa.MakeInterface)
555 | if !ok {
556 | return false
557 | }
558 | pName := ga.Value.Parent().Name()
559 | return pName != "main"
560 | })
561 | assert.Len(t, foundGA.Lockset.Locks, 1)
562 |
563 | foundGA = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
564 | if !IsGARead(ga) {
565 | return false
566 | }
567 | _, ok := ga.Value.(*ssa.MakeInterface)
568 | if !ok {
569 | return false
570 | }
571 | pName := ga.Value.Parent().Name()
572 | return pName == "main"
573 |
574 | })
575 | assert.Len(t, foundGA.Lockset.Locks, 0)
576 | }
577 |
578 | func Test_HandleFunction_MultipleLocksNoRace(t *testing.T) {
579 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/MultipleLocksNoRace/prog1.go")
580 | ctx := domain.NewEmptyContext()
581 | state := HandleFunction(ctx, f)
582 | assert.Len(t, state.Lockset.Locks, 0)
583 | assert.Len(t, state.Lockset.Unlocks, 0)
584 |
585 | GA1 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
586 | if !IsGARead(ga) {
587 | return false
588 | }
589 | val, ok := ga.Value.(*ssa.Const)
590 | if !ok {
591 | return false
592 | }
593 | if val.Int64() != 1 {
594 | return false
595 | }
596 | return true
597 | })
598 | assert.Len(t, GA1.Lockset.Locks, 1)
599 |
600 | GA2 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
601 | if !IsGARead(ga) {
602 | return false
603 | }
604 | val, ok := ga.Value.(*ssa.Const)
605 | if !ok {
606 | return false
607 | }
608 | if val.Int64() != 2 {
609 | return false
610 | }
611 | return true
612 | })
613 |
614 | assert.Len(t, GA2.Lockset.Locks, 1)
615 | assert.False(t, GA1.IsConflicting(GA2))
616 | }
617 |
618 | func Test_HandleFunction_NestedConditionWithLockInAllBranches(t *testing.T) {
619 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/NestedConditionWithLockInAllBranches/prog1.go")
620 | ctx := domain.NewEmptyContext()
621 | state := HandleFunction(ctx, f)
622 | assert.Len(t, state.Lockset.Locks, 1)
623 | assert.Len(t, state.Lockset.Unlocks, 0)
624 |
625 | GA1 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
626 | if !IsGARead(ga) {
627 | return false
628 | }
629 | _, ok := ga.Value.(*ssa.MakeInterface)
630 | return ok
631 | })
632 | assert.Len(t, GA1.Lockset.Locks, 1)
633 | }
634 |
635 | func Test_HandleFunction_NestedLockInStruct(t *testing.T) {
636 | f, _ := LoadMain(t, "./testdata/Functions/LocksAndUnlocks/NestedLockInStruct/prog1.go")
637 | ctx := domain.NewEmptyContext()
638 | state := HandleFunction(ctx, f)
639 | assert.Len(t, state.Lockset.Locks, 0)
640 | assert.Len(t, state.Lockset.Unlocks, 1)
641 |
642 | GA1 := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
643 | if !IsGARead(ga) {
644 | return false
645 | }
646 | val, ok := ga.Value.(*ssa.Const)
647 | if !ok {
648 | return false
649 | }
650 | if val.Int64() != 5 {
651 | return false
652 | }
653 | return true
654 | })
655 | assert.Len(t, GA1.Lockset.Locks, 0)
656 | assert.Len(t, GA1.Lockset.Unlocks, 1)
657 | }
658 |
659 | func Test_HandleFunction_DataRaceGoto(t *testing.T) {
660 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceGoto/prog1.go")
661 | ctx := domain.NewEmptyContext()
662 | state := HandleFunction(ctx, f)
663 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
664 | require.NoError(t, err)
665 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
666 | if !IsGAWrite(ga) {
667 | return false
668 | }
669 | val, ok := ga.Value.(*ssa.Global)
670 | if !ok {
671 | return false
672 | }
673 | if GetGlobalString(val) != "a" {
674 | return false
675 | }
676 | return true
677 | }, 2)
678 |
679 | found := false
680 | for _, ca := range conflictingAccesses {
681 | if EqualDifferentOrder(gas, ca) {
682 | found = true
683 | }
684 | }
685 | assert.True(t, found)
686 |
687 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
688 | if !IsGAWrite(ga) {
689 | return false
690 | }
691 | val, ok := ga.Value.(*ssa.Global)
692 | if !ok {
693 | return false
694 | }
695 | if GetGlobalString(val) != "a" {
696 | return false
697 | }
698 | return true
699 | })
700 |
701 | found = false
702 | for _, ca := range conflictingAccesses {
703 | if EqualDifferentOrder([]*domain.GuardedAccess{gas[1], ga}, ca) {
704 | found = true
705 | }
706 | }
707 | assert.True(t, found)
708 | }
709 |
710 | func Test_HandleFunction_DataRaceMap(t *testing.T) {
711 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceMap/prog1.go")
712 | ctx := domain.NewEmptyContext()
713 | state := HandleFunction(ctx, f)
714 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
715 | require.NoError(t, err)
716 | gaA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
717 | if !IsGAWrite(ga) {
718 | return false
719 | }
720 | val, ok := ga.Value.(*ssa.UnOp)
721 | if !ok {
722 | return false
723 | }
724 | X, ok := val.X.(*ssa.FreeVar)
725 | if !ok {
726 | return false
727 | }
728 | if X.Name() != "m" {
729 | return false
730 | }
731 | return true
732 | })
733 |
734 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
735 | if !IsGAWrite(ga) {
736 | return false
737 | }
738 | val, ok := ga.Value.(*ssa.UnOp)
739 | if !ok {
740 | return false
741 | }
742 | X, ok := val.X.(*ssa.Alloc)
743 | if !ok {
744 | return false
745 | }
746 | if X.Comment != "m" {
747 | return false
748 | }
749 | return true
750 | })
751 |
752 | found := false
753 | for _, ca := range conflictingAccesses {
754 | if EqualDifferentOrder([]*domain.GuardedAccess{gaA, gaB}, ca) {
755 | found = true
756 | }
757 | }
758 | assert.True(t, found)
759 | }
760 |
761 | func Test_HandleFunction_DataRaceNestedSameFunction(t *testing.T) {
762 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceNestedSameFunction/prog1.go")
763 | ctx := domain.NewEmptyContext()
764 | state := HandleFunction(ctx, f)
765 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
766 | require.NoError(t, err)
767 | gas := FindMultipleGA(state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
768 | global, ok := ga.Value.(*ssa.Global)
769 | if !ok {
770 | return false
771 | }
772 | if global.Name() != "count" {
773 | return false
774 | }
775 | return true
776 | })
777 |
778 | assert.Subset(t, gas, conflictingAccesses[0])
779 | }
780 |
781 | func Test_HandleFunction_DataRaceProperty(t *testing.T) {
782 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceProperty/prog1.go")
783 | ctx := domain.NewEmptyContext()
784 | state := HandleFunction(ctx, f)
785 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
786 | require.NoError(t, err)
787 | gaA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
788 | if !IsGAWrite(ga) {
789 | return false
790 | }
791 | field, ok := ga.Value.(*ssa.FieldAddr)
792 | if !ok {
793 | return false
794 | }
795 | if field.X.Name() != "w" {
796 | return false
797 | }
798 | return true
799 | })
800 | assert.Len(t, gaA.State.Clock, 2)
801 |
802 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
803 | if !IsGARead(ga) {
804 | return false
805 | }
806 | field, ok := ga.Value.(*ssa.FieldAddr)
807 | if !ok {
808 | return false
809 | }
810 | _, ok = field.X.(*ssa.UnOp)
811 | return ok
812 | })
813 | assert.Len(t, gaB.State.Clock, 3)
814 |
815 | found := false
816 | for _, ca := range conflictingAccesses {
817 | if EqualDifferentOrder([]*domain.GuardedAccess{gaA, gaB}, ca) {
818 | found = true
819 | }
820 | }
821 | assert.True(t, found)
822 | }
823 |
824 | func Test_HandleFunction_DataRaceRecursion(t *testing.T) {
825 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceRecursion/prog1.go")
826 | ctx := domain.NewEmptyContext()
827 | state := HandleFunction(ctx, f)
828 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
829 | require.NoError(t, err)
830 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
831 | if !IsGAWrite(ga) {
832 | return false
833 | }
834 | field, ok := ga.Value.(*ssa.Global)
835 | if !ok {
836 | return false
837 | }
838 | if field.Name() != "a" {
839 | return false
840 | }
841 | return true
842 | }, 3)
843 |
844 | // We check that the recursion is iterated twice. Then we make sure that both cases can conflict with the read at
845 | // the beginning.
846 | found := false
847 | gasToCheck := []*domain.GuardedAccess{gas[0], gas[1]}
848 | for _, ca := range conflictingAccesses {
849 | if EqualDifferentOrder(gasToCheck, ca) {
850 | found = true
851 | }
852 | }
853 | assert.True(t, found)
854 |
855 | found = false
856 | gasToCheck = []*domain.GuardedAccess{gas[0], gas[2]}
857 | for _, ca := range conflictingAccesses {
858 | if EqualDifferentOrder(gasToCheck, ca) {
859 | found = true
860 | }
861 | }
862 | assert.True(t, found)
863 | }
864 |
865 | func Test_HandleFunction_DataRaceShadowedErr(t *testing.T) {
866 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceShadowedErr/prog1.go")
867 | ctx := domain.NewEmptyContext()
868 | state := HandleFunction(ctx, f)
869 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
870 | require.NoError(t, err)
871 | assert.Len(t, conflictingAccesses, 6)
872 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses)
873 | assert.Len(t, filteredAccesses, 3)
874 | }
875 |
876 | func Test_HandleFunction_DataRaceWithOnlyAlloc(t *testing.T) {
877 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceWithOnlyAlloc/prog1.go")
878 | ctx := domain.NewEmptyContext()
879 | state := HandleFunction(ctx, f)
880 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
881 | require.NoError(t, err)
882 | assert.Len(t, conflictingAccesses, 2)
883 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses)
884 | assert.Len(t, filteredAccesses, 1)
885 |
886 | gaA := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
887 | if !IsGAWrite(ga) {
888 | return false
889 | }
890 | _, ok := ga.Value.(*ssa.FreeVar)
891 | return ok
892 | })
893 |
894 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
895 | if !IsGARead(ga) {
896 | return false
897 | }
898 | _, ok := ga.Value.(*ssa.Alloc)
899 | return ok
900 | })
901 | require.Len(t, filteredAccesses, 1)
902 | assert.True(t, EqualDifferentOrder([]*domain.GuardedAccess{gaA, gaB}, filteredAccesses[0]))
903 | }
904 |
905 | func Test_HandleFunction_DataRaceWithSameFunction(t *testing.T) {
906 | f, pkg := LoadMain(t, "./testdata/Functions/General/DataRaceWithSameFunction/prog1.go")
907 | ctx := domain.NewEmptyContext()
908 | entryCallCommon := ssa.CallCommon{Value: f}
909 | state := HandleCallCommon(ctx, &entryCallCommon, f.Pos())
910 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
911 | require.NoError(t, err)
912 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses)
913 | assert.Len(t, filteredAccesses, 1)
914 |
915 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
916 | val, ok := ga.Value.(*ssa.Global)
917 | if !ok {
918 | return false
919 | }
920 | if GetGlobalString(val) != "count" {
921 | return false
922 | }
923 | return true
924 | }, 4)
925 | for _, ga := range gas {
926 | assert.Len(t, ga.State.StackTrace.Iter(), 2)
927 | }
928 | require.Len(t, filteredAccesses, 1)
929 | assert.Subset(t, gas, conflictingAccesses[0])
930 | }
931 |
932 | func Test_HandleFunction_NestedFunctions(t *testing.T) {
933 | f, _ := LoadMain(t, "./testdata/Functions/General/NestedFunctions/prog1.go")
934 | ctx := domain.NewEmptyContext()
935 | state := HandleFunction(ctx, f)
936 | assert.Len(t, state.Lockset.Locks, 1)
937 | assert.Len(t, state.Lockset.Unlocks, 2)
938 |
939 | ga := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
940 | if !IsGARead(ga) {
941 | return false
942 | }
943 | val, ok := ga.Value.(*ssa.Const)
944 | if !ok {
945 | return false
946 | }
947 | if val.Int64() != 5 {
948 | return false
949 | }
950 | return true
951 | })
952 | assert.Len(t, ga.Lockset.Locks, 2)
953 | assert.Len(t, ga.Lockset.Unlocks, 0)
954 |
955 | ga = FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
956 | if !IsGARead(ga) {
957 | return false
958 | }
959 | val, ok := ga.Value.(*ssa.Const)
960 | if !ok {
961 | return false
962 | }
963 | if val.Int64() != 6 {
964 | return false
965 | }
966 | return true
967 | })
968 | assert.Len(t, ga.Lockset.Locks, 3)
969 | assert.Len(t, ga.Lockset.Unlocks, 0)
970 | }
971 |
972 | func Test_HandleFunction_RecursionWithGoroutine(t *testing.T) {
973 | f, _ := LoadMain(t, "./testdata/Functions/General/RecursionWithGoroutine/prog1.go")
974 | ctx := domain.NewEmptyContext()
975 | state := HandleFunction(ctx, f)
976 |
977 | // Should found 2 occurrences since the algorithm should traverse more then once to find conflicting accesses such
978 | // as recursion with a goroutine
979 | _ = FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
980 | if !IsGARead(ga) {
981 | return false
982 | }
983 | val, ok := ga.Value.(*ssa.Const)
984 | if !ok {
985 | return false
986 | }
987 | return val.Int64() == 0
988 | }, 2)
989 | }
990 |
991 | func Test_HandleFunction_Simple(t *testing.T) {
992 | f, _ := LoadMain(t, "./testdata/Functions/General/Simple/prog1.go")
993 | ctx := domain.NewEmptyContext()
994 | state := HandleFunction(ctx, f)
995 |
996 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
997 | if !IsGAWrite(ga) {
998 | return false
999 | }
1000 | _, ok := ga.Value.(*ssa.FieldAddr)
1001 | return ok
1002 | }, 2)
1003 | assert.True(t, gas[0].State.MayConcurrent(gas[1].State))
1004 | }
1005 |
1006 | func Test_HandleFunction_StructMethod(t *testing.T) {
1007 | f, _ := LoadMain(t, "./testdata/Functions/General/StructMethod/prog1.go")
1008 | ctx := domain.NewEmptyContext()
1009 | state := HandleFunction(ctx, f)
1010 |
1011 | gaA := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
1012 | if !IsGAWrite(ga) {
1013 | return false
1014 | }
1015 | val, ok := ga.Value.(*ssa.FieldAddr)
1016 | if !ok {
1017 | return false
1018 | }
1019 | _, ok = val.X.(*ssa.Alloc)
1020 | return !ok
1021 | }, 1)
1022 |
1023 | gaB := FindGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
1024 | if !IsGARead(ga) {
1025 | return false
1026 | }
1027 | val, ok := ga.Value.(*ssa.FieldAddr)
1028 | if !ok {
1029 | return false
1030 | }
1031 | _, ok = val.X.(*ssa.Parameter)
1032 | return ok
1033 | })
1034 | assert.True(t, gaA[0].State.MayConcurrent(gaB.State))
1035 | }
1036 |
1037 | func Test_HandleFunction_DataRaceInterfaceOverChannel(t *testing.T) {
1038 | f, pkg := LoadMain(t, "./testdata/Functions/PointerAnalysis/DataRaceInterfaceOverChannel/prog1.go")
1039 | ctx := domain.NewEmptyContext()
1040 | state := HandleFunction(ctx, f)
1041 |
1042 | gas := FindMultipleGAWithFail(t, state.GuardedAccesses, func(ga *domain.GuardedAccess) bool {
1043 | if !IsGAWrite(ga) {
1044 | return false
1045 | }
1046 | _, ok := ga.Value.(*ssa.FieldAddr)
1047 | return ok
1048 | }, 2)
1049 |
1050 | conflictingAccesses, err := pointerAnalysis.Analysis(pkg, state.GuardedAccesses)
1051 | require.NoError(t, err)
1052 | filteredAccesses := pointerAnalysis.FilterDuplicates(conflictingAccesses)
1053 | require.Len(t, filteredAccesses, 1)
1054 | found := false
1055 | for _, ca := range conflictingAccesses {
1056 | if EqualDifferentOrder(gas, ca) {
1057 | found = true
1058 | }
1059 | }
1060 | assert.True(t, found)
1061 | }
1062 |
--------------------------------------------------------------------------------
/ssaUtils/Locks.go:
--------------------------------------------------------------------------------
1 | package ssaUtils
2 |
3 | import (
4 | "github.com/amit-davidson/Chronos/domain"
5 | "github.com/amit-davidson/Chronos/ssaPureUtils"
6 | "go/token"
7 | "golang.org/x/tools/go/ssa"
8 | )
9 |
10 | func AddLock(funcState *domain.BlockState, call *ssa.CallCommon, isUnlock bool) {
11 | recv := call.Args[0]
12 | mutexPos := ssaPureUtils.GetMutexPos(recv)
13 | lock := map[token.Pos]*ssa.CallCommon{mutexPos: call}
14 | if isUnlock {
15 | funcState.Lockset.UpdateWithNewLockSet(nil, lock)
16 | } else {
17 | funcState.Lockset.UpdateWithNewLockSet(lock, nil)
18 | }
19 | }
--------------------------------------------------------------------------------
/ssaUtils/Packages.go:
--------------------------------------------------------------------------------
1 | package ssaUtils
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/amit-davidson/Chronos/domain"
7 | "go/token"
8 | "go/types"
9 | "golang.org/x/tools/go/loader"
10 | "golang.org/x/tools/go/packages"
11 | "golang.org/x/tools/go/ssa"
12 | "golang.org/x/tools/go/ssa/ssautil"
13 | "sort"
14 | "testing"
15 | )
16 |
17 | var typesCache = make(map[*types.Interface][]*ssa.Function)
18 | var GlobalProgram *ssa.Program
19 | var GlobalModuleName string
20 |
21 | var ErrNoPackages = errors.New("no packages in the path")
22 | var ErrLoadPackages = errors.New("loading the following file contained errors")
23 |
24 | func Create(t *testing.T, path, fileName string) *ssa.Package {
25 | var conf loader.Config
26 | f, err := conf.ParseFile(fileName, nil)
27 | if err != nil {
28 | t.Fatal(err)
29 | }
30 | conf.CreateFromFiles(path, f)
31 |
32 | lprog, err := conf.Load()
33 | if err != nil {
34 | t.Fatal(err)
35 | }
36 |
37 | // We needn't call Build.
38 | foo := lprog.Package(path).Pkg
39 | return ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions).Package(foo)
40 | }
41 |
42 | func LoadPackage(path, modulePath string) (*ssa.Program, *ssa.Package, error) {
43 | conf1 := packages.Config{
44 | Mode: packages.LoadAllSyntax,
45 | Dir: modulePath,
46 | }
47 | loadQuery := fmt.Sprintf("file=%s", path)
48 | pkgs, err := packages.Load(&conf1, loadQuery)
49 | if err != nil {
50 | return nil, nil, err
51 | }
52 | if len(pkgs) == 0 {
53 | return nil, nil, fmt.Errorf("%s: %w", path, ErrNoPackages)
54 | }
55 |
56 | if len(pkgs[0].Errors) > 0 {
57 | return nil, nil, fmt.Errorf("%w %s: %s", ErrLoadPackages, path, pkgs[0].Errors[0].Msg)
58 | }
59 | ssaProg, ssaPkgs := ssautil.AllPackages(pkgs, 0)
60 | ssaProg.Build()
61 | ssaPkg := ssaPkgs[0]
62 | return ssaProg, ssaPkg, nil
63 | }
64 |
65 | func GetStackTrace(prog *ssa.Program, ga *domain.GuardedAccess) string {
66 | stack := ""
67 | for _, pos := range ga.State.StackTrace.Iter() {
68 | calculatedPos := prog.Fset.Position(token.Pos(pos))
69 | stack += calculatedPos.String()
70 | stack += " ->\n"
71 | }
72 | return stack
73 | }
74 |
75 | func GetMethodImplementations(recv types.Type, method *types.Func) []*ssa.Function {
76 | methodImplementations := make([]*ssa.Function, 0)
77 | recvInterface := recv.(*types.Interface)
78 |
79 | if methodImplementations, ok := typesCache[recvInterface]; ok {
80 | return methodImplementations
81 | }
82 |
83 | implementors := make([]types.Type, 0)
84 | for _, typ := range GlobalProgram.RuntimeTypes() {
85 | if types.Implements(typ, recvInterface) {
86 | implementors = append(implementors, typ)
87 | }
88 | }
89 | for _, implementor := range implementors {
90 | setMethods := GlobalProgram.MethodSets.MethodSet(implementor)
91 | method := setMethods.Lookup(method.Pkg(), method.Name())
92 | methodImpl := GlobalProgram.MethodValue(method)
93 | if methodImpl.Synthetic == "" {
94 | methodImplementations = append(methodImplementations, methodImpl)
95 | }
96 | }
97 |
98 | // Sort by pos to enter previous implementations first. This make the search deterministic and easier for debugging
99 | sortedImplementations := sortMethodImplementations(methodImplementations)
100 | typesCache[recvInterface] = sortedImplementations
101 | return sortedImplementations
102 | }
103 |
104 | func sortMethodImplementations(methodImplementations []*ssa.Function) []*ssa.Function {
105 | posSlice := make([]int, 0)
106 | sortedImplementations := make([]*ssa.Function, 0)
107 | implMap := make(map[int]*ssa.Function)
108 | for _, methodImplementation := range methodImplementations {
109 | pos := methodImplementation.Pos()
110 | implMap[int(pos)] = methodImplementation
111 | posSlice = append(posSlice, int(pos))
112 | }
113 | sort.Ints(posSlice)
114 | for _, pos := range posSlice {
115 | sortedImplementations = append(sortedImplementations, implMap[pos])
116 | }
117 | return sortedImplementations
118 | }
119 |
--------------------------------------------------------------------------------
/ssaUtils/PreProcess.go:
--------------------------------------------------------------------------------
1 | package ssaUtils
2 |
3 | import (
4 | "errors"
5 | "github.com/amit-davidson/Chronos/utils/stacks"
6 | "go/types"
7 | "golang.org/x/tools/go/ssa"
8 | "os"
9 | "path"
10 | "strings"
11 | )
12 |
13 | type FunctionWithLocksPreprocess struct {
14 | FunctionWithLocks map[*types.Signature]bool
15 | locks map[int]struct{} // Is lock exists, and if it's in a conditional path
16 | visitedFuncs *stacks.FunctionStackWithMap
17 | }
18 |
19 | func InitPreProcess(prog *ssa.Program, defaultModulePath string) error {
20 | GlobalProgram = prog
21 | p := strings.TrimSuffix(defaultModulePath, string(os.PathSeparator))
22 | splittedPath := strings.Split(p, string(os.PathSeparator))
23 | if len(splittedPath) < 3 {
24 | return errors.New("path to module can be relative or absolute but must contain the format:{VCS}/{organization}/{package}")
25 | }
26 | l := len(splittedPath)
27 | moduleName := path.Join(splittedPath[l-3], splittedPath[l-2], splittedPath[l-1])
28 | GlobalModuleName = moduleName
29 | return nil
30 | }
31 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/Defer/DeferredLockAndUnlockIfBranch/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "math/rand"
5 | "sync"
6 | )
7 |
8 | var a int
9 | var cond = false
10 |
11 | func main() {
12 | mutex := sync.Mutex{}
13 | if rand.Int() > 0 {
14 | defer mutex.Lock()
15 | }
16 | defer func() {
17 | a = 5
18 | }()
19 | if rand.Int() > 0 {
20 | defer mutex.Unlock()
21 | }
22 | a = 6
23 | }
24 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/Defer/NestedDeferWithLockAndUnlock/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "math/rand"
5 | "sync"
6 | )
7 |
8 | var a int
9 | var cond = false
10 |
11 | func main() {
12 | mutex := sync.Mutex{}
13 | if rand.Int() > 0 {
14 | defer mutex.Lock()
15 | }
16 | defer func() {
17 | a = 6
18 | }()
19 | defer func() {
20 | defer mutex.Lock()
21 | defer mutex.Unlock()
22 | }()
23 | }
24 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/Defer/NestedDeferWithLockAndUnlockAndGoroutine/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "math/rand"
5 | "sync"
6 | )
7 |
8 | var a int
9 | var cond = false
10 |
11 | func main() {
12 | mutex := sync.Mutex{}
13 | if rand.Int() > 0 {
14 | defer mutex.Lock()
15 | }
16 | defer func() {
17 | mutex.Lock()
18 | a = 6
19 | defer mutex.Unlock()
20 | }()
21 | mutex.Lock()
22 | defer func() {
23 | go func() {
24 | a = 7
25 | defer mutex.Unlock()
26 | }()
27 | }()
28 | }
29 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/ForLoops/ForLoopLockInsideLoop/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | func main() {
6 | m := make(map[string]string)
7 | m["2"] = "b" // Second conflicting access.
8 | for _, _ = range m {
9 | //for i, j := range m {
10 | //fmt.Println(i, j)
11 | //if i > "1" {
12 | // fmt.Println(i, j)
13 | //}
14 | mutex := sync.Mutex{}
15 | mutex.Lock()
16 | m["2"] = "c" // Second conflicting access.
17 | //}
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/ForLoops/ForLoopLockOutsideLoop/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | func main() {
6 | m := make(map[string]string)
7 | m["2"] = "b" // Second conflicting access.
8 | mutex := sync.Mutex{}
9 | mutex.Lock()
10 | for _, _ = range m {
11 | //for i, j := range m {
12 | //fmt.Println(i, j)
13 | //if i > "1" {
14 | // fmt.Println(i, j)
15 | //}
16 | m["2"] = "c" // Second conflicting access.
17 | //}
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/ForLoops/NestedForLoopWithRace/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | m := make(map[string]string)
5 | go func() {
6 | for _, _ = range m {
7 | for _, _ = range m {
8 | m["1"] = "a"
9 | }
10 | }
11 | }()
12 | m["2"] = "b"
13 | }
14 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/ForLoops/WhileLoop/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import "time"
5 |
6 | type myType struct {
7 | A int
8 | }
9 |
10 | func main() {
11 | x := new(myType)
12 | c := make(chan int, 100)
13 | go func() {
14 | for true {
15 | x = new(myType) // write to x
16 | c <- 0
17 | <-c
18 | }
19 | }()
20 | for i := 0; i < 4; i++ {
21 | go func() {
22 | for true {
23 | _ = *x // if exists a race condition, `*x` will visit a wrong memory address, and will panic
24 | c <- 0
25 | <-c
26 | }
27 | }()
28 | }
29 | time.Sleep(time.Second * 10)
30 | fmt.Println("end")
31 | }
32 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/ForLoops/WhileLoopWithoutHeader/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "sync"
6 | )
7 | import "time"
8 |
9 | type myType struct {
10 | A int
11 | }
12 |
13 | var mutex = sync.Mutex{}
14 | func main() {
15 | x := new(myType)
16 | c := make(chan int, 100)
17 | go func() {
18 | for {
19 | mutex.Lock()
20 | x = new(myType) // write to x
21 | c <- 0
22 | <-c
23 | }
24 | }()
25 | for i := 0; i < 4; i++ {
26 | go func() {
27 | for {
28 | _ = *x // if exists a race condition, `*x` will visit a wrong memory address, and will panic
29 | c <- 0
30 | <-c
31 | }
32 | }()
33 | }
34 | time.Sleep(time.Second * 10)
35 | fmt.Println("end")
36 | }
37 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceGoto/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var a int
8 | var mutex1 = sync.Mutex{}
9 |
10 | func main() {
11 | go func() {
12 | a = 5
13 | }()
14 | fn1()
15 | }
16 |
17 | func fn1() {
18 | if a > 4 {
19 | goto End
20 | }
21 | return
22 | End:
23 | a = 6
24 | }
25 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceMap/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | m := make(map[string]string)
6 | go func() {
7 | m["1"] = "a" // First conflicting access.
8 | c <- true
9 | }()
10 | m["2"] = "b" // Second conflicting access.
11 | }
12 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceNestedSameFunction/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | var count int
4 |
5 | func race() {
6 | count++
7 | }
8 |
9 | func f() {
10 | race()
11 | }
12 |
13 | func main() {
14 | go f()
15 | go f()
16 | }
17 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceProperty/prog1.go:
--------------------------------------------------------------------------------
1 | package testutils
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "time"
7 | )
8 |
9 | type Watchdog struct{ last int64 }
10 |
11 | func (w *Watchdog) KeepAlive() {
12 | w.last = time.Now().UnixNano() // First conflicting access.
13 | }
14 |
15 | func (w *Watchdog) Start() {
16 | go func() {
17 | time.Sleep(time.Second)
18 | // Second conflicting access.
19 | if w.last < time.Now().Add(-10*time.Second).UnixNano() {
20 | fmt.Println("No keepalives for 10 seconds. Dying.")
21 | os.Exit(1)
22 | }
23 | }()
24 | }
25 |
26 | func main() {
27 | wd := Watchdog{}
28 | go wd.KeepAlive()
29 | go wd.Start()
30 | }
31 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceRecursion/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | var a int
4 |
5 | func main() {
6 | go func() {
7 | a = 5
8 | }()
9 | fn1(0)
10 | }
11 |
12 | func fn1(counter int) {
13 | if counter >= 10 {
14 | return
15 | }
16 | if counter == 8 {
17 | a = 6
18 | }
19 | counter += 1
20 | fn1(counter)
21 | }
22 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceShadowedErr/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | )
6 |
7 | // ParallelWrite writes data to file1 and file2, returns the errors.
8 | func ParallelWrite(data []byte) chan error {
9 | res := make(chan error, 2)
10 | f1, err := os.Create("file1")
11 | if err != nil {
12 | res <- err
13 | } else {
14 | go func() {
15 | // This err is shared with the main goroutine,
16 | // so the write races with the write below.
17 | _, err = f1.Write(data)
18 | res <- err
19 | f1.Close()
20 | }()
21 | }
22 | f2, err := os.Create("file2") // The second conflicting write to err.
23 | if err != nil {
24 | res <- err
25 | } else {
26 | go func() {
27 | _, err = f2.Write(data)
28 | res <- err
29 | f2.Close()
30 | }()
31 | }
32 | return res
33 | }
34 |
35 | func main() {
36 | ParallelWrite([]byte("1"))
37 | }
38 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceWithOnlyAlloc/prog1.go:
--------------------------------------------------------------------------------
1 | package testutils
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | fmt.Println(getNumber())
9 | }
10 |
11 | func getNumber() int {
12 | var i int
13 | go func() {
14 | i = 5
15 | }()
16 |
17 | return i
18 | }
19 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/DataRaceWithSameFunction/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | var count int
8 |
9 | func race() {
10 | count++
11 | }
12 |
13 | func main() {
14 | go race()
15 | go race()
16 | time.Sleep(1 * time.Second)
17 | }
18 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/ElseIf/prog1.go:
--------------------------------------------------------------------------------
1 | package Lock
2 |
3 | import (
4 | "math/rand"
5 | )
6 |
7 | var a int
8 |
9 | func main() {
10 | a := rand.Int()
11 | if a > 0 {
12 | a = 3
13 | } else if a == 0 {
14 | a = 4
15 | } else {
16 | a = 5
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/NestedFunctions/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var a int
8 | var mutex1 = sync.Mutex{}
9 |
10 | func main() {
11 | mutex2 := sync.Mutex{}
12 | mutex1.Lock()
13 | mutex2.Lock()
14 | a = 5
15 | fn2()
16 | }
17 |
18 | func fn2() {
19 | mutex2 := sync.Mutex{}
20 | mutex2.Lock()
21 | a = 6
22 | mutex2.Unlock()
23 | mutex1.Unlock()
24 | }
25 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/RecursionWithGoroutine/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | func main() {
6 | g := &sync.WaitGroup{}
7 | runs := 5
8 | recur(runs, g)
9 | }
10 |
11 | func recur(iter int, g *sync.WaitGroup) {
12 | if iter <= 0 {
13 | return
14 | }
15 | go recur(iter-1, g)
16 | }
17 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/RecursionWithRecover/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | f()
7 | fmt.Println("Returned normally from f.")
8 | }
9 |
10 | func f() {
11 | defer func() {
12 | if r := recover(); r != nil {
13 | fmt.Println("Recovered in f", r)
14 | }
15 | }()
16 | fmt.Println("Calling g.")
17 | g(0)
18 | fmt.Println("Returned normally from g.")
19 | }
20 |
21 | func g(i int) {
22 | if i > 3 {
23 | fmt.Println("Panicking!")
24 | panic(fmt.Sprintf("%v", i))
25 | }
26 | defer fmt.Println("Defer in g", i)
27 | fmt.Println("Printing in g", i)
28 | g(i + 1)
29 | }
30 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/Simple/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type A struct {
4 | X int
5 | }
6 |
7 | func main() {
8 | a := A{}
9 | go func() {
10 | a.X = 1
11 | }()
12 | a.X = 2
13 | }
14 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/General/StructMethod/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | type Ben struct {
8 | name string
9 | }
10 |
11 | func (b *Ben) Hello() {
12 | fmt.Printf("my name is:%s", b.name)
13 | }
14 |
15 | func main() {
16 | var ben = &Ben{"Ben"}
17 |
18 | go func() {
19 | ben.name = "Jerry"
20 | }()
21 | ben.Hello()
22 | }
23 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/Interfaces/DataRaceIceCreamMaker/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type IceCreamMaker interface {
4 | Hello()
5 | }
6 |
7 | type Ben struct {
8 | name string
9 | }
10 |
11 | func (b *Ben) Hello() {
12 | b.name = "Ben"
13 | }
14 |
15 | func main() {
16 | var ben = &Ben{}
17 | var maker IceCreamMaker = ben
18 | go maker.Hello()
19 | ben.name = "1"
20 | }
21 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/Interfaces/InterfaceWithLock/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | type IceCreamMaker interface {
6 | // Hello greets a customer
7 | Hello()
8 | }
9 |
10 | type Ben struct {
11 | name string
12 | }
13 |
14 | type Jerry struct {
15 | name string
16 | }
17 |
18 | func (b *Ben) Hello() {
19 | mutex.Lock()
20 | b.name = "Ben"
21 | }
22 |
23 | func (j *Jerry) Hello() {
24 | mutex.Lock()
25 | j.name = "Jerry"
26 | }
27 |
28 | var mutex = sync.Mutex{}
29 |
30 | func main() {
31 | var ben = &Ben{}
32 | var maker IceCreamMaker = ben
33 | maker.Hello()
34 | ben.name = "1"
35 | }
36 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/Interfaces/NestedInterface/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Employee interface {
4 | IceCreamMaker
5 | }
6 |
7 | type IceCreamMaker interface {
8 | Hello()
9 | }
10 |
11 | type Jerry struct {
12 | name string
13 | }
14 |
15 | func (j *Jerry) Hello() {
16 | j.name = "Jerry"
17 | }
18 |
19 | func main() {
20 | var ben = &Jerry{}
21 | var employee Employee = ben
22 | employee.Hello()
23 | }
24 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/Lock/prog1.go:
--------------------------------------------------------------------------------
1 | package Lock
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var a int
8 |
9 | func main() {
10 | mutex := sync.Mutex{}
11 | mutex.Lock()
12 | a = 5
13 | }
14 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/LockAndUnlock/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var a int
8 |
9 | func main() {
10 | mutex := sync.Mutex{}
11 | mutex.Lock()
12 | mutex.Unlock()
13 | a = 5
14 | }
15 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/LockAndUnlockIfBranch/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "math/rand"
5 | "sync"
6 | )
7 |
8 | var a int
9 | var cond = false
10 |
11 | func main() {
12 | mutex := sync.Mutex{}
13 | if rand.Int() > 0 {
14 | mutex.Lock()
15 | }
16 | a = 5
17 | if rand.Int() > 0 {
18 | mutex.Unlock()
19 | }
20 | a = 6
21 | }
22 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/LockInBothBranches/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "math/rand"
5 | "sync"
6 | )
7 |
8 | var a int
9 | var cond = false
10 |
11 | func main() {
12 | mutex := sync.Mutex{}
13 | if rand.Int() > 0 {
14 | mutex.Lock()
15 | } else {
16 | mutex.Lock()
17 | }
18 | a = 5
19 | }
20 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/LockInsideGoroutine/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var a = make(map[string]interface{}, 0)
8 |
9 | func main() {
10 | mutex := sync.Mutex{}
11 | go func() {
12 | mutex.Lock()
13 | a["1"] = make(map[string]interface{}, 0)
14 | }()
15 | a["2"] = make(map[string]interface{}, 0)
16 | }
17 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/MultipleLocksNoRace/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | var x int16 = 0
10 | _ = x
11 | ch := make(chan bool, 2)
12 | go func() {
13 | mu.Lock()
14 | defer mu.Unlock()
15 | x = 1
16 | ch <- true
17 | }()
18 | go func() {
19 | mu.Lock()
20 | x = 2
21 | mu.Unlock()
22 | ch <- true
23 | }()
24 | <-ch
25 | <-ch
26 | }
27 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/MultipleLocksRace/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | var x int16 = 0
10 | _ = x
11 | ch := make(chan bool, 2)
12 | go func() {
13 | x = 1
14 | mu.Lock()
15 | defer mu.Unlock()
16 | ch <- true
17 | }()
18 | go func() {
19 | x = 2
20 | mu.Lock()
21 | mu.Unlock()
22 | ch <- true
23 | }()
24 | <-ch
25 | <-ch
26 | }
27 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/NestedConditionWithLockInAllBranches/prog1.go:
--------------------------------------------------------------------------------
1 | package pkg
2 |
3 | import (
4 | "math/rand"
5 | "sync"
6 | )
7 |
8 | var a = make(map[string]interface{}, 0)
9 |
10 | func main() {
11 | mutex := sync.Mutex{}
12 | if rand.Int() > 0 {
13 | if rand.Int() > 0 {
14 | mutex.Lock()
15 | } else {
16 | mutex.Lock()
17 | }
18 | } else {
19 | mutex.Lock()
20 | }
21 | a["1"] = make(map[string]interface{}, 0)
22 | }
23 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/LocksAndUnlocks/NestedLockInStruct/prog1.go:
--------------------------------------------------------------------------------
1 | package Lock
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | var a int
8 |
9 | type obj3 struct {
10 | obj2 obj2
11 | }
12 | type obj2 struct {
13 | obj1 obj1
14 | }
15 |
16 | type obj1 struct {
17 | a int
18 | mutex sync.Mutex
19 | }
20 |
21 | func main() {
22 | obj3 := obj3{obj2: obj2{obj1: obj1{1, sync.Mutex{}}}}
23 | obj3.obj2.obj1.mutex.Lock()
24 | obj3.obj2.obj1.mutex.Unlock()
25 | a = 5
26 | }
27 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Functions/PointerAnalysis/DataRaceInterfaceOverChannel/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type IceCreamMaker interface {
4 | // Hello greets a customer
5 | Hello()
6 | }
7 |
8 | type Jerry struct {
9 | name string
10 | }
11 |
12 | func (j *Jerry) Hello() {
13 | j.name = "Jerry"
14 | }
15 |
16 | func sayHello() {
17 | var maker IceCreamMaker
18 | maker = <-channel
19 | maker.Hello()
20 | }
21 |
22 | var channel = make(chan IceCreamMaker)
23 |
24 | func main() {
25 | jerry := &Jerry{}
26 | channel <- jerry
27 | go sayHello()
28 | jerry.name = "1"
29 | }
30 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/EvenLockAndUnlockAndLockComesLater/EvenLockAndUnlockAndLockComesLater.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | mu.Unlock()
10 | mu.Lock()
11 | a = 5
12 | _ = a
13 | }
14 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/LockAndUnlockInDifferentBranches/LockAndUnlockInDifferentBranches.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "math/rand"
5 | "sync"
6 | )
7 |
8 | var a int
9 |
10 | func main() {
11 | var mu sync.Mutex
12 | if rand.Int() > 0 {
13 | mu.Unlock()
14 | } else {
15 | mu.Lock()
16 | }
17 | a = 5
18 | _ = a
19 | }
20 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/LockInAStruct/LockInAStruct.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 | type B struct {
7 | Lock sync.Mutex
8 | }
9 | func main() {
10 | b := B{Lock:sync.Mutex{}}
11 | b.Lock.Lock()
12 | a = 5
13 | _ = a
14 | }
15 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/LockInEmbeddedStruct/LockInEmbeddedStruct.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | type B struct {
8 | C
9 | }
10 |
11 | type C struct {
12 | Lock sync.Mutex
13 | }
14 |
15 | func main() {
16 | b := B{C: C{
17 | sync.Mutex{},
18 | }}
19 | b.Lock.Lock()
20 | a = 5
21 | _ = a
22 | }
23 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/MutexInterface/MutexInterface.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | // A Locker represents an object that can be locked and unlocked.
6 | type Locker interface {
7 | Lock()
8 | Unlock()
9 | }
10 |
11 | type A struct {
12 | Lock Locker
13 | }
14 |
15 | func main() {
16 | b := A{Lock: &sync.Mutex{}}
17 | b.Lock.Lock()
18 | }
19 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/NestedFunctionWithLock/NestedFunctionWithLock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | f()
9 | }
10 |
11 | func f() {
12 | var mu sync.Mutex
13 | mu.Lock()
14 | a = 5
15 | _ = a
16 | }
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/NestedFunctionWithLockButThenUnlock/NestedFunctionWithLockButThenUnlock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var mu sync.Mutex
6 |
7 | func main() {
8 | f()
9 | }
10 |
11 | func f() {
12 | g()
13 | mu.Unlock()
14 | }
15 |
16 | func g() {
17 | mu.Lock()
18 | }
19 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/RecursionNestedFunctionWithLock/RecursionNestedFunctionWithLock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var mu sync.Mutex
6 |
7 | func main() {
8 | f()
9 | }
10 |
11 | func f() {
12 | mu.Lock()
13 | f()
14 | }
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/UnevenLockAndUnlockAndLockComesLater/UnevenLockAndUnlockAndLockComesLater.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | mu.Lock()
10 | mu.Lock()
11 | mu.Unlock()
12 | a = 5
13 | _ = a
14 | }
15 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/functionWithEvenLockAndDeferUnlock/functionWithEvenLockAndDeferUnlock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | mu.Lock()
10 | defer mu.Unlock()
11 | a = 5
12 | _ = a
13 | }
14 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/functionWithEvenLockAndUnlock/functionWithEvenLockAndUnlock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | mu.Lock()
10 | mu.Unlock()
11 | a = 5
12 | _ = a
13 | }
14 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/functionWithLock/functionWithLock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | mu.Lock()
10 | a = 5
11 | _ = a
12 | }
13 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/functionWithUnevenLockAndUnlock/functionWithUnevenLockAndUnlock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | mu.Lock()
10 | mu.Lock()
11 | mu.Unlock()
12 | a = 5
13 | _ = a
14 | }
15 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/functionWithUnlock/functionWithUnlock.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | var a int
6 |
7 | func main() {
8 | var mu sync.Mutex
9 | mu.Unlock()
10 | a = 5
11 | _ = a
12 | }
13 |
--------------------------------------------------------------------------------
/ssaUtils/testdata/Preprocess/functionWithoutLocks/functionWithoutLocks.go:
--------------------------------------------------------------------------------
1 | package functionWithoutLocks
2 |
3 | var a int
4 |
5 | func main() {
6 | a = 5
7 | _ = a
8 | }
9 |
--------------------------------------------------------------------------------
/ssaUtils/testutils.go:
--------------------------------------------------------------------------------
1 | package ssaUtils
2 |
3 | import (
4 | "go/constant"
5 | "path/filepath"
6 | "runtime"
7 | "testing"
8 |
9 | "github.com/amit-davidson/Chronos/domain"
10 | "github.com/amit-davidson/Chronos/utils"
11 | "github.com/stretchr/testify/require"
12 | "golang.org/x/tools/go/ssa"
13 | )
14 |
15 | func FindGA(GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool) *domain.GuardedAccess {
16 | wasFound := false
17 | for _, ga := range GuardedAccesses {
18 | wasFound = validationFunc(ga)
19 | if wasFound {
20 | return ga
21 | }
22 | }
23 | return nil
24 | }
25 |
26 | func FindMultipleGA(GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool) []*domain.GuardedAccess {
27 | foundGAs := make([]*domain.GuardedAccess, 0)
28 | for _, ga := range GuardedAccesses {
29 | wasFound := validationFunc(ga)
30 | if wasFound {
31 | foundGAs = append(foundGAs, ga)
32 | }
33 | }
34 | return foundGAs
35 | }
36 |
37 | func GetConstString(v *ssa.Const) string {
38 | return constant.StringVal(v.Value)
39 | }
40 |
41 | func GetGlobalString(v *ssa.Global) string {
42 | return v.Name()
43 | }
44 |
45 | func IsGARead(ga *domain.GuardedAccess) bool {
46 | return ga.OpKind == domain.GuardAccessRead
47 | }
48 |
49 | func IsGAWrite(ga *domain.GuardedAccess) bool {
50 | return ga.OpKind == domain.GuardAccessWrite
51 | }
52 |
53 | func FindGAWithFail(t *testing.T, GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool) *domain.GuardedAccess {
54 | res := FindGA(GuardedAccesses, validationFunc)
55 | require.NotNil(t, res)
56 | return res
57 | }
58 |
59 | func FindMultipleGAWithFail(t *testing.T, GuardedAccesses []*domain.GuardedAccess, validationFunc func(value *domain.GuardedAccess) bool, expectedAmount int) []*domain.GuardedAccess {
60 | res := FindMultipleGA(GuardedAccesses, validationFunc)
61 | require.Equal(t, expectedAmount, len(res))
62 | return res
63 | }
64 |
65 | func LoadMain(t *testing.T, filePath string) (*ssa.Function, *ssa.Package) {
66 | domain.GoroutineCounter = utils.NewCounter()
67 | domain.GuardedAccessCounter = utils.NewCounter()
68 | domain.PosIDCounter = utils.NewCounter()
69 |
70 | _, ex, _, ok := runtime.Caller(0)
71 | require.True(t, ok)
72 | modulePath := filepath.Dir(filepath.Dir(ex))
73 |
74 | ssaProg, ssaPkg, err := LoadPackage(filePath, modulePath)
75 | require.NoError(t, err)
76 | f := ssaPkg.Func("main")
77 | err = InitPreProcess(ssaProg, modulePath)
78 | require.NoError(t, err)
79 | return f, ssaPkg
80 | }
81 |
82 | func EqualDifferentOrder(a, b []*domain.GuardedAccess) bool {
83 | if len(a) != len(b) {
84 | return false
85 | }
86 | diff := make(map[int]int, len(a))
87 | for _, x := range a {
88 | diff[x.ID]++
89 | }
90 | for _, y := range b {
91 | if _, ok := diff[y.ID]; !ok {
92 | return false
93 | }
94 | diff[y.ID] -= 1
95 | if diff[y.ID] == 0 {
96 | delete(diff, y.ID)
97 | }
98 | }
99 | return len(diff) == 0
100 | }
101 |
--------------------------------------------------------------------------------
/tests/stdlib_runner_test.go:
--------------------------------------------------------------------------------
1 | package e2e_tests
2 |
3 | import (
4 | "fmt"
5 | "github.com/amit-davidson/Chronos/domain"
6 | "github.com/amit-davidson/Chronos/output"
7 | "github.com/amit-davidson/Chronos/pointerAnalysis"
8 | "github.com/amit-davidson/Chronos/ssaUtils"
9 | "github.com/amit-davidson/Chronos/utils"
10 | "github.com/stretchr/testify/require"
11 | "golang.org/x/tools/go/ssa"
12 | "os"
13 | "path/filepath"
14 | "runtime"
15 | "testing"
16 | )
17 |
18 | func TestStdlib(t *testing.T) {
19 | var testCases = []struct {
20 | name string
21 | testPath string
22 | }{
23 | {name: "TestNoRaceStackPushPop", testPath: "testdata/stdlib/TestNoRaceStackPushPop/prog1.go"},
24 | {name: "RaceNestedArrayCopy", testPath: "testdata/stdlib/RaceNestedArrayCopy/prog1.go"},
25 | {name: "TestNoRaceAsFunc4", testPath: "testdata/stdlib/TestNoRaceAsFunc4/prog1.go"},
26 | {name: "TestRaceAsFunc3", testPath: "testdata/stdlib/TestRaceAsFunc3/prog1.go"},
27 | {name: "TestRaceAsFunc2", testPath: "testdata/stdlib/TestRaceAsFunc2/prog1.go"},
28 | {name: "TestRaceAsFunc1", testPath: "testdata/stdlib/TestRaceAsFunc1/prog1.go"},
29 | {name: "TestRaceCaseTypeIssue5890", testPath: "testdata/stdlib/TestRaceCaseTypeIssue5890/prog1.go"},
30 | {name: "TestRaceCaseIssue6418", testPath: "testdata/stdlib/TestRaceCaseIssue6418/prog1.go"},
31 | {name: "TestRaceCaseFallthrough", testPath: "testdata/stdlib/TestRaceCaseFallthrough/prog1.go"},
32 | //{name: "TestNoRaceBlank", testPath: "testdata/stdlibNoSuccess/TestNoRaceBlank/prog1.go"}, // blank space
33 | //{name: "TestRaceMethodThunk4", testPath: "testdata/stdlibNoSuccess/TestRaceMethodThunk4/prog1.go"}, // Might be a bug in pointer analysis
34 | {name: "TestRaceMethodThunk3", testPath: "testdata/stdlib/TestRaceMethodThunk3/prog1.go"},
35 | {name: "TestRaceMethodThunk2", testPath: "testdata/stdlib/TestRaceMethodThunk2/prog1.go"},
36 | {name: "TestRaceMethodThunk", testPath: "testdata/stdlibNoSuccess/TestRaceMethodThunk/prog1.go"}, // blank space
37 | {name: "TestNoRaceMethodThunk", testPath: "testdata/stdlib/TestNoRaceMethodThunk/prog1.go"},
38 | {name: "TestRaceNestedStruct", testPath: "testdata/stdlib/TestRaceNestedStruct/prog1.go"},
39 | {name: "TestNoRaceEmptyStruct", testPath: "testdata/stdlibNoSuccess/TestNoRaceEmptyStruct/prog1.go"},
40 | //{name: "TestRaceHeapParam", testPath: "testdata/stdlib/TestRaceHeapParam/prog1.go"}, // No ssa param as value. Might be a bug in ssa.
41 | {name: "TestRaceStructInd", testPath: "testdata/stdlib/TestRaceStructInd/prog1.go"},
42 | {name: "TestRaceAppendSliceStruct", testPath: "testdata/stdlibNoSuccess/TestRaceAppendSliceStruct/prog1.go"},
43 | {name: "TestRaceSliceStruct", testPath: "testdata/stdlibNoSuccess/TestRaceSliceStruct/prog1.go"}, // same
44 | {name: "TestRaceSliceString", testPath: "testdata/stdlib/TestRaceSliceString/prog1.go"},
45 | {name: "TestRaceSliceSlice2", testPath: "testdata/stdlib/TestRaceSliceSlice2/prog1.go"},
46 | {name: "TestRaceSliceSlice", testPath: "testdata/stdlib/TestRaceSliceSlice/prog1.go"},
47 | {name: "TestRaceBlockAs", testPath: "testdata/stdlib/TestRaceBlockAs/prog1.go"},
48 | {name: "TestRaceTypeAssert", testPath: "testdata/stdlib/TestRaceTypeAssert/prog1.go"},
49 | {name: "TestRaceAddrExpr", testPath: "testdata/stdlib/TestRaceAddrExpr/prog1.go"},
50 | {name: "TestNoRaceAddrExpr", testPath: "testdata/stdlib/TestNoRaceAddrExpr/prog1.go"},
51 | {name: "TestRaceDeferArg2", testPath: "testdata/stdlib/TestRaceDeferArg2/prog1.go"},
52 | {name: "TestRaceDeferArg", testPath: "testdata/stdlib/TestRaceDeferArg/prog1.go"},
53 | {name: "TestRacePanicArg", testPath: "testdata/stdlib/TestRacePanicArg/prog1.go"},
54 | {name: "TestNoRaceMethodValue", testPath: "testdata/stdlib/TestNoRaceMethodValue/prog1.go"},
55 | //{name: "TestRaceMethodValue3", testPath: "testdata/stdlib/TestRaceMethodValue3/prog1.go"}, // Might be a bug in pointer analysis
56 | {name: "TestRaceMethodValue2", testPath: "testdata/stdlib/TestRaceMethodValue2/prog1.go"},
57 | {name: "TestRaceMethodValue", testPath: "testdata/stdlib/TestRaceMethodValue/prog1.go"},
58 | {name: "TestRaceMethodCall2", testPath: "testdata/stdlib/TestRaceMethodCall2/prog1.go"},
59 | {name: "TestRaceMethodCall", testPath: "testdata/stdlib/TestRaceMethodCall/prog1.go"},
60 | {name: "TestRaceFuncCall", testPath: "testdata/stdlib/TestRaceFuncCall/prog1.go"},
61 | {name: "TestRaceInterCall2", testPath: "testdata/stdlib/TestRaceInterCall2/prog1.go"},
62 | {name: "TestRaceInterCall", testPath: "testdata/stdlib/TestRaceInterCall/prog1.go"},
63 | {name: "TestRaceMapInit2", testPath: "testdata/stdlib/TestRaceMapInit2/prog1.go"},
64 | {name: "TestRaceMapInit", testPath: "testdata/stdlib/TestRaceMapInit/prog1.go"},
65 | {name: "TestRaceArrayInit", testPath: "testdata/stdlib/TestRaceArrayInit/prog1.go"},
66 | {name: "TestRaceStructInit", testPath: "testdata/stdlib/TestRaceStructInit/prog1.go"},
67 | //{name: "TestNoRaceFuncUnlock", testPath: "testdata/stdlibNoSuccess/TestNoRaceFuncUnlock/prog1.go"}, // No pointer analysis for locks
68 | {name: "TestRaceFuncItself", testPath: "testdata/stdlib/TestRaceFuncItself/prog1.go"},
69 | //{name: "TestNoRaceShortCalc2", testPath: "testdata/stdlibNoSuccess/TestNoRaceShortCalc2/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute
70 | //{name: "TestNoRaceShortCalc", testPath: "testdata/stdlibNoSuccess/TestNoRaceShortCalc/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute
71 | //{name: "TestNoRaceOr", testPath: "testdata/stdlibNoSuccess/TestNoRaceOr/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute
72 | //{name: "TestRaceOr2", testPath: "testdata/stdlib/TestRaceOr2/prog1.go"},
73 | {name: "TestRaceOr", testPath: "testdata/stdlib/TestRaceOr/prog1.go"},
74 | {name: "TestNoRaceAnd", testPath: "testdata/stdlibNoSuccess/TestNoRaceAnd/prog1.go"}, // Cant evaluate first part of condition to see the second will never execute
75 | {name: "TestRaceAnd2", testPath: "testdata/stdlib/TestRaceAnd2/prog1.go"},
76 | {name: "TestRaceAnd", testPath: "testdata/stdlib/TestRaceAnd/prog1.go"},
77 | //{name: "TestRaceEmptyInterface2", testPath: "testdata/stdlibNoSuccess/TestRaceEmptyInterface2/prog1.go"},
78 | {name: "TestRaceEmptyInterface1", testPath: "testdata/stdlib/TestRaceEmptyInterface/prog1.go"},
79 | {name: "TestRaceRune", testPath: "testdata/stdlib/TestRaceRune/prog1.go"},
80 | {name: "TestRaceIndirection", testPath: "testdata/stdlibNoSuccess/TestRaceIndirection/prog1.go"}, // sync using channels
81 | {name: "TestRaceFuncArgsRW", testPath: "testdata/stdlib/TestRaceFuncArgsRW/prog1.go"},
82 | {name: "TestNoRaceFuncArgsRW", testPath: "testdata/stdlib/TestNoRaceFuncArgsRW/prog1.go"},
83 | {name: "TestRaceAppendCapRW", testPath: "testdata/stdlib/TestRaceAppendCapRW/prog1.go"},
84 | {name: "TestRaceAppendLenRW", testPath: "testdata/stdlib/TestRaceAppendLenRW/prog1.go"},
85 | {name: "TestRaceAppendRW", testPath: "testdata/stdlib/TestRaceAppendRW/prog1.go"},
86 | //{name: "TestRacePanic", testPath: "testdata/stdlibNoSuccess/TestRacePanic/prog1.go"}, // cfg is weird because of the recover
87 | {name: "TestRaceFuncVariableWW", testPath: "testdata/stdlib/TestRaceFuncVariableWW/prog1.go"},
88 | {name: "TestRaceFuncVariableRW", testPath: "testdata/stdlib/TestRaceFuncVariableRW/prog1.go"},
89 | {name: "TestRaceUnsafePtrRW", testPath: "testdata/stdlib/TestRaceUnsafePtrRW/prog1.go"},
90 | {name: "TestRaceComplex128WW", testPath: "testdata/stdlib/TestRaceComplex128WW/prog1.go"},
91 | {name: "TestRaceFloat64WW", testPath: "testdata/stdlib/TestRaceFloat64WW/prog1.go"},
92 | {name: "TestRaceStringPtrRW", testPath: "testdata/stdlib/TestRaceStringPtrRW/prog1.go"},
93 | {name: "TestRaceStringRW", testPath: "testdata/stdlib/TestRaceStringRW/prog1.go"},
94 | {name: "TestRaceIntptrRW", testPath: "testdata/stdlib/TestRaceIntptrRW/prog1.go"},
95 | {name: "TestRaceError", testPath: "testdata/stdlib/TestRaceError/prog1.go"},
96 | {name: "TestRaceIfaceConv", testPath: "testdata/stdlib/TestRaceIfaceConv/prog1.go"},
97 | {name: "TestRaceEfaceConv", testPath: "testdata/stdlib/TestRaceEfaceConv/prog1.go"},
98 | {name: "TestRaceIfaceCmpNil", testPath: "testdata/stdlib/TestRaceIfaceCmpNil/prog1.go"},
99 | {name: "TestRaceIfaceCmp", testPath: "testdata/stdlib/TestRaceIfaceCmp/prog1.go"},
100 | {name: "TestRaceIfaceWW", testPath: "testdata/stdlib/TestRaceIfaceWW/prog1.go"}, // Before write, a read is performed. So the creation confused with the read later.
101 | {name: "TestRaceEfaceWW", testPath: "testdata/stdlib/TestRaceEfaceWW/prog1.go"},
102 | {name: "TestRaceStructFieldRW3", testPath: "testdata/stdlib/TestRaceStructFieldRW3/prog1.go"},
103 | {name: "TestRaceStructFieldRW2", testPath: "testdata/stdlib/TestRaceStructFieldRW2/prog1.go"},
104 | {name: "TestNoRaceStructFieldRW2", testPath: "testdata/stdlib/TestNoRaceStructFieldRW2/prog1.go"},
105 | {name: "TestNoRaceStructFieldRW1", testPath: "testdata/stdlib/TestNoRaceStructFieldRW1/prog1.go"},
106 | {name: "TestRaceStructFieldRW1", testPath: "testdata/stdlib/TestRaceStructFieldRW1/prog1.go"},
107 | {name: "TestRaceStructRW", testPath: "testdata/stdlibNoSuccess/TestRaceStructRW/prog1.go"}, // The compiler optimizes the ssa in a way that instead of allocating on line 16, the fields are modified. It means that according to ssa, there shouldn't be any data race since the end result is the same. Maybe a bug in ssa?
108 | {name: "TestRaceArrayCopy", testPath: "testdata/stdlib/TestRaceArrayCopy/prog1.go"},
109 | {name: "TestRaceSprint", testPath: "testdata/stdlib/TestRaceSprint/prog1.go"},
110 | {name: "TestRaceFuncArgument2", testPath: "testdata/stdlib/TestRaceFuncArgument2/prog1.go"},
111 | {name: "TestRaceFuncArgument", testPath: "testdata/stdlib/TestRaceFuncArgument/prog1.go"},
112 | {name: "TestNoRaceEnoughRegisters", testPath: "testdata/stdlib/TestNoRaceEnoughRegisters/prog1.go"},
113 | {name: "TestRaceRotate", testPath: "testdata/stdlib/TestRaceRotate/prog1.go"},
114 | {name: "TestRaceModConst", testPath: "testdata/stdlib/TestRaceModConst/prog1.go"},
115 | {name: "TestRaceMod", testPath: "testdata/stdlib/TestRaceMod/prog1.go"},
116 | {name: "TestRaceDivConst", testPath: "testdata/stdlib/TestRaceDivConst/prog1.go"},
117 | {name: "TestRaceDiv", testPath: "testdata/stdlib/TestRaceDiv/prog1.go"},
118 | {name: "TestRaceComplement", testPath: "testdata/stdlib/TestRaceComplement/prog1.go"},
119 | {name: "TestNoRacePlus", testPath: "testdata/stdlib/TestNoRacePlus/prog1.go"},
120 | {name: "TestRacePlus2", testPath: "testdata/stdlib/TestRacePlus2/prog1.go"},
121 | {name: "TestRacePlus", testPath: "testdata/stdlib/TestRacePlus/prog1.go"},
122 | {name: "TestRaceCaseTypeBody", testPath: "testdata/stdlib/TestRaceCaseTypeBody/prog1.go"},
123 | {name: "TestRaceCaseType", testPath: "testdata/stdlib/TestRaceCaseType/prog1.go"},
124 | //{name: "TestNoRaceCaseFallthrough", testPath: "testdata/stdlibNoSuccess/TestNoRaceCaseFallthrough/prog1.go"}, // No way to determine flow as the detector is flow insensitive
125 | {name: "TestRaceCaseBody", testPath: "testdata/stdlib/TestRaceCaseBody/prog1.go"},
126 | {name: "TestRaceCaseCondition2", testPath: "testdata/stdlib/TestRaceCaseCondition2/prog1.go"},
127 | {name: "TestRaceCaseCondition", testPath: "testdata/stdlib/TestRaceCaseCondition/prog1.go"},
128 | {name: "TestRaceInt32RWClosures", testPath: "testdata/stdlib/TestRaceInt32RWClosures/prog1.go"},
129 | {name: "TestNoRaceIntRWClosures", testPath: "testdata/stdlib/TestNoRaceIntRWClosures/prog1.go"},
130 | {name: "TestRaceIntRWClosures", testPath: "testdata/stdlib/TestRaceIntRWClosures/prog1.go"},
131 | {name: "TestRaceIntRWGlobalFuncs", testPath: "testdata/stdlib/TestRaceIntRWGlobalFuncs/prog1.go"},
132 | {name: "TestNoRaceComp", testPath: "testdata/stdlib/TestNoRaceComp/prog1.go"},
133 | {name: "TestRaceComp2", testPath: "testdata/stdlib/TestRaceComp2/prog1.go"},
134 | {name: "TestRaceSelect1", testPath: "testdata/stdlib/TestRaceSelect1/prog1.go"},
135 | {name: "TestRaceSelect2", testPath: "testdata/stdlib/TestRaceSelect2/prog1.go"},
136 | {name: "TestRaceSelect3", testPath: "testdata/stdlib/TestRaceSelect3/prog1.go"},
137 | {name: "TestRaceSelect4", testPath: "testdata/stdlib/TestRaceSelect4/prog1.go"},
138 | {name: "TestRaceSelect5", testPath: "testdata/stdlib/TestRaceSelect5/prog1.go"},
139 | //{name: "TestNoRaceSelect1", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect1/prog1.go"}, // All of the no race use syncing with channels
140 | //{name: "TestNoRaceSelect2", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect2/prog1.go"},
141 | //{name: "TestNoRaceSelect3", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect3/prog1.go"},
142 | //{name: "TestNoRaceSelect4", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect4/prog1.go"},
143 | //{name: "TestNoRaceSelect5", testPath: "testdata/stdlibNoSuccess/TestNoRaceSelect5/prog1.go"},
144 | {name: "TestRaceUnaddressableMapLen", testPath: "testdata/stdlib/TestRaceUnaddressableMapLen/prog1.go"},
145 | {name: "TestNoRaceCase", testPath: "testdata/stdlib/TestNoRaceCase/prog1.go"},
146 | {name: "TestNoRaceRangeIssue5446", testPath: "testdata/stdlib/TestNoRaceRangeIssue5446/prog1.go"},
147 | //{name: "TestRaceRange", testPath: "testdata/stdlibNoSuccess/TestRaceRange/prog1.go"}, // doesn't work. There's a race between v on line 11 from iter i+1 and 16/18 form iter i and it's not handled properly.
148 | {name: "TestRaceForInit", testPath: "testdata/stdlib/TestRaceForInit/prog1.go"},
149 | //{name: "TestNoRaceForInit", testPath: "testdata/stdlibNoSuccess/TestNoRaceForInit/prog1.go"}, // flow analysis required
150 | {name: "TestRaceForTest", testPath: "testdata/stdlib/TestRaceForTest/prog1.go"},
151 | {name: "TestRaceForIncr", testPath: "testdata/stdlib/TestRaceForIncr/prog1.go"},
152 | {name: "TestNoRaceForIncr", testPath: "testdata/stdlibNoSuccess/TestNoRaceForIncr/prog1.go"}, // flow analysis required
153 | {name: "TestNoRaceHeapReallocation", testPath: "testdata/stdlib/TestNoRaceHeapReallocation/prog1.go"},
154 | //{name: "TestRaceIssue5567", testPath: "testdata/stdlib/TestRaceIssue5567/prog1.go"}, // There's write inside f.Read on b which is an external package
155 | //{name: "TestRaceIssue5654", testPath: "testdata/stdlib/TestRaceIssue5654/prog1.go"}, // PackageProblem
156 | {name: "TestNoRaceTinyAlloc", testPath: "testdata/stdlib/TestNoRaceTinyAlloc/prog1.go"},
157 | }
158 | _, ex, _, ok := runtime.Caller(0)
159 | require.True(t, ok)
160 | modulePath := filepath.Dir(filepath.Dir(ex))
161 | for _, tc := range testCases {
162 | t.Run(tc.name, func(t *testing.T) {
163 |
164 | ssaProg, ssaPkg, err := ssaUtils.LoadPackage(tc.testPath, modulePath)
165 | require.NoError(t, err)
166 |
167 | domain.GoroutineCounter = utils.NewCounter()
168 | domain.GuardedAccessCounter = utils.NewCounter()
169 | domain.PosIDCounter = utils.NewCounter()
170 |
171 | entryFunc := ssaPkg.Func("main")
172 | err = ssaUtils.InitPreProcess(ssaProg, modulePath)
173 | require.NoError(t, err)
174 |
175 | entryCallCommon := ssa.CallCommon{Value: entryFunc}
176 | functionState := ssaUtils.HandleCallCommon(domain.NewEmptyContext(), &entryCallCommon, entryFunc.Pos())
177 | conflictingGAs, err := pointerAnalysis.Analysis(ssaPkg, functionState.GuardedAccesses)
178 | if err != nil {
179 | fmt.Printf("Error in analysis:%s\n", err)
180 | os.Exit(1)
181 | }
182 | err = output.GenerateError(conflictingGAs, ssaProg)
183 | if err != nil {
184 | fmt.Printf("Error in generating errors:%s\n", err)
185 | os.Exit(1)
186 | }
187 | })
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/RaceNestedArrayCopy/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | ch := make(chan bool, 1)
9 | type (
10 | Point32 [2][2][2][2][2]Point
11 | Point1024 [2][2][2][2][2]Point32
12 | Point32k [2][2][2][2][2]Point1024
13 | Point1M [2][2][2][2][2]Point32k
14 | )
15 | var a, b Point1M
16 | go func() {
17 | a[0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1][0][1].y = 1
18 | ch <- true
19 | }()
20 | a = b
21 | <-ch
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceAddrExpr/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | x := 0
6 | go func() {
7 | x = 42
8 | c <- true
9 | }()
10 | _ = &x
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceAsFunc4/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | func main() {
6 | c := make(chan bool, 1)
7 | var mu sync.Mutex
8 | x := 0
9 | _ = x
10 | go func() {
11 | x = func() int { // Write of x must be under the mutex.
12 | mu.Lock()
13 | return 42
14 | }()
15 | mu.Unlock()
16 | c <- true
17 | }()
18 | mu.Lock()
19 | x = 42
20 | mu.Unlock()
21 | <-c
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceCase/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | var y int
9 | for x := -1; x <= 1; x++ {
10 | switch {
11 | case x < 0:
12 | y = -1
13 | case x == 0:
14 | y = 0
15 | case x > 0:
16 | y = 1
17 | }
18 | }
19 | y++
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceComp/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type P struct {
4 | x, y int
5 | }
6 |
7 | type S struct {
8 | s1, s2 P
9 | }
10 |
11 | func main() {
12 | c := make(chan bool, 1)
13 | var s S
14 | go func() {
15 | s.s2.x = 1
16 | c <- true
17 | }()
18 | s.s2.y = 2
19 | <-c
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceEnoughRegisters/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | // from erf.go
5 | const (
6 | sa1 = 1
7 | sa2 = 2
8 | sa3 = 3
9 | sa4 = 4
10 | sa5 = 5
11 | sa6 = 6
12 | sa7 = 7
13 | sa8 = 8
14 | )
15 | var s, S float64
16 | s = 3.1415
17 | S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
18 | s = S
19 | }
20 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceFuncArgsRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | ch := make(chan byte, 1)
9 | var x byte
10 | go func(y byte) {
11 | _ = y
12 | ch <- 0
13 | }(x)
14 | x = 1
15 | <-ch
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceHeapReallocation/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "runtime"
5 | "time"
6 | )
7 |
8 | func main() {
9 | // It is possible that a future implementation
10 | // of memory allocation will ruin this test.
11 | // Increasing n might help in this case, so
12 | // this test is a bit more generic than most of the
13 | // others.
14 | const n = 2
15 | done := make(chan bool, n)
16 | empty := func(p *int) {}
17 | for i := 0; i < n; i++ {
18 | ms := i
19 | go func() {
20 | <-time.After(time.Duration(ms) * time.Millisecond)
21 | runtime.GC()
22 | var x int
23 | empty(&x) // x goes to the heap
24 | done <- true
25 | }()
26 | }
27 | for i := 0; i < n; i++ {
28 | <-done
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceIntRWClosures/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int
5 | _ = y
6 | ch := make(chan int, 1)
7 |
8 | go func() {
9 | y = x
10 | ch <- 1
11 | }()
12 | <-ch
13 | go func() {
14 | x = 1
15 | ch <- 1
16 | }()
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceMethodThunk/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Base int
4 |
5 | func (b *Base) Foo() int {
6 | return 42
7 | }
8 |
9 | func (b Base) Bar() int {
10 | return int(b)
11 | }
12 |
13 | func main() {
14 | type Derived struct {
15 | pad int
16 | *Base
17 | }
18 | var d Derived
19 | done := make(chan bool)
20 | go func() {
21 | _ = d.Foo()
22 | done <- true
23 | }()
24 | d = Derived{}
25 | <-done
26 | }
27 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceMethodValue/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | i := InterImpl2{}
26 | go func() {
27 | i = InterImpl2{}
28 | c <- true
29 | }()
30 | _ = i.Foo // takes the address of i only.
31 | <-c
32 | }
33 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRacePlus/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = x
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = y / (z + 1)
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceRangeIssue5446/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | ch := make(chan int, 3)
5 | a := []int{1, 2, 3}
6 | b := []int{4}
7 | // used to insert a spurious instrumentation of a[i]
8 | // and crash.
9 | i := 1
10 | for i, a[i] = range b {
11 | ch <- i
12 | }
13 | close(ch)
14 | }
15 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceSelect4/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | type Task struct {
5 | f func()
6 | done chan bool
7 | }
8 |
9 | queue := make(chan Task)
10 | dummy := make(chan bool)
11 |
12 | go func() {
13 | for true {
14 | select {
15 | case t := <-queue:
16 | t.f()
17 | t.done <- true
18 | }
19 | }
20 | }()
21 |
22 | doit := func(f func()) {
23 | done := make(chan bool, 1)
24 | select {
25 | case queue <- Task{f, done}:
26 | case <-dummy:
27 | }
28 | select {
29 | case <-done:
30 | case <-dummy:
31 | }
32 | }
33 |
34 | var x int
35 | doit(func() {
36 | x = 1
37 | })
38 | _ = x
39 | }
40 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceStackPushPop/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type stack []int
4 |
5 | func (s *stack) push(x int) {
6 | *s = append(*s, x)
7 | }
8 |
9 | func (s *stack) pop() int {
10 | i := len(*s)
11 | n := (*s)[i-1]
12 | *s = (*s)[:i-1]
13 | return n
14 | }
15 |
16 | func main() {
17 | var s stack
18 | go func(s *stack) {}(&s)
19 | s.push(1)
20 | x := s.pop()
21 | _ = x
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceStructFieldRW1/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | type NamedPoint struct {
8 | name string
9 | p Point
10 | }
11 |
12 | func main() {
13 | // Same struct, different variables, no
14 | // pointers. The layout is known (at compile time?) ->
15 | // no read on p
16 | // writes on x and y
17 | p := Point{0, 0}
18 | ch := make(chan bool, 1)
19 | go func() {
20 | p.x = 1
21 | ch <- true
22 | }()
23 | p.y = 1
24 | <-ch
25 | _ = p
26 | }
27 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceStructFieldRW2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | type NamedPoint struct {
8 | name string
9 | p Point
10 | }
11 |
12 | func main() {
13 | // Same as NoRaceStructFieldRW1
14 | // but p is a pointer, so there is a read on p
15 | p := Point{0, 0}
16 | ch := make(chan bool, 1)
17 | go func() {
18 | p.x = 1
19 | ch <- true
20 | }()
21 | p.y = 1
22 | <-ch
23 | _ = p
24 | }
25 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestNoRaceTinyAlloc/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | const P = 4
5 | const N = 1e6
6 | var tinySink *byte
7 | _ = tinySink
8 | done := make(chan bool)
9 | for p := 0; p < P; p++ {
10 | go func() {
11 | for i := 0; i < N; i++ {
12 | var b byte
13 | if b != 0 {
14 | tinySink = &b // make it heap allocated
15 | }
16 | b = 42
17 | }
18 | done <- true
19 | }()
20 | }
21 | for p := 0; p < P; p++ {
22 | <-done
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAddrExpr/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type AddrT struct {
4 | _ [256]byte
5 | x int
6 | }
7 |
8 | type AddrT2 struct {
9 | _ [512]byte
10 | p *AddrT
11 | }
12 |
13 | func main() {
14 | c := make(chan bool, 1)
15 | a := AddrT2{p: &AddrT{x: 42}}
16 | go func() {
17 | a.p = &AddrT{x: 43}
18 | c <- true
19 | }()
20 | _ = &a.p.x
21 | <-c
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAnd/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | x = 1
8 | c <- true
9 | }()
10 | if x == 1 && y == 1 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAnd2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | x = 1
8 | c <- true
9 | }()
10 | if y == 0 && x == 1 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAppendCapRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | a := make([]int, 0)
9 | ch := make(chan string)
10 | go func() {
11 | a = append(a, 1)
12 | ch <- ""
13 | }()
14 | _ = cap(a)
15 | <-ch
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAppendLenRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | a := make([]int, 0)
9 | ch := make(chan bool)
10 | go func() {
11 | a = append(a, 1)
12 | ch <- true
13 | }()
14 | _ = len(a)
15 | <-ch
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAppendRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | a := make([]int, 10)
9 | ch := make(chan bool)
10 | go func() {
11 | _ = append(a, 1)
12 | ch <- true
13 | }()
14 | a[0] = 1
15 | <-ch
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceArrayCopy/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | ch := make(chan bool, 1)
5 | var a [5]int
6 | go func() {
7 | a[3] = 1
8 | ch <- true
9 | }()
10 | a = [5]int{1, 2, 3, 4, 5}
11 | <-ch
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceArrayInit/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | y := 0
6 | go func() {
7 | y = 42
8 | c <- true
9 | }()
10 | x := []int{0, y, 42}
11 | _ = x
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAsFunc1/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var s []byte
5 | c := make(chan bool, 1)
6 | go func() {
7 | var err error
8 | s, err = func() ([]byte, error) {
9 | t := []byte("hello world")
10 | return t, nil
11 | }()
12 | c <- true
13 | _ = err
14 | }()
15 | _ = string(s)
16 | <-c
17 | }
18 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAsFunc2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | x := 0
6 | go func() {
7 | func(x int) {
8 | }(x)
9 | c <- true
10 | }()
11 | x = 42
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceAsFunc3/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | func main() {
6 | c := make(chan bool, 1)
7 | var mu sync.Mutex
8 | x := 0
9 | go func() {
10 | func(x int) {
11 | mu.Lock()
12 | }(x) // Read of x must be outside of the mutex.
13 | mu.Unlock()
14 | c <- true
15 | }()
16 | mu.Lock()
17 | x = 42
18 | mu.Unlock()
19 | <-c
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceBlockAs/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | var x, y int
6 | go func() {
7 | x = 42
8 | c <- true
9 | }()
10 | x, y = y, x
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseBody/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int
5 | _ = y
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | y = x
10 | ch <- 1
11 | }()
12 | go func() {
13 | switch {
14 | default:
15 | x = 1
16 | case x == 100:
17 | x = -x
18 | }
19 | ch <- 1
20 | }()
21 | <-ch
22 | <-ch
23 | }
24 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseCondition/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x int = 0
5 | ch := make(chan int, 2)
6 |
7 | go func() {
8 | x = 2
9 | ch <- 1
10 | }()
11 | go func() {
12 | switch x < 2 {
13 | case true:
14 | x = 1
15 | //case false:
16 | // x = 5
17 | }
18 | ch <- 1
19 | }()
20 | <-ch
21 | <-ch
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseCondition2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | // switch body is rearranged by the compiler so the tests
5 | // passes even if we don't instrument '<'
6 | var x int = 0
7 | ch := make(chan int, 2)
8 |
9 | go func() {
10 | x = 2
11 | ch <- 1
12 | }()
13 | go func() {
14 | switch x < 2 {
15 | case true:
16 | x = 1
17 | case false:
18 | x = 5
19 | }
20 | ch <- 1
21 | }()
22 | <-ch
23 | <-ch
24 | }
25 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseFallthrough/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = y
6 | ch := make(chan int, 2)
7 | z = 1
8 |
9 | go func() {
10 | y = x
11 | ch <- 1
12 | }()
13 | go func() {
14 | switch {
15 | case z == 1:
16 | fallthrough
17 | case z == 2:
18 | x = 2
19 | }
20 | ch <- 1
21 | }()
22 |
23 | <-ch
24 | <-ch
25 | }
26 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseIssue6418/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | m := map[string]map[string]string{
5 | "a": {
6 | "b": "c",
7 | },
8 | }
9 | ch := make(chan int)
10 | go func() {
11 | m["a"]["x"] = "y"
12 | ch <- 1
13 | }()
14 | switch m["a"]["b"] {
15 | }
16 | <-ch
17 | }
18 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseType/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int
5 | var i interface{} = x
6 | c := make(chan int, 1)
7 | go func() {
8 | switch i.(type) {
9 | case nil:
10 | case int:
11 | }
12 | c <- 1
13 | }()
14 | i = y
15 | <-c
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseTypeBody/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int
5 | var i interface{} = &x
6 | c := make(chan int, 1)
7 | go func() {
8 | switch i := i.(type) {
9 | case nil:
10 | case *int:
11 | *i = y
12 | }
13 | c <- 1
14 | }()
15 | x = y
16 | <-c
17 | }
18 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceCaseTypeIssue5890/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int
5 | m := make(map[int]map[int]interface{})
6 | m[0] = make(map[int]interface{})
7 | c := make(chan int, 1)
8 | go func() {
9 | switch i := m[0][1].(type) {
10 | case nil:
11 | case *int:
12 | *i = x
13 | }
14 | c <- 1
15 | }()
16 | m[0][1] = y
17 | <-c
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceComp2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type P struct {
4 | x, y int
5 | }
6 |
7 | type S struct {
8 | s1, s2 P
9 | }
10 |
11 | func main() {
12 | c := make(chan bool, 1)
13 | var s S
14 | go func() {
15 | s.s1.x = 1
16 | c <- true
17 | }()
18 | s = S{}
19 | <-c
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceComplement/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = x
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = ^y
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = ^z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceComplex128WW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y complex128
5 | ch := make(chan bool, 1)
6 | go func() {
7 | x = 2 + 2i
8 | ch <- true
9 | }()
10 | x = 4 + 4i
11 | <-ch
12 |
13 | y = x
14 | x = y
15 | }
16 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceDeferArg/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type DeferT int
4 |
5 | func (d DeferT) Foo() {
6 | }
7 |
8 | func main() {
9 | c := make(chan bool, 1)
10 | x := 0
11 | go func() {
12 | x = 42
13 | c <- true
14 | }()
15 | func() {
16 | defer func(x int) {
17 | }(x)
18 | }()
19 | <-c
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceDeferArg2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type DeferT int
4 |
5 | func (d DeferT) Foo() {
6 | }
7 |
8 | func main() {
9 | c := make(chan bool, 1)
10 | var x DeferT
11 | go func() {
12 | var y DeferT
13 | x = y
14 | c <- true
15 | }()
16 | func() {
17 | defer x.Foo()
18 | }()
19 | <-c
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceDiv/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = x
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = y / (z + 1)
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceDivConst/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z uint32
5 | _ = x
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = y / 3 // involves only a HMUL node
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceEfaceConv/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | v := 0
6 | go func() {
7 | go func(x interface{}) {
8 | }(v)
9 | c <- true
10 | }()
11 | v = 42
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceEfaceWW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type DummyWriter struct {
4 | state int
5 | }
6 | type Writer interface {
7 | Write(p []byte) (n int)
8 | }
9 |
10 | func (d DummyWriter) Write(p []byte) (n int) {
11 | return 0
12 | }
13 |
14 | func main() {
15 | var a, b interface{}
16 | ch := make(chan bool, 1)
17 | go func() {
18 | a = 1
19 | ch <- true
20 | }()
21 | a = 2
22 | <-ch
23 | _, _ = a, b
24 | }
25 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceEmptyInterface/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | var x interface{}
6 | go func() {
7 | x = nil
8 | c <- true
9 | }()
10 | _ = x
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceEmptyInterface2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | type Point struct {
6 | x, y int
7 | }
8 |
9 | var mutex sync.Mutex
10 |
11 | func main() {
12 | c := make(chan bool)
13 | var x interface{}
14 | go func() {
15 | x = &Point{}
16 | c <- true
17 | }()
18 | mutex.Lock()
19 | _ = x
20 | <-c
21 | }
22 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceError/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | ch := make(chan bool, 1)
5 | var err error
6 | go func() {
7 | err = nil
8 | ch <- true
9 | }()
10 | _ = err
11 | <-ch
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFloat64WW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y float64
5 | ch := make(chan bool, 1)
6 | go func() {
7 | x = 1.0
8 | ch <- true
9 | }()
10 | x = 2.0
11 | <-ch
12 |
13 | y = x
14 | x = y
15 | }
16 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceForIncr/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | done := make(chan bool)
5 | c := make(chan bool)
6 | x := 0
7 | go func() {
8 | for {
9 | _, ok := <-c
10 | if !ok {
11 | done <- true
12 | return
13 | }
14 | x++
15 | }
16 | }()
17 | for i := 0; i < 10; x++ {
18 | i++
19 | c <- true
20 | }
21 | close(c)
22 | <-done
23 | }
24 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceForInit/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan int)
5 | x := 0
6 | go func() {
7 | c <- x
8 | }()
9 | for x = 42; false; {
10 | }
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceForTest/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | done := make(chan bool)
5 | c := make(chan bool)
6 | stop := false
7 | go func() {
8 | for {
9 | _, ok := <-c
10 | if !ok {
11 | done <- true
12 | return
13 | }
14 | stop = true
15 | }
16 | }()
17 | for !stop {
18 | c <- true
19 | }
20 | close(c)
21 | <-done
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFuncArgsRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | ch := make(chan byte, 1)
9 | var x byte
10 | go func(y *byte) {
11 | _ = *y
12 | ch <- 0
13 | }(&x)
14 | x = 1
15 | <-ch
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFuncArgument/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | // emptyFunc should not be inlined.
6 | func emptyFunc(x int) {
7 | if false {
8 | fmt.Println(x)
9 | }
10 | }
11 |
12 | func main() {
13 | var x int
14 | ch := make(chan bool, 1)
15 | go func() {
16 | emptyFunc(x)
17 | ch <- true
18 | }()
19 | x = 1
20 | <-ch
21 | }
22 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFuncArgument2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x int
5 | ch := make(chan bool, 2)
6 | go func() {
7 | x = 42
8 | ch <- true
9 | }()
10 | go func(y int) {
11 | ch <- true
12 | }(x)
13 | <-ch
14 | <-ch
15 | }
16 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFuncCall/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | f := func(x, y int) {}
26 | x, y := 0, 0
27 | go func() {
28 | y = 42
29 | c <- true
30 | }()
31 | f(x, y)
32 | <-c
33 | }
34 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFuncItself/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | f := func() {}
6 | go func() {
7 | f()
8 | c <- true
9 | }()
10 | f = func() {}
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFuncVariableRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var f func(x int) int
5 | f = func(x int) int {
6 | return x * x
7 | }
8 | ch := make(chan bool, 1)
9 | go func() {
10 | f = func(x int) int {
11 | return x
12 | }
13 | ch <- true
14 | }()
15 | y := f(1)
16 | <-ch
17 | x := y
18 | y = x
19 | <-ch
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceFuncVariableWW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var f func(x int) int
5 | _ = f
6 | ch := make(chan bool, 1)
7 | go func() {
8 | f = func(x int) int {
9 | return x
10 | }
11 | ch <- true
12 | }()
13 | f = func(x int) int {
14 | return x * x
15 | }
16 | <-ch
17 | }
18 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIfaceCmp/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type DummyWriter struct {
4 | state int
5 | }
6 | type Writer interface {
7 | Write(p []byte) (n int)
8 | }
9 |
10 | func (d DummyWriter) Write(p []byte) (n int) {
11 | return 0
12 | }
13 |
14 | func main() {
15 | var a, b Writer
16 | a = DummyWriter{1}
17 | ch := make(chan bool, 1)
18 | go func() {
19 | a = DummyWriter{1}
20 | ch <- true
21 | }()
22 | _ = a == b
23 | <-ch
24 | }
25 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIfaceCmpNil/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type DummyWriter struct {
4 | state int
5 | }
6 | type Writer interface {
7 | Write(p []byte) (n int)
8 | }
9 |
10 | func (d DummyWriter) Write(p []byte) (n int) {
11 | return 0
12 | }
13 |
14 | func main() {
15 | var a Writer
16 | a = DummyWriter{1}
17 | ch := make(chan bool, 1)
18 | go func() {
19 | a = DummyWriter{1}
20 | ch <- true
21 | }()
22 | _ = a == nil
23 | <-ch
24 | }
25 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIfaceConv/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type OsFile struct{}
4 |
5 | func (*OsFile) Read() {
6 | }
7 |
8 | type IoReader interface {
9 | Read()
10 | }
11 |
12 | func main() {
13 | c := make(chan bool)
14 | f := &OsFile{}
15 | go func() {
16 | go func(x IoReader) {
17 | }(f)
18 | c <- true
19 | }()
20 | f = &OsFile{}
21 | <-c
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIfaceWW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type DummyWriter struct {
4 | state int
5 | }
6 | type Writer interface {
7 | Write(p []byte) (n int)
8 | }
9 |
10 | func (d DummyWriter) Write(p []byte) (n int) {
11 | return 0
12 | }
13 |
14 | func main() {
15 | var a, b Writer
16 | ch := make(chan bool, 1)
17 | go func() {
18 | a = DummyWriter{1}
19 | ch <- true
20 | }()
21 | a = DummyWriter{2}
22 | <-ch
23 | b = a
24 | a = b
25 | }
26 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceInt32RWClosures/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int32
5 | _ = y
6 | ch := make(chan bool, 2)
7 |
8 | go func() {
9 | y = x
10 | ch <- true
11 | }()
12 | go func() {
13 | x = 1
14 | ch <- true
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIntRWClosures/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int
5 | _ = y
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | y = x
10 | ch <- 1
11 | }()
12 | go func() {
13 | x = 1
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIntRWGlobalFuncs/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | var GlobalX, GlobalY int = 0, 0
4 | var GlobalCh chan int = make(chan int, 2)
5 |
6 | func GlobalFunc1() {
7 | GlobalY = GlobalX
8 | GlobalCh <- 1
9 | }
10 |
11 | func GlobalFunc2() {
12 | GlobalX = 1
13 | GlobalCh <- 1
14 | }
15 |
16 | func main() {
17 | go GlobalFunc1()
18 | go GlobalFunc2()
19 | <-GlobalCh
20 | <-GlobalCh
21 | }
22 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceInterCall/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | p := InterImpl{}
26 | var x Inter = p
27 | go func() {
28 | p2 := InterImpl{}
29 | x = p2
30 | c <- true
31 | }()
32 | x.Foo(0)
33 | <-c
34 | }
35 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceInterCall2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | p := InterImpl{}
26 | var x Inter = p
27 | z := 0
28 | go func() {
29 | z = 42
30 | c <- true
31 | }()
32 | x.Foo(z)
33 | <-c
34 | }
35 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIntptrRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y int
5 | var p *int = &x
6 | ch := make(chan bool, 1)
7 | go func() {
8 | *p = 5
9 | ch <- true
10 | }()
11 | y = *p
12 | x = y
13 | <-ch
14 | }
15 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIssue5564/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | )
6 |
7 | func main() {
8 | text := `Friends, Romans, countrymen, lend me your ears;
9 | I come to bury Caesar, not to praise him.
10 | The evil that men do lives after them;
11 | The good is oft interred with their bones;
12 | So let it be with Caesar. The noble Brutus
13 | Hath told you Caesar was ambitious:
14 | If it were so, it was a grievous fault,
15 | And grievously hath Caesar answer'd it.
16 | Here, under leave of Brutus and the rest -
17 | For Brutus is an honourable man;
18 | So are they all, all honourable men -
19 | Come I to speak in Caesar's funeral.
20 | He was my friend, faithful and just to me:
21 | But Brutus says he was ambitious;
22 | And Brutus is an honourable man.`
23 |
24 | data := bytes.NewBufferString(text)
25 | in := make(chan []byte)
26 |
27 | go func() {
28 | buf := make([]byte, 16)
29 | var n int
30 | var err error
31 | for ; err == nil; n, err = data.Read(buf) {
32 | in <- buf[:n]
33 | }
34 | close(in)
35 | }()
36 | res := ""
37 | for s := range in {
38 | res += string(s)
39 | }
40 | _ = res
41 | }
42 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceIssue5567/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/sha1"
5 | "io"
6 | "os"
7 | "runtime"
8 | )
9 |
10 | func main() {
11 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
12 | in := make(chan []byte)
13 | res := make(chan error)
14 | go func() {
15 | var err error
16 | defer func() {
17 | close(in)
18 | res <- err
19 | }()
20 | path := "mop_test.go"
21 | f, err := os.Open(path)
22 | if err != nil {
23 | return
24 | }
25 | defer f.Close()
26 | var n, total int
27 | b := make([]byte, 17) // the race is on b buffer
28 | for err == nil {
29 | n, err = f.Read(b)
30 | total += n
31 | if n > 0 {
32 | in <- b[:n]
33 | }
34 | }
35 | if err == io.EOF {
36 | err = nil
37 | }
38 | }()
39 | h := sha1.New()
40 | for b := range in {
41 | h.Write(b)
42 | }
43 | _ = h.Sum(nil)
44 | err := <-res
45 | if err != nil {
46 | os.Exit(1)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMapInit/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | y := 0
6 | go func() {
7 | y = 42
8 | c <- true
9 | }()
10 | x := map[int]int{0: 42, y: 42}
11 | _ = x
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMapInit2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | y := 0
6 | go func() {
7 | y = 42
8 | c <- true
9 | }()
10 | x := map[int]int{0: 42, 42: y}
11 | _ = x
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMethodCall/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | i := InterImpl{}
26 | x := 0
27 | go func() {
28 | x = 42
29 | c <- true
30 | }()
31 | i.Foo(x)
32 | <-c
33 | }
34 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMethodCall2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | i := &InterImpl{}
26 | go func() {
27 | i = &InterImpl{}
28 | c <- true
29 | }()
30 | i.Foo(0)
31 | <-c
32 | }
33 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMethodThunk2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Base int
4 |
5 | func (b *Base) Foo() int {
6 | return 42
7 | }
8 |
9 | func (b Base) Bar() int {
10 | return int(b)
11 | }
12 |
13 | func main() {
14 | type Derived struct {
15 | pad int
16 | Base
17 | }
18 | var d Derived
19 | done := make(chan bool)
20 | go func() {
21 | _ = d.Bar()
22 | done <- true
23 | }()
24 | d = Derived{}
25 | <-done
26 | }
27 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMethodThunk3/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Base int
4 |
5 | func (b *Base) Foo() int {
6 | return 42
7 | }
8 |
9 | func (b Base) Bar() int {
10 | return int(b)
11 | }
12 |
13 | func main() {
14 | type Derived struct {
15 | pad int
16 | *Base
17 | }
18 | var d Derived
19 | d.Base = new(Base)
20 | done := make(chan bool)
21 | go func() {
22 | _ = d.Bar()
23 | done <- true
24 | }()
25 | d.Base = new(Base)
26 | <-done
27 | }
28 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMethodValue/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | i := InterImpl{}
26 | go func() {
27 | i = InterImpl{}
28 | c <- true
29 | }()
30 | _ = i.Foo
31 | <-c
32 | }
33 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMethodValue2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | var i Inter = InterImpl{}
26 | go func() {
27 | i = InterImpl{}
28 | c <- true
29 | }()
30 | _ = i.Foo
31 | <-c
32 | }
33 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMethodValue3/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Inter interface {
4 | Foo(x int)
5 | }
6 | type InterImpl struct {
7 | x, y int
8 | }
9 |
10 | //go:noinline
11 | func (p InterImpl) Foo(x int) {
12 | }
13 |
14 | type InterImpl2 InterImpl
15 |
16 | func (p *InterImpl2) Foo(x int) {
17 | if p == nil {
18 | InterImpl{}.Foo(x)
19 | }
20 | InterImpl(*p).Foo(x)
21 | }
22 |
23 | func main() {
24 | c := make(chan bool, 1)
25 | i := &InterImpl{}
26 | go func() {
27 | *i = InterImpl{}
28 | c <- true
29 | }()
30 | _ = i.Foo // dereferences i.
31 | <-c
32 | }
33 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceMod/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = x
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = y % (z + 1)
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceModConst/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = x
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = y % 3
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceNestedStruct/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | type X struct {
5 | x, y int
6 | }
7 | type Y struct {
8 | x X
9 | }
10 | c := make(chan Y)
11 | var t Y
12 | go func() {
13 | c <- t
14 | }()
15 | //time.Sleep(2*time.Second)
16 | t.x.y = 42
17 | val := <-c
18 | _ = val
19 | }
20 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceOr/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | x = 1
8 | c <- true
9 | }()
10 | if x == 1 || y == 1 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceOr2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | x = 1
8 | c <- true
9 | }()
10 | if x == 1 || y == 1 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRacePanicArg/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "errors"
4 |
5 | func main() {
6 | c := make(chan bool, 1)
7 | err := errors.New("err")
8 | go func() {
9 | err = errors.New("err2")
10 | c <- true
11 | }()
12 | defer func() {
13 | recover()
14 | <-c
15 | }()
16 | panic(err)
17 | }
18 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRacePlus/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = y
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | y = x + z
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = x + z + z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRacePlus2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = y
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = 1
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = +x + z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceRotate/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z uint32
5 | _ = x
6 | ch := make(chan int, 2)
7 |
8 | go func() {
9 | x = y<<12 | y>>20
10 | ch <- 1
11 | }()
12 | go func() {
13 | y = z
14 | ch <- 1
15 | }()
16 | <-ch
17 | <-ch
18 | }
19 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceRune/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | var x rune
6 | go func() {
7 | x = 1
8 | c <- true
9 | }()
10 | _ = x
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSelect1/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x int
5 | _ = x
6 | compl := make(chan bool, 2)
7 | c := make(chan bool)
8 | c1 := make(chan bool)
9 |
10 | go func() {
11 | <-c
12 | <-c
13 | }()
14 | f := func() {
15 | select {
16 | case c <- true:
17 | case c1 <- true:
18 | }
19 | x = 1
20 | compl <- true
21 | }
22 | go f()
23 | go f()
24 | <-compl
25 | <-compl
26 | }
27 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSelect2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x int
5 | _ = x
6 | compl := make(chan bool)
7 | c := make(chan bool)
8 | c1 := make(chan bool)
9 | go func() {
10 | x = 1
11 | select {
12 | case <-c:
13 | case <-c1:
14 | }
15 | compl <- true
16 | }()
17 | close(c)
18 | x = 2
19 | <-compl
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSelect3/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x int
5 | _ = x
6 | compl := make(chan bool)
7 | c := make(chan bool)
8 | c1 := make(chan bool)
9 | go func() {
10 | x = 1
11 | select {
12 | case c <- true:
13 | case c1 <- true:
14 | }
15 | compl <- true
16 | }()
17 | x = 2
18 | select {
19 | case <-c:
20 | }
21 | <-compl
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSelect4/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | done := make(chan bool, 1)
5 | var x int
6 | go func() {
7 | select {
8 | default:
9 | x = 2
10 | }
11 | done <- true
12 | }()
13 | _ = x
14 | <-done
15 | }
16 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSelect5/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | done := make(chan bool, 1)
5 | c1 := make(chan bool, 1)
6 | c2 := make(chan bool)
7 | var x, y int
8 | go func() {
9 | select {
10 | case c1 <- true:
11 | x = 1
12 | case c2 <- true:
13 | y = 1
14 | }
15 | done <- true
16 | }()
17 | _ = x
18 | _ = y
19 | <-done
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSliceSlice/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | x := make([]int, 10)
6 | go func() {
7 | x = make([]int, 20)
8 | c <- true
9 | }()
10 | _ = x[2:3]
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSliceSlice2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | x := make([]int, 10)
6 | i := 2
7 | go func() {
8 | i = 3
9 | c <- true
10 | }()
11 | _ = x[i:4]
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSliceString/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | x := "hello"
6 | go func() {
7 | x = "world"
8 | c <- true
9 | }()
10 | _ = x[2:3]
11 | <-c
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceSprint/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | var x int
7 | ch := make(chan bool, 1)
8 | go func() {
9 | fmt.Sprint(x)
10 | ch <- true
11 | }()
12 | x = 1
13 | <-ch
14 | }
15 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceStringPtrRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | ch := make(chan bool, 1)
5 | var x string
6 | p := &x
7 | go func() {
8 | *p = "a"
9 | ch <- true
10 | }()
11 | _ = *p
12 | <-ch
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceStringRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | ch := make(chan bool, 1)
5 | s := ""
6 | go func() {
7 | s = "abacaba"
8 | ch <- true
9 | }()
10 | _ = s
11 | <-ch
12 | }
13 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceStructFieldRW1/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | type NamedPoint struct {
8 | name string
9 | p Point
10 | }
11 |
12 | func main() {
13 | p := Point{0, 0}
14 | ch := make(chan bool, 1)
15 | go func() {
16 | p.x = 1
17 | ch <- true
18 | }()
19 | _ = p.x
20 | <-ch
21 | }
22 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceStructFieldRW2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | p := &Point{0, 0}
9 | ch := make(chan bool, 1)
10 | go func() {
11 | p.x = 1
12 | ch <- true
13 | }()
14 | _ = p.x
15 | <-ch
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceStructFieldRW3/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | type NamedPoint struct {
8 | name string
9 | p Point
10 | }
11 |
12 | func main() {
13 | p := NamedPoint{name: "a", p: Point{0, 0}}
14 | ch := make(chan bool, 1)
15 | go func() {
16 | p.p.x = 1
17 | ch <- true
18 | }()
19 | _ = p.p.x
20 | <-ch
21 | }
22 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceStructInd/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | type Item struct {
6 | x, y int
7 | }
8 | i := Item{}
9 | go func(p *Item) {
10 | *p = Item{}
11 | c <- true
12 | }(&i)
13 | i.y = 42
14 | <-c
15 | }
16 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceStructInit/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | type X struct {
5 | x, y int
6 | }
7 | c := make(chan bool, 1)
8 | y := 0
9 | go func() {
10 | y = 42
11 | c <- true
12 | }()
13 | x := X{x: y}
14 | _ = x
15 | <-c
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceTLS/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | comm := make(chan *int)
5 | done := make(chan bool, 2)
6 | go func() {
7 | var x int
8 | comm <- &x
9 | x = 1
10 | x = *(<-comm)
11 | done <- true
12 | }()
13 | go func() {
14 | p := <-comm
15 | *p = 2
16 | comm <- p
17 | done <- true
18 | }()
19 | <-done
20 | <-done
21 | }
22 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceTypeAssert/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool, 1)
5 | x := 0
6 | var i interface{} = x
7 | go func() {
8 | y := 0
9 | i = y
10 | c <- true
11 | }()
12 | _ = i.(int)
13 | <-c
14 | }
15 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceUnaddressableMapLen/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | m := make(map[int]map[int]int)
5 | ch := make(chan int, 1)
6 | m[0] = make(map[int]int)
7 | go func() {
8 | _ = len(m[0])
9 | ch <- 0
10 | }()
11 | m[0][0] = 1
12 | <-ch
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlib/TestRaceUnsafePtrRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "unsafe"
4 |
5 | func main() {
6 | var x, y, z int
7 | x, y, z = 1, 2, 3
8 | var p unsafe.Pointer = unsafe.Pointer(&x)
9 | ch := make(chan bool, 1)
10 | go func() {
11 | p = (unsafe.Pointer)(&z)
12 | ch <- true
13 | }()
14 | y = *(*int)(p)
15 | x = y
16 | <-ch
17 | }
18 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceAnd/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | x = 1
8 | c <- true
9 | }()
10 | if y == 1 && x == 1 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceBlank/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var a [5]int
5 | ch := make(chan bool, 1)
6 | go func() {
7 | _, _ = a[0], a[1]
8 | ch <- true
9 | }()
10 | _, _ = a[2], a[3]
11 | <-ch
12 | a[1] = a[0]
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceCaseFallthrough/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x, y, z int
5 | _ = y
6 | ch := make(chan int, 2)
7 | z = 1
8 |
9 | go func() {
10 | y = x
11 | ch <- 1
12 | }()
13 | go func() {
14 | switch {
15 | case z == 1:
16 | case z == 2:
17 | x = 2
18 | }
19 | ch <- 1
20 | }()
21 | <-ch
22 | <-ch
23 | }
24 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceEmptyStruct/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | type Empty struct{}
5 | type X struct {
6 | y int64
7 | Empty
8 | }
9 | type Y struct {
10 | x X
11 | y int64
12 | }
13 | c := make(chan X)
14 | var y Y
15 | go func() {
16 | x := y.x
17 | c <- x
18 | }()
19 | y.y = 42
20 | <-c
21 | }
22 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceForIncr/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | done := make(chan bool)
5 | x := 0
6 | go func() {
7 | x++
8 | done <- true
9 | }()
10 | for i := 0; i < 0; x++ {
11 | }
12 | <-done
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceForInit/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | done := make(chan bool)
5 | c := make(chan bool)
6 | x := 0
7 | go func() {
8 | for {
9 | _, ok := <-c
10 | if !ok {
11 | done <- true
12 | return
13 | }
14 | x++
15 | }
16 | }()
17 | i := 0
18 | for x = 42; i < 10; i++ {
19 | c <- true
20 | }
21 | close(c)
22 | <-done
23 | }
24 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceFuncUnlock/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "sync"
4 |
5 | func main() {
6 | ch := make(chan bool, 1)
7 | var mu sync.Mutex
8 | x := 0
9 | _ = x
10 | go func() {
11 | mu.Lock()
12 | x = 42
13 | mu.Unlock()
14 | ch <- true
15 | }()
16 | x = func(mu *sync.Mutex) int {
17 | mu.Lock()
18 | return 43
19 | }(&mu)
20 | mu.Unlock()
21 | <-ch
22 | }
23 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceOr/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | x = 1
8 | c <- true
9 | }()
10 | if y == 0 || x == 1 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceSelect1/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x int
5 | _ = x
6 | compl := make(chan bool)
7 | c := make(chan bool)
8 | c1 := make(chan bool)
9 |
10 | go func() {
11 | x = 1
12 | // At least two channels are needed because
13 | // otherwise the compiler optimizes select out.
14 | // See comment in runtime/select.go:^func selectgo.
15 | select {
16 | case c <- true:
17 | case c1 <- true:
18 | }
19 | compl <- true
20 | }()
21 | select {
22 | case <-c:
23 | case c1 <- true:
24 | }
25 | x = 2
26 | <-compl
27 | }
28 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceSelect2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "runtime"
4 |
5 | func main() {
6 | var x int
7 | _ = x
8 | compl := make(chan bool)
9 | c := make(chan bool)
10 | c1 := make(chan bool)
11 | go func() {
12 | select {
13 | case <-c:
14 | case <-c1:
15 | }
16 | x = 1
17 | compl <- true
18 | }()
19 | x = 2
20 | close(c)
21 | runtime.Gosched()
22 | <-compl
23 | }
24 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceSelect3/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | var x int
5 | _ = x
6 | compl := make(chan bool)
7 | c := make(chan bool, 10)
8 | c1 := make(chan bool)
9 | go func() {
10 | x = 1
11 | select {
12 | case c <- true:
13 | case <-c1:
14 | }
15 | compl <- true
16 | }()
17 | <-c
18 | x = 2
19 | <-compl
20 | }
21 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceSelect5/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "runtime"
4 |
5 | func main() {
6 | test := func(sel, needSched bool) {
7 | var x int
8 | _ = x
9 | ch := make(chan bool)
10 | c1 := make(chan bool)
11 |
12 | done := make(chan bool, 2)
13 | go func() {
14 | if needSched {
15 | runtime.Gosched()
16 | }
17 | // println(1)
18 | x = 1
19 | if sel {
20 | select {
21 | case ch <- true:
22 | case <-c1:
23 | }
24 | } else {
25 | ch <- true
26 | }
27 | done <- true
28 | }()
29 |
30 | go func() {
31 | // println(2)
32 | if sel {
33 | select {
34 | case <-ch:
35 | case <-c1:
36 | }
37 | } else {
38 | <-ch
39 | }
40 | x = 1
41 | done <- true
42 | }()
43 | <-done
44 | <-done
45 | }
46 |
47 | test(true, true)
48 | test(true, false)
49 | test(false, true)
50 | test(false, false)
51 | }
52 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceShortCalc/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | y = 1
8 | c <- true
9 | }()
10 | if x == 0 || y == 0 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestNoRaceShortCalc2/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | c := make(chan bool)
5 | x, y := 0, 0
6 | go func() {
7 | y = 1
8 | c <- true
9 | }()
10 | if x == 1 && y == 0 {
11 | }
12 | <-c
13 | }
14 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceAppendSliceStruct/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | type X struct {
5 | x, y int
6 | }
7 | c := make(chan bool, 1)
8 | x := make([]X, 10)
9 | go func() {
10 | y := make([]X, 0, 10)
11 | y = append(y, x...)
12 | c <- true
13 | }()
14 | x[1].y = 42
15 | <-c
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceHeapParam/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | done := make(chan bool)
5 | x := func() (x int) {
6 | go func() {
7 | x = 42
8 | done <- true
9 | }()
10 | return
11 | }()
12 | _ = x
13 | <-done
14 | }
15 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceIndirection/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | ch := make(chan struct{}, 1)
5 | var y int
6 | var x *int = &y
7 | go func() {
8 | *x = 1
9 | ch <- struct{}{}
10 | }()
11 | *x = 2
12 | <-ch
13 | _ = *x
14 | }
15 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceMethodThunk/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Base int
4 |
5 | func (b *Base) Foo() int {
6 | return 42
7 | }
8 |
9 | func (b Base) Bar() int {
10 | return int(b)
11 | }
12 |
13 | func main() {
14 | type Derived struct {
15 | pad int
16 | *Base
17 | }
18 | var d Derived
19 | done := make(chan bool)
20 | go func() {
21 | _ = d.Foo()
22 | done <- true
23 | }()
24 | d = Derived{}
25 | <-done
26 | }
27 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceMethodThunk4/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Base int
4 |
5 | func (b *Base) Foo() int {
6 | return 42
7 | }
8 |
9 | func (b Base) Bar() int {
10 | return int(b)
11 | }
12 |
13 | func main() {
14 | type Derived struct {
15 | pad int
16 | *Base
17 | }
18 | var d Derived
19 | d.Base = new(Base)
20 | done := make(chan bool)
21 | go func() {
22 | _ = d.Bar()
23 | done <- true
24 | }()
25 | *(*int)(d.Base) = 42
26 | <-done
27 | }
28 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRacePanic/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | func main() {
8 | var x int
9 | _ = x
10 | var zero int = 0
11 | ch := make(chan bool, 2)
12 | go func() {
13 | defer func() {
14 | err := recover()
15 | if err == nil {
16 | panic("should be panicking")
17 | }
18 | x = 1
19 | ch <- true
20 | }()
21 | var y int = 1 / zero
22 | zero = y
23 | }()
24 | go func() {
25 | defer func() {
26 | err := recover()
27 | if err == nil {
28 | panic("should be panicking")
29 | }
30 | x = 2
31 | ch <- true
32 | }()
33 | var y int = 1 / zero
34 | zero = y
35 | }()
36 |
37 | <-ch
38 | <-ch
39 | if zero != 0 {
40 | panic("zero has changed")
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceRange/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "runtime"
4 |
5 | func main() {
6 | const N = 2
7 | var a [N]int
8 | var x, y int
9 | _ = x + y
10 | done := make(chan bool, N)
11 | for i, v := range a {
12 | go func(i int) {
13 | // we don't want a write-vs-write race
14 | // so there is no array b here
15 | if i == 0 {
16 | x = v
17 | } else {
18 | y = v
19 | }
20 | done <- true
21 | }(i)
22 | // Ensure the goroutine runs before we continue the loop.
23 | runtime.Gosched()
24 | }
25 | for i := 0; i < N; i++ {
26 | <-done
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceSliceStruct/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 | type X struct {
5 | x, y int
6 | }
7 | c := make(chan bool, 1)
8 | x := make([]X, 10)
9 | go func() {
10 | y := make([]X, 10)
11 | copy(y, x)
12 | c <- true
13 | }()
14 | x[1].y = 42
15 | <-c
16 | }
17 |
--------------------------------------------------------------------------------
/tests/testdata/stdlibNoSuccess/TestRaceStructRW/prog1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type Point struct {
4 | x, y int
5 | }
6 |
7 | type NamedPoint struct {
8 | name string
9 | p Point
10 | }
11 |
12 | func main() {
13 | p := Point{0, 0}
14 | ch := make(chan bool, 1)
15 | go func() {
16 | p = Point{1, 1}
17 | ch <- true
18 | }()
19 | q := p
20 | <-ch
21 | p = q
22 | }
23 |
--------------------------------------------------------------------------------
/utils/DoubleKeyMap.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "go/token"
5 | "hash/fnv"
6 | "strconv"
7 | )
8 |
9 | type DoubleKeyMap map[uint32]struct{}
10 |
11 | func NewDoubleKeyMap() DoubleKeyMap {
12 | return make(DoubleKeyMap)
13 | }
14 |
15 | func getHash(num int) uint32 {
16 | h := fnv.New32a()
17 | _, _ = h.Write([]byte(strconv.Itoa(num)))
18 | return h.Sum32()
19 | }
20 |
21 | func calcHash(posA token.Pos, posB token.Pos) uint32 {
22 | hashA := getHash(int(posA))
23 | hashB := getHash(int(posB))
24 | return hashA + hashB
25 | }
26 |
27 | func (m DoubleKeyMap) Add(posA token.Pos, posB token.Pos) {
28 | key := calcHash(posA, posB)
29 | m[key] = struct{}{} // Hashes are added to make the dict commutative
30 |
31 | }
32 | func (m DoubleKeyMap) IsExist(posA token.Pos, posB token.Pos) bool {
33 | key := calcHash(posA, posB)
34 | _, ok := m[key]
35 | return ok
36 | }
37 |
--------------------------------------------------------------------------------
/utils/counter.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | type Counter struct {
4 | count int
5 | }
6 |
7 | func NewCounter() *Counter {
8 | return &Counter{}
9 | }
10 |
11 | func (c *Counter) GetNext() int {
12 | c.count += 1
13 | return c.count
14 | }
--------------------------------------------------------------------------------
/utils/set.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | type IntSet struct {
4 | set map[int]int
5 | }
6 |
7 | func (s *IntSet) Add(num int) bool {
8 | if !s.Exist(num) {
9 | s.set[num] = 1
10 | return true
11 | }
12 | return false
13 | }
14 |
15 | func (s *IntSet) Exist(num int) bool {
16 | _, exist := s.set[num]
17 | return exist
18 | }
19 |
--------------------------------------------------------------------------------
/utils/stacks/basicBlockStack.go:
--------------------------------------------------------------------------------
1 | package stacks
2 |
3 | import "golang.org/x/tools/go/ssa"
4 |
5 | // Used by CFG to traverse the graph. It uses both as a stack for traversal by order, and as a map to count occurrences
6 | // and fast retrieval of items.
7 |
8 | type BlockMap map[int]struct{}
9 |
10 | func NewBlockMap() *BlockMap {
11 | blocksMap := make(BlockMap)
12 | return &blocksMap
13 | }
14 |
15 | func (s *BlockMap) Add(v *ssa.BasicBlock) {
16 | (*s)[v.Index] = struct{}{}
17 | }
18 |
19 | func (s *BlockMap) Remove(v *ssa.BasicBlock) {
20 | delete(*s, v.Index)
21 | }
22 |
23 | func (s *BlockMap) Contains(v int) bool {
24 | _, ok := (*s)[v]
25 | return ok
26 | }
27 |
--------------------------------------------------------------------------------
/utils/stacks/callCommonStack.go:
--------------------------------------------------------------------------------
1 | package stacks
2 |
3 | import "golang.org/x/tools/go/ssa"
4 |
5 | type CallCommonStack []*ssa.CallCommon
6 |
7 | func NewCallCommonStack() *CallCommonStack {
8 | stack := make([]*ssa.CallCommon, 0)
9 | return (*CallCommonStack)(&stack)
10 | }
11 |
12 | func (s *CallCommonStack) GetItems() []*ssa.CallCommon {
13 | return *s
14 | }
15 |
16 | func (s *CallCommonStack) Push(v *ssa.CallCommon) {
17 | *s = append(*s, v)
18 | }
19 |
20 | func (s *CallCommonStack) Len() int {
21 | return len(*s)
22 | }
23 |
24 | func (s *CallCommonStack) Pop() *ssa.CallCommon {
25 | if len(*s) == 0 {
26 | return nil
27 | }
28 | v := (*s)[len(*s)-1]
29 | *s = (*s)[:len(*s)-1]
30 | return v
31 | }
32 |
33 | func (s *CallCommonStack) MergeStacks(ns *CallCommonStack) {
34 | for _, item := range ns.GetItems() {
35 | s.Push(item)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/utils/stacks/functionStack.go:
--------------------------------------------------------------------------------
1 | package stacks
2 |
3 | import "golang.org/x/tools/go/ssa"
4 |
5 | type FunctionStackWithMap struct {
6 | stack FunctionStack
7 | FunctionMap FunctionMap
8 | }
9 |
10 | func NewFunctionStackWithMap() *FunctionStackWithMap {
11 | stack := make([]*ssa.Function, 0)
12 | FunctionMap := make(FunctionMap)
13 | basicBlockStack := &FunctionStackWithMap{stack: stack, FunctionMap: FunctionMap}
14 | return basicBlockStack
15 | }
16 |
17 | func (s *FunctionStackWithMap) Copy() *FunctionStackWithMap {
18 | tmp := NewFunctionStackWithMap()
19 | tmpStack := s.stack.Copy()
20 | tmpMap := make(FunctionMap)
21 | for k, v := range s.FunctionMap {
22 | tmpMap[k] = v
23 | }
24 | tmp.stack = *tmpStack
25 | tmp.FunctionMap = tmpMap
26 | return tmp
27 | }
28 |
29 | func (s *FunctionStackWithMap) GetItems() *FunctionStack {
30 | return &s.stack
31 | }
32 |
33 | func (s *FunctionStackWithMap) Iter() []*ssa.Function {
34 | return s.stack
35 | }
36 |
37 | func (s *FunctionStackWithMap) Contains(v *ssa.Function) bool {
38 | _, ok := s.FunctionMap[v]
39 | return ok
40 | }
41 |
42 | func (s *FunctionStackWithMap) Push(v *ssa.Function) {
43 | s.stack.Push(v)
44 | s.FunctionMap[v] = struct{}{}
45 | }
46 |
47 | func (s *FunctionStackWithMap) Pop() *ssa.Function {
48 | v := s.stack.Pop()
49 | delete(s.FunctionMap, v)
50 | return v
51 | }
52 |
53 | func (s *FunctionStackWithMap) Merge(sn *FunctionStackWithMap) {
54 | for _, item := range sn.Iter() {
55 | s.stack.Push(item)
56 | s.FunctionMap[item] = struct{}{}
57 | }
58 | }
59 |
60 | type FunctionMap map[*ssa.Function]struct{}
61 |
62 | type FunctionStack []*ssa.Function
63 |
64 | func (s *FunctionStack) GetItems() []*ssa.Function {
65 | return *s
66 | }
67 |
68 | func (s *FunctionStack) Copy() *FunctionStack {
69 | tmp := make([]*ssa.Function, len(*s))
70 | copy(tmp, *s)
71 | return (*FunctionStack)(&tmp)
72 | }
73 |
74 | func (s *FunctionStack) Push(v *ssa.Function) {
75 | *s = append(*s, v)
76 | }
77 |
78 | func (s *FunctionStack) Pop() *ssa.Function {
79 | if len(*s) == 0 {
80 | return nil
81 | }
82 | v := (*s)[len(*s)-1]
83 | *s = (*s)[:len(*s)-1]
84 | return v
85 | }
86 |
87 | func (s *FunctionStack) MergeStacks(items *FunctionStack) {
88 | for _, item := range items.GetItems() {
89 | s.Push(item)
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/utils/stacks/intStack.go:
--------------------------------------------------------------------------------
1 | package stacks
2 |
3 | type IntStackWithMap struct {
4 | stack IntStack
5 | intMap IntMap
6 | }
7 |
8 | func NewEmptyIntStackWithMap() *IntStackWithMap {
9 | stack := make([]int, 0, 20)
10 | intMap := make(IntMap)
11 | basicBlockStack := &IntStackWithMap{stack: stack, intMap: intMap}
12 | return basicBlockStack
13 | }
14 |
15 | func NewIntStackWithMap(stack IntStack, intMap IntMap) *IntStackWithMap {
16 | basicBlockStack := &IntStackWithMap{stack: stack, intMap: intMap}
17 | return basicBlockStack
18 | }
19 |
20 | func (s *IntStackWithMap) Copy() *IntStackWithMap {
21 | tmp := &IntStackWithMap{}
22 | tmpStack := s.stack.Copy()
23 | tmpMap := make(IntMap, len(s.stack))
24 | for k, v := range s.intMap {
25 | tmpMap[k] = v
26 | }
27 | tmp.stack = *tmpStack
28 | tmp.intMap = tmpMap
29 | return tmp
30 | }
31 |
32 | func (s *IntStackWithMap) GetItems() *IntStack {
33 | return &s.stack
34 | }
35 |
36 | func (s *IntStackWithMap) Iter() []int {
37 | return s.stack
38 | }
39 |
40 | func (s *IntStackWithMap) Contains(v int) bool {
41 | _, ok := s.intMap[v]
42 | return ok
43 | }
44 |
45 | func (s *IntStackWithMap) Push(v int) {
46 | s.stack.Push(v)
47 | s.intMap[v] = struct{}{}
48 | }
49 |
50 | func (s *IntStackWithMap) Pop() int {
51 | v := s.stack.Pop()
52 | delete(s.intMap, v)
53 | return v
54 | }
55 |
56 | func (s *IntStackWithMap) Merge(sn *IntStackWithMap) {
57 | for _, item := range sn.Iter() {
58 | s.stack.Push(item)
59 | }
60 | }
61 |
62 | type IntMap map[int]struct{}
63 |
64 | type IntStack []int
65 |
66 | func (s *IntStack) GetItems() []int {
67 | return *s
68 | }
69 |
70 | func (s *IntStack) Copy() *IntStack {
71 | tmp := make([]int, len(*s))
72 | copy(tmp, *s)
73 | return (*IntStack)(&tmp)
74 | }
75 |
76 | func (s *IntStack) Push(v int) {
77 | *s = append(*s, v)
78 | }
79 |
80 | func (s *IntStack) Pop() int {
81 | if len(*s) == 0 {
82 | return -1
83 | }
84 | v := (*s)[len(*s)-1]
85 | *s = (*s)[:len(*s)-1]
86 | return v
87 | }
88 |
89 | func (s *IntStack) MergeStacks(items *IntStack) {
90 | for _, item := range items.GetItems() {
91 | s.Push(item)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/utils/util.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bufio"
5 | "github.com/stretchr/testify/require"
6 | "go/types"
7 | "golang.org/x/tools/go/ssa"
8 | "io/ioutil"
9 | "os"
10 | "testing"
11 | )
12 |
13 | func IsCallTo(call *ssa.Function, names ...string) bool {
14 | fn, ok := call.Object().(*types.Func)
15 | if !ok {
16 | return false
17 | }
18 | q := fn.FullName()
19 | for _, name := range names {
20 | if q == name {
21 | return true
22 | }
23 | }
24 | return false
25 | }
26 |
27 | func OpenFile(fileName string) (*os.File, error) {
28 | f, err := os.Open(fileName)
29 | if err != nil {
30 | return nil, err
31 | }
32 | return f, nil
33 | }
34 |
35 | func CreateFile(fileName string) (*os.File, error) {
36 | f, err := os.Create(fileName)
37 | if err != nil {
38 | return nil, err
39 | }
40 | return f, nil
41 | }
42 | func WriteFile(f *os.File, text []byte) error {
43 | dataWriter := bufio.NewWriter(f)
44 | _, err := dataWriter.Write(text)
45 | if err != nil {
46 | return err
47 | }
48 | err = dataWriter.Flush()
49 | if err != nil {
50 | return err
51 | }
52 | return nil
53 | }
54 |
55 | func UpdateFile(t *testing.T, path string, data []byte) {
56 | f, err := CreateFile(path)
57 | require.NoError(t, err)
58 | err = WriteFile(f, data)
59 | require.NoError(t, err)
60 | err = f.Close()
61 | require.NoError(t, err)
62 | }
63 |
64 | func ReadFile(filePath string) ([]byte, error) {
65 | f, err := OpenFile(filePath)
66 | if err != nil {
67 | return nil, err
68 | }
69 | content, err := ioutil.ReadAll(f)
70 | if err != nil {
71 | return nil, err
72 | }
73 | err = f.Close()
74 | if err != nil {
75 | return nil, err
76 | }
77 | return content, err
78 | }
79 |
80 | func ReadLineByNumber(filePath string, lineNumber int) (string, error) {
81 | f, err := OpenFile(filePath)
82 | if err != nil {
83 | return "", err
84 | }
85 |
86 | scanner := bufio.NewScanner(f)
87 | line := ""
88 | for i := 1; scanner.Scan(); i++ {
89 | if i == lineNumber {
90 | line = scanner.Text()
91 | break
92 | }
93 | }
94 |
95 | err = f.Close()
96 | if err != nil {
97 | return "", err
98 | }
99 | return line, err
100 | }
101 |
--------------------------------------------------------------------------------