set test "affection"

# Goal: Ensure that the mechanism for figuring out which probes can affect the
# conditions of which other probes works properly. Also verifies that the probes
# lock the proper variables.

# Reads output from stap line by line and returns a list of pairs denoting
# affection. E.g. "1 0 3 4" means that probe 1 affects probe 0 and probe 3
# affects probe 4.
proc parse_affection {output} {
   set ret ""
   set re "probe (\[0-9\]+) can affect condition of probe (\[0-9\]+)"
   foreach line [split $output "\n"] {
      if {[regexp "^$re$" $line dummy pidx1 pidx2]} {
         set ret "$ret $pidx1 $pidx2"
      }
   }
   return [string trimleft $ret]
}

# Reads output from stap line by line and returns a list of pairs denoting
# locking. E.g. "1 {var1[r] var2[w]} 2 {}" means that probe 1 read-locks var1
# and write-locks var2, while probe 2 locks nothing.
proc parse_locking {output} {
   set ret ""
   set re "probe (\[0-9\]+) \\('\[^'\]+'\\) locks (.*)"
   foreach line [split $output "\n"] {
      if {[regexp "^$re$" $line dummy pidx vars] && \
          ![string equal $vars "nothing"]} {
         lappend ret $pidx $vars
      }
   }
   return $ret
}

# Runs a subtest
# @subtest: the name of the subtest
# @expected_affection: the affection expected given as a list of pairs
# @expected_locking: the locking expected given as a list of pairs
# @script: script to test
proc run_subtest {subtest expected_affection expected_locking script} {
   global test

   verbose -log "running: stap -p3 -vvv -e $script"
   if {[catch {exec stap -p3 -vvv -e $script 2>@1} out]} {
      fail "$test - $subtest (stap -p3)"
      verbose -log "stap output: $out"
      return
   }

   set actual_affection [parse_affection $out]
   verbose -log "expected affection: $expected_affection"
   verbose -log "received affection: $actual_affection"
   if {$expected_affection != $actual_affection} {
      fail "$test - $subtest (affection)"
      return
   }

   set actual_locking [parse_locking $out]
   verbose -log "expected locking: $expected_locking"
   verbose -log "received locking: $actual_locking"
   if {$expected_locking != $actual_locking} {
      fail "$test - $subtest (locking)"
      return
   }

   pass "$test - $subtest"
}

### SUBTESTS ###

# No affection
# NB: the [list 0 {enabled[rw]}] means "probe 0 locks var enabled for rw"
run_subtest "none" "" [list 0 {__global_enabled[rw]}] {
   global enabled = 0 // NB: we try not just 0/1 but other integral values too
   probe timer.s(5) {
       enabled = enabled ? 0 : 100;
   }
}

# NB: the [list 1 0] means "probe 1 affects condition of probe 0"
run_subtest "simple1" [list 1 0] [list 0 {__global_enabled[r]} \
                                       1 {__global_enabled[rw]}] {
   global enabled = 0
   probe timer.s(1) if (enabled != 0) {
      next
   }
   probe timer.s(5) {
      enabled = !enabled
   }
}

run_subtest "simple2" [list 0 1] [list 0 {__global_enabled[rw]} \
                                       1 {__global_enabled[r]}] {
   global enabled = 0
   probe timer.s(5) {
      enabled = enabled ? 0 : -9999
   }
   probe timer.s(1) if (enabled & 0xFFFF) {
      next
   }
}

run_subtest "self1" [list 0 0] [list 0 {__global_enabled[rw]}] {
   global enabled = 0
   probe timer.s(1) if (enabled) {
      enabled = !enabled
   }
}

run_subtest "self2" [list 0 1 \
                          1 1] \
                    [list 0 {__global_enabled[rw]} \
                          1 {__global_enabled[rw]}] {
   global enabled = 0
   probe timer.s(5) {
      enabled = !enabled
   }
   probe timer.s(1) if (enabled * 1 != 0) {
      enabled = !enabled
   }
}

run_subtest "many_vars_1" [list 1 0] [list 0 {__global_var1[r] __global_var2[r] __global_var3[r]} \
                                           1 {__global_var1[rw] __global_var2[rw] __global_var3[rw]}] {
   global var1 = 0
   global var2 = 1
   global var3
   probe timer.s(1) if (var1 && (var2 || var3)) {
      next
   }
   probe timer.s(5) {
      var1 = !var1
      var2 = 0xFFFF // not just boolean range!
      var3 = var1 && var2
   }
}

