Added option to use dx in dexfuzz testing as well.

Rationale:
Moving away from jack, we need all our fuzzing tools
to be able to use alternative compilation paths.

BUG: 19637031

Test: nightly fuzzing (Aart's cron job for now)
Change-Id: I92b13e46ed692d7813c072b576d8a3267ff36dd5
diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md
index c87e714..10d175b 100644
--- a/tools/jfuzz/README.md
+++ b/tools/jfuzz/README.md
@@ -48,6 +48,7 @@
                           [--report_script=SCRIPT]
                           [--jfuzz_arg=ARG]
                           [--true_divergence]
+                          [--use_dx]
 
 where
 
@@ -63,6 +64,7 @@
     --report_script   : path to script called for each divergence
     --jfuzz_arg       : argument for jfuzz
     --true_divergence : don't bisect timeout divergences
+    --use_dx          : use dx (rather than jack)
 
 How to start JFuzz nightly testing
 ==================================
@@ -83,12 +85,14 @@
                           [--num_tests=NUM_TESTS]
                           [--num_inputs=NUM_INPUTS]
                           [--device=DEVICE]
+                          [--use_dx]
 
 where
 
     --num_tests : number of tests to run (10000 by default)
     --num_inputs: number of JFuzz programs to generate
     --device    : target device serial number (passed to adb -s)
+    --use_dx    : use dx (rather than jack)
 
 Background
 ==========
diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py
index c1d2e4f..ca0aec0 100755
--- a/tools/jfuzz/run_dex_fuzz_test.py
+++ b/tools/jfuzz/run_dex_fuzz_test.py
@@ -19,6 +19,7 @@
 import shutil
 import sys
 
+from glob import glob
 from subprocess import call
 from tempfile import mkdtemp
 
@@ -40,13 +41,14 @@
 class DexFuzzTester(object):
   """Tester that feeds JFuzz programs into DexFuzz testing."""
 
-  def  __init__(self, num_tests, num_inputs, device):
+  def  __init__(self, num_tests, num_inputs, device, use_dx):
     """Constructor for the tester.
 
     Args:
       num_tests: int, number of tests to run
       num_inputs: int, number of JFuzz programs to generate
       device: string, target device serial number (or None)
+      use_dx: boolean, if True use dx rather than jack
     """
     self._num_tests = num_tests
     self._num_inputs = num_inputs
@@ -56,6 +58,7 @@
     self._dexfuzz_dir = None
     self._inputs_dir = None
     self._dexfuzz_env = None
+    self._use_dx = use_dx
 
   def __enter__(self):
     """On entry, enters new temp directory after saving current directory.
@@ -100,6 +103,35 @@
     self.GenerateJFuzzPrograms()
     self.RunDexFuzz()
 
+  def CompileOnHost(self):
+    """Compiles Test.java into classes.dex using either javac/dx or jack.
+
+    Raises:
+      FatalError: error when compilation fails
+    """
+    if self._use_dx:
+      if RunCommand(['javac', 'Test.java'],
+                    out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS:
+        print('Unexpected error while running javac')
+        raise FatalError('Unexpected error while running javac')
+      cfiles = glob('*.class')
+      if RunCommand(['dx', '--dex', '--output=classes.dex'] + cfiles,
+                    out=None, err='dxerr.txt', timeout=30) != RetCode.SUCCESS:
+        print('Unexpected error while running dx')
+        raise FatalError('Unexpected error while running dx')
+      # Cleanup on success (nothing to see).
+      for cfile in cfiles:
+        os.unlink(cfile)
+      os.unlink('jerr.txt')
+      os.unlink('dxerr.txt')
+    else:
+      jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java']
+      if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
+                    timeout=30) != RetCode.SUCCESS:
+        print('Unexpected error while running Jack')
+        raise FatalError('Unexpected error while running Jack')
+      # Cleanup on success (nothing to see).
+      os.unlink('jackerr.txt')
 
   def GenerateJFuzzPrograms(self):
     """Generates JFuzz programs.
@@ -109,17 +141,12 @@
     """
     os.chdir(self._inputs_dir)
     for i in range(1, self._num_inputs + 1):
-      jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java']
       if RunCommand(['jfuzz'], out='Test.java', err=None) != RetCode.SUCCESS:
         print('Unexpected error while running JFuzz')
         raise FatalError('Unexpected error while running JFuzz')
-      if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
-                    timeout=30) != RetCode.SUCCESS:
-        print('Unexpected error while running Jack')
-        raise FatalError('Unexpected error while running Jack')
+      self.CompileOnHost()
       shutil.move('Test.java', '../Test' + str(i) + '.java')
       shutil.move('classes.dex', 'classes' + str(i) + '.dex')
-    os.unlink('jackerr.txt')
 
   def RunDexFuzz(self):
     """Starts the DexFuzz testing."""
@@ -152,10 +179,12 @@
                       type=int, help='number of tests to run')
   parser.add_argument('--num_inputs', default=10,
                       type=int, help='number of JFuzz program to generate')
+  parser.add_argument('--use_dx', default=False, action='store_true',
+                      help='use dx (rather than jack)')
   parser.add_argument('--device', help='target device serial number')
   args = parser.parse_args()
   # Run the DexFuzz tester.
-  with DexFuzzTester(args.num_tests, args.num_inputs, args.device) as fuzzer:
+  with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.use_dx) as fuzzer:
     fuzzer.Run()
 
 if __name__ == '__main__':
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index 58bc737..dac1c79 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -606,7 +606,7 @@
   parser.add_argument('--true_divergence', default=False, action='store_true',
                       help='don\'t bisect timeout divergences')
   parser.add_argument('--use_dx', default=False, action='store_true',
-                      help='use old-style dx (rather than jack)')
+                      help='use dx (rather than jack)')
   args = parser.parse_args()
   if args.mode1 == args.mode2:
     raise FatalError('Identical execution modes given')