blob: 05e8dafc4c4e4a3a52034e4799d847e368ecd67e [file] [log] [blame]
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -07001#!/usr/bin/env python3.4
Aart Bik7593b992016-08-17 16:51:12 -07002#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import abc
18import argparse
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070019import filecmp
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070020import os
21import shlex
22import shutil
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -070023import subprocess
Aart Bik7593b992016-08-17 16:51:12 -070024import sys
Aart Bik7593b992016-08-17 16:51:12 -070025
Aart Bike0347482016-09-20 14:34:13 -070026from glob import glob
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -070027from subprocess import DEVNULL
Aart Bik7593b992016-08-17 16:51:12 -070028from tempfile import mkdtemp
Aart Bik7593b992016-08-17 16:51:12 -070029
Wojciech Staszkiewiczf64837d2016-09-15 11:41:16 -070030sys.path.append(os.path.dirname(os.path.dirname(
31 os.path.realpath(__file__))))
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070032
Aart Bike0347482016-09-20 14:34:13 -070033from common.common import RetCode
34from common.common import CommandListToCommandString
35from common.common import FatalError
36from common.common import GetJackClassPath
37from common.common import GetEnvVariableOrError
38from common.common import RunCommand
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -070039from common.common import RunCommandForOutput
Aart Bike0347482016-09-20 14:34:13 -070040from common.common import DeviceTestEnv
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070041
42# Return codes supported by bisection bug search.
43BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT)
Aart Bik7593b992016-08-17 16:51:12 -070044
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070045
Aart Bikb16d4132016-08-19 15:45:11 -070046def GetExecutionModeRunner(device, mode):
Aart Bik7593b992016-08-17 16:51:12 -070047 """Returns a runner for the given execution mode.
48
49 Args:
Aart Bikb16d4132016-08-19 15:45:11 -070050 device: string, target device serial number (or None)
Aart Bik7593b992016-08-17 16:51:12 -070051 mode: string, execution mode
52 Returns:
53 TestRunner with given execution mode
54 Raises:
55 FatalError: error for unknown execution mode
56 """
57 if mode == 'ri':
58 return TestRunnerRIOnHost()
59 if mode == 'hint':
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070060 return TestRunnerArtIntOnHost()
Aart Bik7593b992016-08-17 16:51:12 -070061 if mode == 'hopt':
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070062 return TestRunnerArtOptOnHost()
Aart Bik7593b992016-08-17 16:51:12 -070063 if mode == 'tint':
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070064 return TestRunnerArtIntOnTarget(device)
Aart Bik7593b992016-08-17 16:51:12 -070065 if mode == 'topt':
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070066 return TestRunnerArtOptOnTarget(device)
Aart Bik7593b992016-08-17 16:51:12 -070067 raise FatalError('Unknown execution mode')
68
Aart Bike0347482016-09-20 14:34:13 -070069
Aart Bik7593b992016-08-17 16:51:12 -070070#
71# Execution mode classes.
72#
73
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070074
Aart Bik7593b992016-08-17 16:51:12 -070075class TestRunner(object):
76 """Abstraction for running a test in a particular execution mode."""
77 __meta_class__ = abc.ABCMeta
78
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070079 @abc.abstractproperty
80 def description(self):
Aart Bik7593b992016-08-17 16:51:12 -070081 """Returns a description string of the execution mode."""
Aart Bik7593b992016-08-17 16:51:12 -070082
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070083 @abc.abstractproperty
84 def id(self):
Aart Bik7593b992016-08-17 16:51:12 -070085 """Returns a short string that uniquely identifies the execution mode."""
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070086
87 @property
88 def output_file(self):
89 return self.id + '_out.txt'
90
91 @abc.abstractmethod
92 def GetBisectionSearchArgs(self):
93 """Get arguments to pass to bisection search tool.
94
95 Returns:
96 list of strings - arguments for bisection search tool, or None if
97 runner is not bisectable
98 """
Aart Bik7593b992016-08-17 16:51:12 -070099
100 @abc.abstractmethod
101 def CompileAndRunTest(self):
102 """Compile and run the generated test.
103
104 Ensures that the current Test.java in the temporary directory is compiled
105 and executed under the current execution mode. On success, transfers the
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700106 generated output to the file self.output_file in the temporary directory.
Aart Bik7593b992016-08-17 16:51:12 -0700107
108 Most nonzero return codes are assumed non-divergent, since systems may
109 exit in different ways. This is enforced by normalizing return codes.
110
111 Returns:
112 normalized return code
113 """
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700114
Aart Bik7593b992016-08-17 16:51:12 -0700115
116class TestRunnerRIOnHost(TestRunner):
117 """Concrete test runner of the reference implementation on host."""
118
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700119 @property
120 def description(self):
121 return 'RI on host'
122
123 @property
124 def id(self):
125 return 'RI'
Aart Bik7593b992016-08-17 16:51:12 -0700126
127 def CompileAndRunTest(self):
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700128 if RunCommand(['javac', 'Test.java'],
129 out=None, err=None, timeout=30) == RetCode.SUCCESS:
130 retc = RunCommand(['java', 'Test'], self.output_file, err=None)
Aart Bik7593b992016-08-17 16:51:12 -0700131 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700132 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700133 return retc
134
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700135 def GetBisectionSearchArgs(self):
136 return None
Aart Bik7593b992016-08-17 16:51:12 -0700137
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700138
139class TestRunnerArtOnHost(TestRunner):
140 """Abstract test runner of Art on host."""
141
142 def __init__(self, extra_args=None):
Aart Bik7593b992016-08-17 16:51:12 -0700143 """Constructor for the Art on host tester.
144
145 Args:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700146 extra_args: list of strings, extra arguments for dalvikvm
Aart Bik7593b992016-08-17 16:51:12 -0700147 """
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700148 self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex']
149 if extra_args is not None:
150 self._art_cmd += extra_args
151 self._art_cmd.append('Test')
152 self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
153 'Test.java']
Aart Bik7593b992016-08-17 16:51:12 -0700154
155 def CompileAndRunTest(self):
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700156 if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt',
157 timeout=30) == RetCode.SUCCESS:
158 retc = RunCommand(self._art_cmd, self.output_file, 'arterr.txt')
Aart Bik7593b992016-08-17 16:51:12 -0700159 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700160 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700161 return retc
162
Aart Bik7593b992016-08-17 16:51:12 -0700163
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700164class TestRunnerArtIntOnHost(TestRunnerArtOnHost):
165 """Concrete test runner of interpreter mode Art on host."""
166
167 def __init__(self):
168 """Constructor."""
169 super().__init__(['-Xint'])
170
171 @property
172 def description(self):
173 return 'Art interpreter on host'
174
175 @property
176 def id(self):
177 return 'HInt'
178
179 def GetBisectionSearchArgs(self):
180 return None
181
182
183class TestRunnerArtOptOnHost(TestRunnerArtOnHost):
184 """Concrete test runner of optimizing compiler mode Art on host."""
185
186 def __init__(self):
187 """Constructor."""
188 super().__init__(None)
189
190 @property
191 def description(self):
192 return 'Art optimizing on host'
193
194 @property
195 def id(self):
196 return 'HOpt'
197
198 def GetBisectionSearchArgs(self):
199 cmd_str = CommandListToCommandString(
200 self._art_cmd[0:2] + ['{ARGS}'] + self._art_cmd[2:])
201 return ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
202
203
204class TestRunnerArtOnTarget(TestRunner):
205 """Abstract test runner of Art on target."""
206
207 def __init__(self, device, extra_args=None):
Aart Bik7593b992016-08-17 16:51:12 -0700208 """Constructor for the Art on target tester.
209
210 Args:
Aart Bikb16d4132016-08-19 15:45:11 -0700211 device: string, target device serial number (or None)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700212 extra_args: list of strings, extra arguments for dalvikvm
Aart Bik7593b992016-08-17 16:51:12 -0700213 """
Aart Bik842a4f32016-09-21 15:45:18 -0700214 self._test_env = DeviceTestEnv('jfuzz_', specific_device=device)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700215 self._dalvik_cmd = ['dalvikvm']
216 if extra_args is not None:
217 self._dalvik_cmd += extra_args
218 self._device = device
219 self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
220 'Test.java']
221 self._device_classpath = None
Aart Bik7593b992016-08-17 16:51:12 -0700222
223 def CompileAndRunTest(self):
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700224 if RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt',
225 timeout=30) == RetCode.SUCCESS:
226 self._device_classpath = self._test_env.PushClasspath('classes.dex')
227 cmd = self._dalvik_cmd + ['-cp', self._device_classpath, 'Test']
228 (output, retc) = self._test_env.RunCommand(
229 cmd, {'ANDROID_LOG_TAGS': '*:s'})
230 with open(self.output_file, 'w') as run_out:
231 run_out.write(output)
Aart Bik7593b992016-08-17 16:51:12 -0700232 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700233 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700234 return retc
235
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700236 def GetBisectionSearchArgs(self):
237 cmd_str = CommandListToCommandString(
238 self._dalvik_cmd + ['-cp',self._device_classpath, 'Test'])
239 cmd = ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
240 if self._device:
241 cmd += ['--device-serial', self._device]
242 else:
243 cmd.append('--device')
244 return cmd
245
246
247class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget):
248 """Concrete test runner of interpreter mode Art on target."""
249
250 def __init__(self, device):
251 """Constructor.
252
253 Args:
254 device: string, target device serial number (or None)
255 """
256 super().__init__(device, ['-Xint'])
257
258 @property
259 def description(self):
260 return 'Art interpreter on target'
261
262 @property
263 def id(self):
264 return 'TInt'
265
266 def GetBisectionSearchArgs(self):
267 return None
268
269
270class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget):
271 """Concrete test runner of optimizing compiler mode Art on target."""
272
273 def __init__(self, device):
274 """Constructor.
275
276 Args:
277 device: string, target device serial number (or None)
278 """
279 super().__init__(device, None)
280
281 @property
282 def description(self):
283 return 'Art optimizing on target'
284
285 @property
286 def id(self):
287 return 'TOpt'
288
289 def GetBisectionSearchArgs(self):
290 cmd_str = CommandListToCommandString(
291 self._dalvik_cmd + ['-cp', self._device_classpath, 'Test'])
292 cmd = ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
293 if self._device:
294 cmd += ['--device-serial', self._device]
295 else:
296 cmd.append('--device')
297 return cmd
298
299
Aart Bik7593b992016-08-17 16:51:12 -0700300#
Aart Bike0347482016-09-20 14:34:13 -0700301# Tester class.
Aart Bik7593b992016-08-17 16:51:12 -0700302#
303
Aart Bik7593b992016-08-17 16:51:12 -0700304
Aart Bik842a4f32016-09-21 15:45:18 -0700305class JFuzzTester(object):
306 """Tester that runs JFuzz many times and report divergences."""
Aart Bik7593b992016-08-17 16:51:12 -0700307
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700308 def __init__(self, num_tests, device, mode1, mode2, jfuzz_args,
309 report_script):
Aart Bik7593b992016-08-17 16:51:12 -0700310 """Constructor for the tester.
311
312 Args:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700313 num_tests: int, number of tests to run
314 device: string, target device serial number (or None)
315 mode1: string, execution mode for first runner
316 mode2: string, execution mode for second runner
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700317 jfuzz_args: list of strings, additional arguments for jfuzz
318 report_script: string, path to script called for each divergence
Aart Bik7593b992016-08-17 16:51:12 -0700319 """
320 self._num_tests = num_tests
Aart Bikb16d4132016-08-19 15:45:11 -0700321 self._device = device
322 self._runner1 = GetExecutionModeRunner(device, mode1)
323 self._runner2 = GetExecutionModeRunner(device, mode2)
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700324 self._jfuzz_args = jfuzz_args
325 self._report_script = report_script
Aart Bik7593b992016-08-17 16:51:12 -0700326 self._save_dir = None
Aart Bike0347482016-09-20 14:34:13 -0700327 self._results_dir = None
Aart Bik842a4f32016-09-21 15:45:18 -0700328 self._jfuzz_dir = None
Aart Bik7593b992016-08-17 16:51:12 -0700329 # Statistics.
330 self._test = 0
331 self._num_success = 0
332 self._num_not_compiled = 0
333 self._num_not_run = 0
334 self._num_timed_out = 0
335 self._num_divergences = 0
336
337 def __enter__(self):
338 """On entry, enters new temp directory after saving current directory.
339
340 Raises:
341 FatalError: error when temp directory cannot be constructed
342 """
343 self._save_dir = os.getcwd()
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700344 self._results_dir = mkdtemp(dir='/tmp/')
Aart Bik842a4f32016-09-21 15:45:18 -0700345 self._jfuzz_dir = mkdtemp(dir=self._results_dir)
346 if self._results_dir is None or self._jfuzz_dir is None:
Aart Bik7593b992016-08-17 16:51:12 -0700347 raise FatalError('Cannot obtain temp directory')
Aart Bik842a4f32016-09-21 15:45:18 -0700348 os.chdir(self._jfuzz_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700349 return self
350
351 def __exit__(self, etype, evalue, etraceback):
352 """On exit, re-enters previously saved current directory and cleans up."""
353 os.chdir(self._save_dir)
Aart Bik842a4f32016-09-21 15:45:18 -0700354 shutil.rmtree(self._jfuzz_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700355 if self._num_divergences == 0:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700356 shutil.rmtree(self._results_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700357
358 def Run(self):
Aart Bik842a4f32016-09-21 15:45:18 -0700359 """Runs JFuzz many times and report divergences."""
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700360 print()
Aart Bik842a4f32016-09-21 15:45:18 -0700361 print('**\n**** JFuzz Testing\n**')
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700362 print()
363 print('#Tests :', self._num_tests)
364 print('Device :', self._device)
365 print('Directory :', self._results_dir)
366 print('Exec-mode1:', self._runner1.description)
367 print('Exec-mode2:', self._runner2.description)
Aart Bik9d537312016-09-15 10:42:02 -0700368 print()
Aart Bik7593b992016-08-17 16:51:12 -0700369 self.ShowStats()
370 for self._test in range(1, self._num_tests + 1):
Aart Bik842a4f32016-09-21 15:45:18 -0700371 self.RunJFuzzTest()
Aart Bik7593b992016-08-17 16:51:12 -0700372 self.ShowStats()
373 if self._num_divergences == 0:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700374 print('\n\nsuccess (no divergences)\n')
Aart Bik7593b992016-08-17 16:51:12 -0700375 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700376 print('\n\nfailure (divergences)\n')
Aart Bik7593b992016-08-17 16:51:12 -0700377
378 def ShowStats(self):
379 """Shows current statistics (on same line) while tester is running."""
Aart Bike0347482016-09-20 14:34:13 -0700380 print('\rTests:', self._test,
381 'Success:', self._num_success,
382 'Not-compiled:', self._num_not_compiled,
383 'Not-run:', self._num_not_run,
384 'Timed-out:', self._num_timed_out,
385 'Divergences:', self._num_divergences,
386 end='')
Aart Bik7593b992016-08-17 16:51:12 -0700387 sys.stdout.flush()
388
Aart Bik842a4f32016-09-21 15:45:18 -0700389 def RunJFuzzTest(self):
390 """Runs a single JFuzz test, comparing two execution modes."""
Aart Bik7593b992016-08-17 16:51:12 -0700391 self.ConstructTest()
392 retc1 = self._runner1.CompileAndRunTest()
393 retc2 = self._runner2.CompileAndRunTest()
394 self.CheckForDivergence(retc1, retc2)
395 self.CleanupTest()
396
397 def ConstructTest(self):
Aart Bik842a4f32016-09-21 15:45:18 -0700398 """Use JFuzz to generate next Test.java test.
Aart Bik7593b992016-08-17 16:51:12 -0700399
400 Raises:
Aart Bik842a4f32016-09-21 15:45:18 -0700401 FatalError: error when jfuzz fails
Aart Bik7593b992016-08-17 16:51:12 -0700402 """
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700403 if (RunCommand(['jfuzz'] + self._jfuzz_args, out='Test.java', err=None)
404 != RetCode.SUCCESS):
Aart Bik842a4f32016-09-21 15:45:18 -0700405 raise FatalError('Unexpected error while running JFuzz')
Aart Bik7593b992016-08-17 16:51:12 -0700406
407 def CheckForDivergence(self, retc1, retc2):
408 """Checks for divergences and updates statistics.
409
410 Args:
411 retc1: int, normalized return code of first runner
412 retc2: int, normalized return code of second runner
413 """
414 if retc1 == retc2:
415 # Non-divergent in return code.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700416 if retc1 == RetCode.SUCCESS:
Aart Bik7593b992016-08-17 16:51:12 -0700417 # Both compilations and runs were successful, inspect generated output.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700418 runner1_out = self._runner1.output_file
419 runner2_out = self._runner2.output_file
420 if not filecmp.cmp(runner1_out, runner2_out, shallow=False):
421 self.ReportDivergence(retc1, retc2, is_output_divergence=True)
Aart Bik7593b992016-08-17 16:51:12 -0700422 else:
423 self._num_success += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700424 elif retc1 == RetCode.TIMEOUT:
Aart Bik7593b992016-08-17 16:51:12 -0700425 self._num_timed_out += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700426 elif retc1 == RetCode.NOTCOMPILED:
Aart Bik7593b992016-08-17 16:51:12 -0700427 self._num_not_compiled += 1
428 else:
429 self._num_not_run += 1
430 else:
431 # Divergent in return code.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700432 self.ReportDivergence(retc1, retc2, is_output_divergence=False)
Aart Bik7593b992016-08-17 16:51:12 -0700433
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700434 def GetCurrentDivergenceDir(self):
435 return self._results_dir + '/divergence' + str(self._num_divergences)
436
437 def ReportDivergence(self, retc1, retc2, is_output_divergence):
Aart Bik7593b992016-08-17 16:51:12 -0700438 """Reports and saves a divergence."""
439 self._num_divergences += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700440 print('\n' + str(self._num_divergences), end='')
441 if is_output_divergence:
442 print(' divergence in output')
443 else:
444 print(' divergence in return code: ' + retc1.name + ' vs. ' +
445 retc2.name)
Aart Bik7593b992016-08-17 16:51:12 -0700446 # Save.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700447 ddir = self.GetCurrentDivergenceDir()
448 os.mkdir(ddir)
449 for f in glob('*.txt') + ['Test.java']:
450 shutil.copy(f, ddir)
451 # Maybe run bisection bug search.
452 if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
453 self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700454 # Call reporting script.
455 if self._report_script:
456 self.RunReportScript(retc1, retc2, is_output_divergence)
457
458 def RunReportScript(self, retc1, retc2, is_output_divergence):
459 """Runs report script."""
460 try:
461 title = "Divergence between {0} and {1} (found with fuzz testing)".format(
462 self._runner1.description, self._runner2.description)
463 # Prepare divergence comment.
464 jfuzz_cmd_and_version = subprocess.check_output(
465 ['grep', '-o', 'jfuzz.*', 'Test.java'], universal_newlines=True)
466 (jfuzz_cmd_str, jfuzz_ver) = jfuzz_cmd_and_version.split('(')
467 # Strip right parenthesis and new line.
468 jfuzz_ver = jfuzz_ver[:-2]
469 jfuzz_args = ['\'-{0}\''.format(arg)
470 for arg in jfuzz_cmd_str.strip().split(' -')][1:]
471 wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args]
472 repro_cmd_str = (os.path.basename(__file__) + ' --num_tests 1 ' +
473 ' '.join(wrapped_args))
474 comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format(
475 jfuzz_ver, jfuzz_cmd_str, repro_cmd_str)
476 if is_output_divergence:
477 (output, _, _) = RunCommandForOutput(
478 ['diff', self._runner1.output_file, self._runner2.output_file],
479 None, subprocess.PIPE, subprocess.STDOUT)
480 comment += 'Diff:\n' + output
481 else:
482 comment += '{0} vs {1}\n'.format(retc1, retc2)
483 # Prepare report script command.
484 script_cmd = [self._report_script, title, comment]
485 ddir = self.GetCurrentDivergenceDir()
486 bisection_out_files = glob(ddir + '/*_bisection_out.txt')
487 if bisection_out_files:
488 script_cmd += ['--bisection_out', bisection_out_files[0]]
489 subprocess.check_call(script_cmd, stdout=DEVNULL, stderr=DEVNULL)
490 except subprocess.CalledProcessError as err:
491 print('Failed to run report script.\n', err)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700492
493 def RunBisectionSearch(self, args, expected_retcode, expected_output,
494 runner_id):
495 ddir = self.GetCurrentDivergenceDir()
496 outfile_path = ddir + '/' + runner_id + '_bisection_out.txt'
497 logfile_path = ddir + '/' + runner_id + '_bisection_log.txt'
498 errfile_path = ddir + '/' + runner_id + '_bisection_err.txt'
499 args = list(args) + ['--logfile', logfile_path, '--cleanup']
500 args += ['--expected-retcode', expected_retcode.name]
501 if expected_output:
502 args += ['--expected-output', expected_output]
503 bisection_search_path = os.path.join(
504 GetEnvVariableOrError('ANDROID_BUILD_TOP'),
505 'art/tools/bisection_search/bisection_search.py')
506 if RunCommand([bisection_search_path] + args, out=outfile_path,
507 err=errfile_path, timeout=300) == RetCode.TIMEOUT:
508 print('Bisection search TIMEOUT')
509
510 def MaybeBisectDivergence(self, retc1, retc2, is_output_divergence):
511 bisection_args1 = self._runner1.GetBisectionSearchArgs()
512 bisection_args2 = self._runner2.GetBisectionSearchArgs()
513 if is_output_divergence:
514 maybe_output1 = self._runner1.output_file
515 maybe_output2 = self._runner2.output_file
516 else:
517 maybe_output1 = maybe_output2 = None
518 if bisection_args1 is not None:
519 self.RunBisectionSearch(bisection_args1, retc2, maybe_output2,
520 self._runner1.id)
521 if bisection_args2 is not None:
522 self.RunBisectionSearch(bisection_args2, retc1, maybe_output1,
523 self._runner2.id)
Aart Bik7593b992016-08-17 16:51:12 -0700524
525 def CleanupTest(self):
526 """Cleans up after a single test run."""
Aart Bik842a4f32016-09-21 15:45:18 -0700527 for file_name in os.listdir(self._jfuzz_dir):
528 file_path = os.path.join(self._jfuzz_dir, file_name)
Aart Bike0347482016-09-20 14:34:13 -0700529 if os.path.isfile(file_path):
530 os.unlink(file_path)
531 elif os.path.isdir(file_path):
532 shutil.rmtree(file_path)
Aart Bik7593b992016-08-17 16:51:12 -0700533
534
535def main():
536 # Handle arguments.
537 parser = argparse.ArgumentParser()
538 parser.add_argument('--num_tests', default=10000,
539 type=int, help='number of tests to run')
Aart Bikb16d4132016-08-19 15:45:11 -0700540 parser.add_argument('--device', help='target device serial number')
Aart Bik7593b992016-08-17 16:51:12 -0700541 parser.add_argument('--mode1', default='ri',
542 help='execution mode 1 (default: ri)')
543 parser.add_argument('--mode2', default='hopt',
544 help='execution mode 2 (default: hopt)')
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700545 parser.add_argument('--report_script', help='script called for each'
546 'divergence')
547 parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args',
548 action='append', help='argument for jfuzz')
Aart Bik7593b992016-08-17 16:51:12 -0700549 args = parser.parse_args()
550 if args.mode1 == args.mode2:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700551 raise FatalError('Identical execution modes given')
Aart Bik842a4f32016-09-21 15:45:18 -0700552 # Run the JFuzz tester.
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700553 with JFuzzTester(args.num_tests, args.device, args.mode1, args.mode2,
554 args.jfuzz_args, args.report_script) as fuzzer:
Aart Bik7593b992016-08-17 16:51:12 -0700555 fuzzer.Run()
556
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700557if __name__ == '__main__':
Aart Bik7593b992016-08-17 16:51:12 -0700558 main()