run_subtest "many_vars_2" [list 0 3 \
                                1 3 \
                                2 3] \
                          [list 0 {__global_var1[rw] __global_var2[r] __global_var3[r]} \
                                1 {__global_var1[r] __global_var2[rw] __global_var3[r]} \
                                2 {__global_var1[r] __global_var2[r] __global_var3[rw]} \
                                3 {__global_var1[r] __global_var2[r] __global_var3[r]}] {
   global var1 = 0
   global var2 = -1
   global var3
   probe timer.s(5) {
      var1 = !var1
   }
   probe timer.s(7) {
      var2 = ~var2
   }
   probe timer.s(11) {
      var3 = 1-var3
   }
   probe timer.s(1) if (var1 && (var2 || var3)) {
      next
   }
}

# NB: This is a tricky test for locking. Probe 1 needs to write-lock var1, but
# also read-lock var2 which needs to be read during the re-evaluation of probe
# 0's condition in the epilogue of probe 1.
run_subtest "many_vars_3" [list 1 0 \
                                2 0] \
                          [list 0 {__global_var1[r] __global_var2[r]} \
                                1 {__global_var1[rw] __global_var2[r]} \
                                2 {__global_var1[r] __global_var2[rw]}] {
   global var1 = 29
   global var2 = -0x123
   probe timer.s(1) if (var1 || var2) {
      next
   }
   probe timer.s(2) {
      var1--
   }
   probe timer.s(3) {
      var2--
   }
}

run_subtest "many_probes_1" [list 2 0 \
                                  2 1] \
                            [list 0 {__global_var1[r]} \
                                  1 {__global_var1[r]} \
                                  2 {__global_var1[rw]}] {
   global var1 = 0
   probe timer.s(5) if (var1) {
      next
   }
   probe timer.s(7) if (!var1) {
      next
   }
   probe timer.s(11) {
      var1 = !var1
   }
}

run_subtest "many_probes_2" [list 0 0 \
                                  0 3 \
                                  1 0 \
                                  1 3 \
                                  2 1 \
                                  2 3] \
                            [list 0 {__global_var1[rw] __global_var2[r] __global_var3[r]} \
                                  1 {__global_var1[r] __global_var2[rw] __global_var3[r]} \
                                  2 {__global_var1[r] __global_var2[r] __global_var3[rw]} \
                                  3 {__global_var1[r] __global_var2[r] __global_var3[r]}] {
   global var1 = 0
   global var2 = 1
   global var3
   probe timer.s(5) if (var1 || var2) {
      var1 = !var1
   }
   probe timer.s(7) if (var3) {
      var2 = !var2
   }
   probe timer.s(11) {
      var3 = !var3
   }
   probe timer.s(1) if (var1 && (var2 || var3)) {
      next
   }
}

run_subtest "functions_1" [list 1 0] \
                          [list 0 {__global_enabled[r]} \
                                1 {__global_enabled[rw]}] {
   global enabled = 0
   probe timer.s(1) if (enabled) {
      next
   }
   function toggler() {
      enabled = !enabled
   }
   probe timer.s(5) {
      toggler()
   }
}

run_subtest "functions_2" [list 1 0] \
                          [list 0 {__global_enabled[r]} \
                                1 {__global_enabled[rw]}] {
   global enabled = 0
   probe timer.s(1) if (enabled) {
      next
   }
   function realtoggler() {
      enabled = !enabled
   }
   function faketoggler() {
      realtoggler()
   }
   probe timer.s(5) {
      faketoggler()
   }
}

run_subtest "functions_3" [list 0 1 \
                                1 0 \
                                1 1 \
                                2 0 \
                                2 1] \
                          [list 0 {__global_var1[r] __global_var2[rw]} \
                                1 {__global_var1[rw] __global_var2[r]} \
                                2 {__global_var1[rw] __global_var2[rw]}] {
   global var1 = 0
   global var2 = 1
   function toggler1() {
      var2 = !var2
   }
   probe timer.s(1) if (var1) {
      toggler1()
   }
   function toggler2() {
      var1 = !var1
   }
   probe timer.s(2) if (!var2 || var1) {
      toggler2()
   }
   probe timer.s(5) {
      toggler1()
      toggler2()
   }
}
