changeset 226:e147f3d6347d

Bug 3356: [TEST]Add a race-condition test framework Reviewed-by: yasuenag https://github.com/HeapStats/heapstats/pull/91
author KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp>
date Tue, 11 Apr 2017 17:38:38 +0900
parents 5abb11559f3b
children f421a4c6e7ea
files .ignore ChangeLog agent/test/race-condition/README.md agent/test/race-condition/common.py agent/test/race-condition/testcase.sh agent/test/race-condition/testlist.txt
diffstat 5 files changed, 257 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/.ignore	Tue Apr 11 13:05:30 2017 +0900
+++ b/.ignore	Tue Apr 11 17:38:38 2017 +0900
@@ -40,3 +40,8 @@
 
 # IntelliJ (2016.3)
 .idea/workspace.xml
+
+# Python
+__pycache__/
+*.py[cod]
+*$py.class
--- a/ChangeLog	Tue Apr 11 13:05:30 2017 +0900
+++ b/ChangeLog	Tue Apr 11 17:38:38 2017 +0900
@@ -3,6 +3,7 @@
 	* Bug 3353: [TEST]Add a test runner for deadlock and thread-recording
 	* Bug 3354: [TEST]Modify test runners for testing in a similar way
 	* Bug 3355: [TEST]Add unit test cases
+	* Bug 3356: [TEST]Add a race-condition test framework
 
 2017-03-25 KUBOTA Yuji <kubota.yuji@lab.ntt.co.jp>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/test/race-condition/README.md	Tue Apr 11 17:38:38 2017 +0900
