aboutsummaryrefslogtreecommitdiffstats
path: root/prog/collide.go
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-02-13 20:36:16 +0100
committerAleksandr Nogikh <wp32pw@gmail.com>2023-02-14 14:56:49 +0100
commit68f80dec7aa2d7c4ae909b4b3eb0953bc3990701 (patch)
treee67c30b44eccdf99ffdac459dc3d287a6a8ed37f /prog/collide.go
parent9db88265dbce82f690f9b166b24bba1570d15854 (diff)
prog: add a new DupCallCollide collide type
It duplicates random calls in a program and makes the duplicated copies async. E.g. it could transform r0 = test() test2(r0) to r0 = test() test2(r0) (async) test2(r0) or test() (async) r0 = test() test2(r0)
Diffstat (limited to 'prog/collide.go')
-rw-r--r--prog/collide.go40
1 files changed, 40 insertions, 0 deletions
diff --git a/prog/collide.go b/prog/collide.go
index fea8cc146..67cca6ecb 100644
--- a/prog/collide.go
+++ b/prog/collide.go
@@ -97,3 +97,43 @@ func DoubleExecCollide(origProg *Prog, rand *rand.Rand) (*Prog, error) {
prog.Calls = append(prog.Calls, dupCalls...)
return prog, nil
}
+
+// DupCallCollide duplicates some of the calls in the program and marks them async.
+// This should hopefully trigger races in a more granular way than DoubleExecCollide.
+func DupCallCollide(origProg *Prog, rand *rand.Rand) (*Prog, error) {
+ if len(origProg.Calls) < 2 {
+ // For 1-call programs the behavior is similar to DoubleExecCollide.
+ return nil, fmt.Errorf("the prog is too small for the transformation")
+ }
+ // By default let's duplicate 1/3 calls in the original program.
+ insert := len(origProg.Calls) / 3
+ if insert == 0 {
+ // .. but always at least one.
+ insert = 1
+ }
+ if insert > maxAsyncPerProg {
+ insert = maxAsyncPerProg
+ }
+ if insert+len(origProg.Calls) > MaxCalls {
+ insert = MaxCalls - len(origProg.Calls)
+ }
+ if insert == 0 {
+ return nil, fmt.Errorf("no calls could be duplicated")
+ }
+ duplicate := map[int]bool{}
+ for _, pos := range rand.Perm(len(origProg.Calls))[:insert] {
+ duplicate[pos] = true
+ }
+ prog := origProg.Clone()
+ var retCalls []*Call
+ for i, c := range prog.Calls {
+ if duplicate[i] {
+ dupCall := cloneCall(c, nil)
+ dupCall.Props.Async = true
+ retCalls = append(retCalls, dupCall)
+ }
+ retCalls = append(retCalls, c)
+ }
+ prog.Calls = retCalls
+ return prog, nil
+}