@@ -0,0 +1,46 @@
+# Test cases for testing race condition
+
+## Usage
+
+1. Add directories to `testlist.txt`
+2. Run `testcase.sh`
+
+## How to add new tase cases for testing race condition.
+
+1. Create a directory.
+2. Create a `buildenv.sh` to set environment for testing.
+3. Write `test.py` and testcase such like as existing test codes.
+
+### `buildenv.sh`
+
+`buildenv.sh` requires the following.
+
+* `CLASSPATH`
+    * Classpath to build test code.
+    * Should write an absolute path.
+* `MAINCLASS`
+    * A main class of test code.
+* `JAVA_OPTS`
+    * Options for launching java process.
+* `HEAPSTATS_CONF`
+    * Path to `heapstats.conf` for testing
+* Command line to build test code.
+
+### `test.py`
+
+* Import `common.py` on parent directory
+* Use `common.initialize()` method with passing break point names and break condition as arguments
+
+### Result
+
+* `test.py` will touch `test-succeeded` when the test passed correctly. Otherwise, will touch `test-failed`.
+* `testcase.sh` will show a summary of all test cases's result at last as below.
+
+```
+Test summary:
+  Testcase1: succeeded
+  Testcase2: succeeded
+  Testcase3: failed
+  :
+```
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/test/race-condition/common.py	Tue Apr 11 17:38:38 2017 +0900
@@ -0,0 +1,76 @@
+import gdb
+
+class ContinueInvoker:
+    def __init__(self, th):
+        self.__thread_num = th
+
+    def __call__(self):
+        gdb.execute("thread " + str(self.__thread_num))
+        gdb.execute("continue")
+
+
+class BreakPointHandler(gdb.Breakpoint):
+    def __init__(self, bp, condition):
+        super(BreakPointHandler, self).__init__(bp)
+        self.__cond = condition
+        self.thread_num = -1
+
+    def stop(self):
+        if(self.__cond()):
+            self.thread_num = gdb.selected_thread().num
+            self.enabled = False
+            gdb.write("Suspend thread #" + str(self.thread_num) + "\n")
+            return True
+        else:
+            return False
+
+
+class RaceConditionGenerator:
+    def __init__(self, primary, secondary, secondary_enabled):
+        self.__primary = primary
+        self.__secondary = secondary
+        self.__secondary_enabled = secondary_enabled
+
+    def coordinate(self):
+        if((not self.__secondary_enabled) and (not self.__primary.enabled) and (self.__secondary.thread_num == -1)):
+            self.__secondary.enabled = True
+        elif((self.__primary.thread_num != -1) and (self.__secondary.thread_num != -1)):
+            gdb.write("Test start!\n")
+            gdb.post_event(ContinueInvoker(self.__primary.thread_num))
+            gdb.post_event(ContinueInvoker(self.__secondary.thread_num))
+
+
+class StopHandler:
+    def __init__(self, rcgen):
+        self.__rcgen = rcgen
+
+    def __call__(self, event):
+        # Ignore signals
+        if(isinstance(event, gdb.SignalEvent)):
+            gdb.post_event(ContinueInvoker(event.inferior_thread.num))
+        elif(isinstance(event, gdb.BreakpointEvent)):
+            self.__rcgen.coordinate()
+
+
+def return_true():
+    return True
+
+
+def exit_handler(event):
+    gdb.execute("quit")
+
+
+def initialize(primary, primary_cond, secondary, secondary_cond, secondary_enabled):
+    gdb.execute("set breakpoint pending on")
+    gdb.execute("set target-async on")
+    gdb.execute("set pagination off")
+    gdb.execute("set non-stop on")
+
+    p = BreakPointHandler(primary, primary_cond)
+    s = BreakPointHandler(secondary, secondary_cond)
+    s.enabled = secondary_enabled
+    rcgen = RaceConditionGenerator(p, s, secondary_enabled)
+
+    gdb.events.stop.connect(StopHandler(rcgen))
+    gdb.events.exited.connect(exit_handler)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/test/race-condition/testcase.sh	Tue Apr 11 17:38:38 2017 +0900
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+pushd $(dirname $0) >/dev/null
+
+if [[ $1 == "--clean" ]]; then
+  find . \( -name "*.class" -o -name "core*" -o -name "hs_err*log" -o -name "*.gdb" -o -name "*.log" -o -name command.gdb -o -name "heapstats_*" -o -name "test-failed" -o -name "test-succeeded" \) -exec rm -f {} \;
+  rm -fR __pycache__
+  exit
+fi
+
+declare -A DEFAULT_ULIMITS
+
+function store_ulimits(){
+  for item in `ulimit -a | sed -e 's/^.\+\(-.\))\s\+\(.\+\)$/\1,\2/g'`; do
+    key=`echo $item | cut -d',' -f 1`
+    value=`echo $item | cut -d',' -f 2`
+    DEFAULT_ULIMITS[$key]=$value
+  done
+}
+
+function restore_ulimits(){
+  for key in ${!DEFAULT_ULIMITS[@]}; do
+    ulimit -S $key ${DEFAULT_ULIMITS[$key]} > /dev/null 2>&1
+  done
+}
+
+
+if [ -z "$JAVA_HOME" ]; then
+  echo '$JAVA_HOME is not set.'
+  exit 1
+fi;
+
+if [ -z "$HEAPSTATS_LIB" ]; then
+  echo '$HEAPSTATS_LIB is not set.'
+  exit 2
+fi;
+
+TESTLIST=${1:-$PWD/testlist.txt}
+
+if [ ! -e $TESTLIST ]; then
+  echo "$TESTLIST does not exist."
+  exit 3
+fi
+
+ulimit -c unlimited
+store_ulimits
+
+for TEST_ENTRY in `cat $TESTLIST`; do
+  for TESTDIR in `ls -d $TEST_ENTRY`; do
+    echo "Run $TESTDIR"
+
+    export TEST_TARGET=$PWD/$TESTDIR
+    source $TEST_TARGET/buildenv.sh
+
+    AGENTPATH="-agentpath:$HEAPSTATS_LIB"
+
+    if [ -n "$HEAPSTATS_CONF" ]; then
+      AGENTPATH="$AGENTPATH=$HEAPSTATS_CONF"
+    fi
+
+    cat <<EOF > $TEST_TARGET/command.gdb
+set logging file result.log
+set logging on
+run $AGENTPATH $JAVA_OPTS $MAINCLASS >> result.log 2>&1
+EOF
+    # If select specified gdb, load files and symbols to exec
+    if [ -n "$GDB_PATH" ] ; then
+      cat <<EOF > $TEST_TARGET/loadfiles.gdb
+set sysroot /
+set debug-file-directory /usr/lib/debug
+set directories /usr/src/debug
+set use-deprecated-index-sections on
+EOF
+      JAVA_LIB=$(find ${JAVA_HOME%/}/ -name "java" -type f | grep -v jre)
+      echo "file ${JAVA_LIB}" >> ${TEST_TARGET}/loadfiles.gdb
+      JAVA_LIB=${JAVA_LIB%/bin/java}
+      echo "set solib-search-path /lib64/:${JAVA_LIB}:${JAVA_LIB}*/jre/lib/amd64/:${JAVA_LIB}*/jre/lib/amd64/*/:${HEAPSTATS_LIB%/*}" >> ${TEST_TARGET}/loadfiles.gdb
+
+      pushd $TEST_TARGET
+      ${GDB_PATH}/bin/gdb -q -x loadfiles.gdb -x test.py -x command.gdb
+    else
+      pushd $TEST_TARGET
+      gdb -q -x test.py -x command.gdb $JAVA_HOME/bin/java
+    fi
+
+    if [ $? -ne 0 ]; then
+      echo "$TESTDIR failed!"
+      touch test-failed
+    else
+      ls hs_err*.log > /dev/null 2>&1
+
+      if [ $? -eq 0 ]; then
+        echo "$TESTDIR failed! (hs_err exists)"
+        touch test-failed
+      else
+        ls core* > /dev/null 2>&1
+        if [ $? -eq 0 ]; then
+          echo "$TESTDIR failed! (core exists)"
+          touch test-failed
+        else
+          echo "$TESTDIR succeeded"
+          touch test-succeeded
+        fi
+      fi
+
+    fi
+
+    restore_ulimits
+    popd
+  done
+done
+
+echo
+echo "Test summary:"
+
+for TESTDIR in `cat $TESTLIST`; do
+  echo -n "  $TESTDIR: "
+
+  if [ -e $TESTDIR/test-succeeded ]; then
+    echo -e "\e[32msucceeded\e[m"
+  elif [ -e $TESTDIR/test-failed ]; then
+    echo -e "\e[31mfailed\e[m"
+  else
+    echo -e "\e[33munknown\e[m"
+  fi
+
+done
+
+popd >/dev/null