blob: 4a54a3a4f2ecf5e0b19ce94ba18dc176a1358d82 [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 Bik06fa9f02017-12-14 11:15:59 -080046def GetExecutionModeRunner(dexer, debug_info, device, mode):
Aart Bik7593b992016-08-17 16:51:12 -070047 """Returns a runner for the given execution mode.
48
49 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -080050 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -080051 debug_info: boolean, if True include debugging info
Aart Bikb16d4132016-08-19 15:45:11 -070052 device: string, target device serial number (or None)
Aart Bik7593b992016-08-17 16:51:12 -070053 mode: string, execution mode
54 Returns:
55 TestRunner with given execution mode
56 Raises:
57 FatalError: error for unknown execution mode
58 """
59 if mode == 'ri':
Aart Bik06fa9f02017-12-14 11:15:59 -080060 return TestRunnerRIOnHost(debug_info)
Aart Bik7593b992016-08-17 16:51:12 -070061 if mode == 'hint':
Aart Bik06fa9f02017-12-14 11:15:59 -080062 return TestRunnerArtIntOnHost(dexer, debug_info)
Aart Bik7593b992016-08-17 16:51:12 -070063 if mode == 'hopt':
Aart Bik06fa9f02017-12-14 11:15:59 -080064 return TestRunnerArtOptOnHost(dexer, debug_info)
Aart Bik7593b992016-08-17 16:51:12 -070065 if mode == 'tint':
Aart Bik06fa9f02017-12-14 11:15:59 -080066 return TestRunnerArtIntOnTarget(dexer, debug_info, device)
Aart Bik7593b992016-08-17 16:51:12 -070067 if mode == 'topt':
Aart Bik06fa9f02017-12-14 11:15:59 -080068 return TestRunnerArtOptOnTarget(dexer, debug_info, device)
Aart Bik7593b992016-08-17 16:51:12 -070069 raise FatalError('Unknown execution mode')
70
Aart Bike0347482016-09-20 14:34:13 -070071
Aart Bik7593b992016-08-17 16:51:12 -070072#
73# Execution mode classes.
74#
75
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070076
Aart Bik7593b992016-08-17 16:51:12 -070077class TestRunner(object):
78 """Abstraction for running a test in a particular execution mode."""
79 __meta_class__ = abc.ABCMeta
80
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070081 @abc.abstractproperty
82 def description(self):
Aart Bik7593b992016-08-17 16:51:12 -070083 """Returns a description string of the execution mode."""
Aart Bik7593b992016-08-17 16:51:12 -070084
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070085 @abc.abstractproperty
86 def id(self):
Aart Bik7593b992016-08-17 16:51:12 -070087 """Returns a short string that uniquely identifies the execution mode."""
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070088
89 @property
90 def output_file(self):
91 return self.id + '_out.txt'
92
93 @abc.abstractmethod
94 def GetBisectionSearchArgs(self):
95 """Get arguments to pass to bisection search tool.
96
97 Returns:
98 list of strings - arguments for bisection search tool, or None if
99 runner is not bisectable
100 """
Aart Bik7593b992016-08-17 16:51:12 -0700101
102 @abc.abstractmethod
103 def CompileAndRunTest(self):
104 """Compile and run the generated test.
105
106 Ensures that the current Test.java in the temporary directory is compiled
107 and executed under the current execution mode. On success, transfers the
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700108 generated output to the file self.output_file in the temporary directory.
Aart Bik7593b992016-08-17 16:51:12 -0700109
110 Most nonzero return codes are assumed non-divergent, since systems may
111 exit in different ways. This is enforced by normalizing return codes.
112
113 Returns:
114 normalized return code
115 """
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700116
Aart Bik7593b992016-08-17 16:51:12 -0700117
Aart Bik7c598de2016-12-20 19:06:37 -0800118class TestRunnerWithHostCompilation(TestRunner):
119 """Abstract test runner that supports compilation on host."""
120
Aart Bik06fa9f02017-12-14 11:15:59 -0800121 def __init__(self, dexer, debug_info):
Aart Bik7c598de2016-12-20 19:06:37 -0800122 """Constructor for the runner with host compilation.
123
124 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800125 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800126 debug_info: boolean, if True include debugging info
Aart Bik7c598de2016-12-20 19:06:37 -0800127 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800128 self._dexer = dexer
129 self._debug_info = debug_info
Aart Bik7c598de2016-12-20 19:06:37 -0800130 self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.',
131 'Test.java']
Aart Bik7c598de2016-12-20 19:06:37 -0800132
133 def CompileOnHost(self):
Aart Bikad7d26f2017-12-12 15:58:21 -0800134 if self._dexer == 'dx' or self._dexer == 'd8':
Aart Bik06fa9f02017-12-14 11:15:59 -0800135 dbg = '-g' if self._debug_info else '-g:none'
Aart Bik380080d2018-01-12 15:15:01 -0800136 if RunCommand(['javac', '--release=8', dbg, 'Test.java'],
Aart Bik7c598de2016-12-20 19:06:37 -0800137 out=None, err=None, timeout=30) == RetCode.SUCCESS:
Aart Bikad7d26f2017-12-12 15:58:21 -0800138 dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx'
139 retc = RunCommand([dx, '--dex', '--output=classes.dex'] + glob('*.class'),
Aart Bik7c598de2016-12-20 19:06:37 -0800140 out=None, err='dxerr.txt', timeout=30)
141 else:
142 retc = RetCode.NOTCOMPILED
Aart Bikad7d26f2017-12-12 15:58:21 -0800143 elif self._dexer == 'jack':
Aart Bik7c598de2016-12-20 19:06:37 -0800144 retc = RunCommand(['jack'] + self._jack_args,
145 out=None, err='jackerr.txt', timeout=30)
Aart Bikad7d26f2017-12-12 15:58:21 -0800146 else:
147 raise FatalError('Unknown dexer: ' + self._dexer)
Aart Bik7c598de2016-12-20 19:06:37 -0800148 return retc
149
150
Aart Bik7593b992016-08-17 16:51:12 -0700151class TestRunnerRIOnHost(TestRunner):
152 """Concrete test runner of the reference implementation on host."""
153
Aart Bik06fa9f02017-12-14 11:15:59 -0800154 def __init__(self, debug_info):
155 """Constructor for the runner with host compilation.
156
157 Args:
158 debug_info: boolean, if True include debugging info
159 """
160 self._debug_info = debug_info
161
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700162 @property
163 def description(self):
164 return 'RI on host'
165
166 @property
167 def id(self):
168 return 'RI'
Aart Bik7593b992016-08-17 16:51:12 -0700169
170 def CompileAndRunTest(self):
Aart Bik06fa9f02017-12-14 11:15:59 -0800171 dbg = '-g' if self._debug_info else '-g:none'
Aart Bik74478e62018-01-30 09:56:01 -0800172 if RunCommand(['javac', '--release=8', dbg, 'Test.java'],
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700173 out=None, err=None, timeout=30) == RetCode.SUCCESS:
174 retc = RunCommand(['java', 'Test'], self.output_file, err=None)
Aart Bik7593b992016-08-17 16:51:12 -0700175 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700176 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700177 return retc
178
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700179 def GetBisectionSearchArgs(self):
180 return None
Aart Bik7593b992016-08-17 16:51:12 -0700181
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700182
Aart Bik7c598de2016-12-20 19:06:37 -0800183class TestRunnerArtOnHost(TestRunnerWithHostCompilation):
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700184 """Abstract test runner of Art on host."""
185
Aart Bik06fa9f02017-12-14 11:15:59 -0800186 def __init__(self, dexer, debug_info, extra_args=None):
Aart Bik7593b992016-08-17 16:51:12 -0700187 """Constructor for the Art on host tester.
188
189 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800190 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800191 debug_info: boolean, if True include debugging info
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700192 extra_args: list of strings, extra arguments for dalvikvm
Aart Bik7593b992016-08-17 16:51:12 -0700193 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800194 super().__init__(dexer, debug_info)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700195 self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex']
196 if extra_args is not None:
197 self._art_cmd += extra_args
198 self._art_cmd.append('Test')
Aart Bik7593b992016-08-17 16:51:12 -0700199
200 def CompileAndRunTest(self):
Aart Bik7c598de2016-12-20 19:06:37 -0800201 if self.CompileOnHost() == RetCode.SUCCESS:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700202 retc = RunCommand(self._art_cmd, self.output_file, 'arterr.txt')
Aart Bik7593b992016-08-17 16:51:12 -0700203 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700204 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700205 return retc
206
Aart Bik7593b992016-08-17 16:51:12 -0700207
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700208class TestRunnerArtIntOnHost(TestRunnerArtOnHost):
209 """Concrete test runner of interpreter mode Art on host."""
210
Aart Bik06fa9f02017-12-14 11:15:59 -0800211 def __init__(self, dexer, debug_info):
Aart Bik7c598de2016-12-20 19:06:37 -0800212 """Constructor for the Art on host tester (interpreter).
213
214 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800215 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800216 debug_info: boolean, if True include debugging info
Aart Bik7c598de2016-12-20 19:06:37 -0800217 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800218 super().__init__(dexer, debug_info, ['-Xint'])
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700219
220 @property
221 def description(self):
222 return 'Art interpreter on host'
223
224 @property
225 def id(self):
226 return 'HInt'
227
228 def GetBisectionSearchArgs(self):
229 return None
230
231
232class TestRunnerArtOptOnHost(TestRunnerArtOnHost):
233 """Concrete test runner of optimizing compiler mode Art on host."""
234
Aart Bik06fa9f02017-12-14 11:15:59 -0800235 def __init__(self, dexer, debug_info):
Aart Bik7c598de2016-12-20 19:06:37 -0800236 """Constructor for the Art on host tester (optimizing).
237
238 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800239 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800240 debug_info: boolean, if True include debugging info
Aart Bik7c598de2016-12-20 19:06:37 -0800241 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800242 super().__init__(dexer, debug_info, None)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700243
244 @property
245 def description(self):
246 return 'Art optimizing on host'
247
248 @property
249 def id(self):
250 return 'HOpt'
251
252 def GetBisectionSearchArgs(self):
253 cmd_str = CommandListToCommandString(
254 self._art_cmd[0:2] + ['{ARGS}'] + self._art_cmd[2:])
255 return ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
256
257
Aart Bik7c598de2016-12-20 19:06:37 -0800258class TestRunnerArtOnTarget(TestRunnerWithHostCompilation):
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700259 """Abstract test runner of Art on target."""
260
Aart Bik06fa9f02017-12-14 11:15:59 -0800261 def __init__(self, dexer, debug_info, device, extra_args=None):
Aart Bik7593b992016-08-17 16:51:12 -0700262 """Constructor for the Art on target tester.
263
264 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800265 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800266 debug_info: boolean, if True include debugging info
Aart Bikb16d4132016-08-19 15:45:11 -0700267 device: string, target device serial number (or None)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700268 extra_args: list of strings, extra arguments for dalvikvm
Aart Bik7593b992016-08-17 16:51:12 -0700269 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800270 super().__init__(dexer, debug_info)
Aart Bik842a4f32016-09-21 15:45:18 -0700271 self._test_env = DeviceTestEnv('jfuzz_', specific_device=device)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700272 self._dalvik_cmd = ['dalvikvm']
273 if extra_args is not None:
274 self._dalvik_cmd += extra_args
275 self._device = device
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700276 self._device_classpath = None
Aart Bik7593b992016-08-17 16:51:12 -0700277
278 def CompileAndRunTest(self):
Aart Bik7c598de2016-12-20 19:06:37 -0800279 if self.CompileOnHost() == RetCode.SUCCESS:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700280 self._device_classpath = self._test_env.PushClasspath('classes.dex')
281 cmd = self._dalvik_cmd + ['-cp', self._device_classpath, 'Test']
282 (output, retc) = self._test_env.RunCommand(
283 cmd, {'ANDROID_LOG_TAGS': '*:s'})
284 with open(self.output_file, 'w') as run_out:
285 run_out.write(output)
Aart Bik7593b992016-08-17 16:51:12 -0700286 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700287 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700288 return retc
289
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700290 def GetBisectionSearchArgs(self):
291 cmd_str = CommandListToCommandString(
292 self._dalvik_cmd + ['-cp',self._device_classpath, 'Test'])
293 cmd = ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
294 if self._device:
295 cmd += ['--device-serial', self._device]
296 else:
297 cmd.append('--device')
298 return cmd
299
300
301class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget):
302 """Concrete test runner of interpreter mode Art on target."""
303
Aart Bik06fa9f02017-12-14 11:15:59 -0800304 def __init__(self, dexer, debug_info, device):
Aart Bik7c598de2016-12-20 19:06:37 -0800305 """Constructor for the Art on target tester (interpreter).
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700306
307 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800308 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800309 debug_info: boolean, if True include debugging info
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700310 device: string, target device serial number (or None)
311 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800312 super().__init__(dexer, debug_info, device, ['-Xint'])
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700313
314 @property
315 def description(self):
316 return 'Art interpreter on target'
317
318 @property
319 def id(self):
320 return 'TInt'
321
322 def GetBisectionSearchArgs(self):
323 return None
324
325
326class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget):
327 """Concrete test runner of optimizing compiler mode Art on target."""
328
Aart Bik06fa9f02017-12-14 11:15:59 -0800329 def __init__(self, dexer, debug_info, device):
Aart Bik7c598de2016-12-20 19:06:37 -0800330 """Constructor for the Art on target tester (optimizing).
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700331
332 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800333 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800334 debug_info: boolean, if True include debugging info
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700335 device: string, target device serial number (or None)
336 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800337 super().__init__(dexer, debug_info, device, None)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700338
339 @property
340 def description(self):
341 return 'Art optimizing on target'
342
343 @property
344 def id(self):
345 return 'TOpt'
346
347 def GetBisectionSearchArgs(self):
348 cmd_str = CommandListToCommandString(
349 self._dalvik_cmd + ['-cp', self._device_classpath, 'Test'])
350 cmd = ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
351 if self._device:
352 cmd += ['--device-serial', self._device]
353 else:
354 cmd.append('--device')
355 return cmd
356
357
Aart Bik7593b992016-08-17 16:51:12 -0700358#
Aart Bike0347482016-09-20 14:34:13 -0700359# Tester class.
Aart Bik7593b992016-08-17 16:51:12 -0700360#
361
Aart Bik7593b992016-08-17 16:51:12 -0700362
Aart Bik842a4f32016-09-21 15:45:18 -0700363class JFuzzTester(object):
364 """Tester that runs JFuzz many times and report divergences."""
Aart Bik7593b992016-08-17 16:51:12 -0700365
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700366 def __init__(self, num_tests, device, mode1, mode2, jfuzz_args,
Aart Bik06fa9f02017-12-14 11:15:59 -0800367 report_script, true_divergence_only, dexer, debug_info):
Aart Bik7593b992016-08-17 16:51:12 -0700368 """Constructor for the tester.
369
370 Args:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700371 num_tests: int, number of tests to run
372 device: string, target device serial number (or None)
373 mode1: string, execution mode for first runner
374 mode2: string, execution mode for second runner
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700375 jfuzz_args: list of strings, additional arguments for jfuzz
376 report_script: string, path to script called for each divergence
Wojciech Staszkiewicz8569e522016-09-23 18:02:55 -0700377 true_divergence_only: boolean, if True don't bisect timeout divergences
Aart Bikad7d26f2017-12-12 15:58:21 -0800378 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800379 debug_info: boolean, if True include debugging info
Aart Bik7593b992016-08-17 16:51:12 -0700380 """
381 self._num_tests = num_tests
Aart Bikb16d4132016-08-19 15:45:11 -0700382 self._device = device
Aart Bik06fa9f02017-12-14 11:15:59 -0800383 self._runner1 = GetExecutionModeRunner(dexer, debug_info, device, mode1)
384 self._runner2 = GetExecutionModeRunner(dexer, debug_info, device, mode2)
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700385 self._jfuzz_args = jfuzz_args
386 self._report_script = report_script
Wojciech Staszkiewicz8569e522016-09-23 18:02:55 -0700387 self._true_divergence_only = true_divergence_only
Aart Bikad7d26f2017-12-12 15:58:21 -0800388 self._dexer = dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800389 self._debug_info = debug_info
Aart Bik7593b992016-08-17 16:51:12 -0700390 self._save_dir = None
Aart Bike0347482016-09-20 14:34:13 -0700391 self._results_dir = None
Aart Bik842a4f32016-09-21 15:45:18 -0700392 self._jfuzz_dir = None
Aart Bik7593b992016-08-17 16:51:12 -0700393 # Statistics.
394 self._test = 0
395 self._num_success = 0
396 self._num_not_compiled = 0
397 self._num_not_run = 0
398 self._num_timed_out = 0
399 self._num_divergences = 0
400
401 def __enter__(self):
402 """On entry, enters new temp directory after saving current directory.
403
404 Raises:
405 FatalError: error when temp directory cannot be constructed
406 """
407 self._save_dir = os.getcwd()
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700408 self._results_dir = mkdtemp(dir='/tmp/')
Aart Bik842a4f32016-09-21 15:45:18 -0700409 self._jfuzz_dir = mkdtemp(dir=self._results_dir)
410 if self._results_dir is None or self._jfuzz_dir is None:
Aart Bik7593b992016-08-17 16:51:12 -0700411 raise FatalError('Cannot obtain temp directory')
Aart Bik842a4f32016-09-21 15:45:18 -0700412 os.chdir(self._jfuzz_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700413 return self
414
415 def __exit__(self, etype, evalue, etraceback):
416 """On exit, re-enters previously saved current directory and cleans up."""
417 os.chdir(self._save_dir)
Aart Bik842a4f32016-09-21 15:45:18 -0700418 shutil.rmtree(self._jfuzz_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700419 if self._num_divergences == 0:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700420 shutil.rmtree(self._results_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700421
422 def Run(self):
Aart Bik842a4f32016-09-21 15:45:18 -0700423 """Runs JFuzz many times and report divergences."""
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700424 print()
Aart Bik842a4f32016-09-21 15:45:18 -0700425 print('**\n**** JFuzz Testing\n**')
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700426 print()
427 print('#Tests :', self._num_tests)
428 print('Device :', self._device)
429 print('Directory :', self._results_dir)
430 print('Exec-mode1:', self._runner1.description)
431 print('Exec-mode2:', self._runner2.description)
Aart Bikad7d26f2017-12-12 15:58:21 -0800432 print('Dexer :', self._dexer)
Aart Bik06fa9f02017-12-14 11:15:59 -0800433 print('Debug-info:', self._debug_info)
Aart Bik9d537312016-09-15 10:42:02 -0700434 print()
Aart Bik7593b992016-08-17 16:51:12 -0700435 self.ShowStats()
436 for self._test in range(1, self._num_tests + 1):
Aart Bik842a4f32016-09-21 15:45:18 -0700437 self.RunJFuzzTest()
Aart Bik7593b992016-08-17 16:51:12 -0700438 self.ShowStats()
439 if self._num_divergences == 0:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700440 print('\n\nsuccess (no divergences)\n')
Aart Bik7593b992016-08-17 16:51:12 -0700441 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700442 print('\n\nfailure (divergences)\n')
Aart Bik7593b992016-08-17 16:51:12 -0700443
444 def ShowStats(self):
445 """Shows current statistics (on same line) while tester is running."""
Aart Bike0347482016-09-20 14:34:13 -0700446 print('\rTests:', self._test,
447 'Success:', self._num_success,
448 'Not-compiled:', self._num_not_compiled,
449 'Not-run:', self._num_not_run,
450 'Timed-out:', self._num_timed_out,
451 'Divergences:', self._num_divergences,
452 end='')
Aart Bik7593b992016-08-17 16:51:12 -0700453 sys.stdout.flush()
454
Aart Bik842a4f32016-09-21 15:45:18 -0700455 def RunJFuzzTest(self):
456 """Runs a single JFuzz test, comparing two execution modes."""
Aart Bik7593b992016-08-17 16:51:12 -0700457 self.ConstructTest()
458 retc1 = self._runner1.CompileAndRunTest()
459 retc2 = self._runner2.CompileAndRunTest()
460 self.CheckForDivergence(retc1, retc2)
461 self.CleanupTest()
462
463 def ConstructTest(self):
Aart Bik842a4f32016-09-21 15:45:18 -0700464 """Use JFuzz to generate next Test.java test.
Aart Bik7593b992016-08-17 16:51:12 -0700465
466 Raises:
Aart Bik842a4f32016-09-21 15:45:18 -0700467 FatalError: error when jfuzz fails
Aart Bik7593b992016-08-17 16:51:12 -0700468 """
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700469 if (RunCommand(['jfuzz'] + self._jfuzz_args, out='Test.java', err=None)
470 != RetCode.SUCCESS):
Aart Bik842a4f32016-09-21 15:45:18 -0700471 raise FatalError('Unexpected error while running JFuzz')
Aart Bik7593b992016-08-17 16:51:12 -0700472
473 def CheckForDivergence(self, retc1, retc2):
474 """Checks for divergences and updates statistics.
475
476 Args:
477 retc1: int, normalized return code of first runner
478 retc2: int, normalized return code of second runner
479 """
480 if retc1 == retc2:
Aart Bik38f63012016-12-14 17:16:10 -0800481 # No divergence in return code.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700482 if retc1 == RetCode.SUCCESS:
Aart Bik7593b992016-08-17 16:51:12 -0700483 # Both compilations and runs were successful, inspect generated output.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700484 runner1_out = self._runner1.output_file
485 runner2_out = self._runner2.output_file
486 if not filecmp.cmp(runner1_out, runner2_out, shallow=False):
Aart Bik38f63012016-12-14 17:16:10 -0800487 # Divergence in output.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700488 self.ReportDivergence(retc1, retc2, is_output_divergence=True)
Aart Bik7593b992016-08-17 16:51:12 -0700489 else:
Aart Bik38f63012016-12-14 17:16:10 -0800490 # No divergence in output.
Aart Bik7593b992016-08-17 16:51:12 -0700491 self._num_success += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700492 elif retc1 == RetCode.TIMEOUT:
Aart Bik7593b992016-08-17 16:51:12 -0700493 self._num_timed_out += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700494 elif retc1 == RetCode.NOTCOMPILED:
Aart Bik7593b992016-08-17 16:51:12 -0700495 self._num_not_compiled += 1
496 else:
497 self._num_not_run += 1
498 else:
Aart Bik38f63012016-12-14 17:16:10 -0800499 # Divergence in return code.
Aart Bikdaacc5e2017-05-16 12:53:49 -0700500 if self._true_divergence_only:
501 # When only true divergences are requested, any divergence in return
502 # code where one is a time out is treated as a regular time out.
503 if RetCode.TIMEOUT in (retc1, retc2):
504 self._num_timed_out += 1
505 return
506 # When only true divergences are requested, a runtime crash in just
507 # the RI is treated as if not run at all.
508 if retc1 == RetCode.ERROR and retc2 == RetCode.SUCCESS:
509 if self._runner1.GetBisectionSearchArgs() is None:
510 self._num_not_run += 1
511 return
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700512 self.ReportDivergence(retc1, retc2, is_output_divergence=False)
Aart Bik7593b992016-08-17 16:51:12 -0700513
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700514 def GetCurrentDivergenceDir(self):
515 return self._results_dir + '/divergence' + str(self._num_divergences)
516
517 def ReportDivergence(self, retc1, retc2, is_output_divergence):
Aart Bik7593b992016-08-17 16:51:12 -0700518 """Reports and saves a divergence."""
519 self._num_divergences += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700520 print('\n' + str(self._num_divergences), end='')
521 if is_output_divergence:
522 print(' divergence in output')
523 else:
524 print(' divergence in return code: ' + retc1.name + ' vs. ' +
525 retc2.name)
Aart Bik7593b992016-08-17 16:51:12 -0700526 # Save.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700527 ddir = self.GetCurrentDivergenceDir()
528 os.mkdir(ddir)
529 for f in glob('*.txt') + ['Test.java']:
530 shutil.copy(f, ddir)
Aart Bik38f63012016-12-14 17:16:10 -0800531 # Maybe run bisection bug search.
532 if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
533 self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
534 # Call reporting script.
535 if self._report_script:
536 self.RunReportScript(retc1, retc2, is_output_divergence)
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700537
538 def RunReportScript(self, retc1, retc2, is_output_divergence):
539 """Runs report script."""
540 try:
541 title = "Divergence between {0} and {1} (found with fuzz testing)".format(
542 self._runner1.description, self._runner2.description)
543 # Prepare divergence comment.
544 jfuzz_cmd_and_version = subprocess.check_output(
545 ['grep', '-o', 'jfuzz.*', 'Test.java'], universal_newlines=True)
546 (jfuzz_cmd_str, jfuzz_ver) = jfuzz_cmd_and_version.split('(')
547 # Strip right parenthesis and new line.
548 jfuzz_ver = jfuzz_ver[:-2]
549 jfuzz_args = ['\'-{0}\''.format(arg)
550 for arg in jfuzz_cmd_str.strip().split(' -')][1:]
551 wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args]
Aart Bik3a9aa5b2017-07-27 09:47:35 -0700552 repro_cmd_str = (os.path.basename(__file__) +
Aart Bikad7d26f2017-12-12 15:58:21 -0800553 ' --num_tests=1 --dexer=' + self._dexer +
Aart Bikbd82d172017-12-15 11:31:57 -0800554 (' --debug_info ' if self._debug_info else ' ') +
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700555 ' '.join(wrapped_args))
556 comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format(
557 jfuzz_ver, jfuzz_cmd_str, repro_cmd_str)
558 if is_output_divergence:
559 (output, _, _) = RunCommandForOutput(
560 ['diff', self._runner1.output_file, self._runner2.output_file],
561 None, subprocess.PIPE, subprocess.STDOUT)
562 comment += 'Diff:\n' + output
563 else:
564 comment += '{0} vs {1}\n'.format(retc1, retc2)
565 # Prepare report script command.
566 script_cmd = [self._report_script, title, comment]
567 ddir = self.GetCurrentDivergenceDir()
568 bisection_out_files = glob(ddir + '/*_bisection_out.txt')
569 if bisection_out_files:
570 script_cmd += ['--bisection_out', bisection_out_files[0]]
571 subprocess.check_call(script_cmd, stdout=DEVNULL, stderr=DEVNULL)
572 except subprocess.CalledProcessError as err:
573 print('Failed to run report script.\n', err)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700574
575 def RunBisectionSearch(self, args, expected_retcode, expected_output,
576 runner_id):
577 ddir = self.GetCurrentDivergenceDir()
578 outfile_path = ddir + '/' + runner_id + '_bisection_out.txt'
579 logfile_path = ddir + '/' + runner_id + '_bisection_log.txt'
580 errfile_path = ddir + '/' + runner_id + '_bisection_err.txt'
581 args = list(args) + ['--logfile', logfile_path, '--cleanup']
582 args += ['--expected-retcode', expected_retcode.name]
583 if expected_output:
584 args += ['--expected-output', expected_output]
585 bisection_search_path = os.path.join(
586 GetEnvVariableOrError('ANDROID_BUILD_TOP'),
587 'art/tools/bisection_search/bisection_search.py')
588 if RunCommand([bisection_search_path] + args, out=outfile_path,
589 err=errfile_path, timeout=300) == RetCode.TIMEOUT:
590 print('Bisection search TIMEOUT')
591
592 def MaybeBisectDivergence(self, retc1, retc2, is_output_divergence):
593 bisection_args1 = self._runner1.GetBisectionSearchArgs()
594 bisection_args2 = self._runner2.GetBisectionSearchArgs()
595 if is_output_divergence:
596 maybe_output1 = self._runner1.output_file
597 maybe_output2 = self._runner2.output_file
598 else:
599 maybe_output1 = maybe_output2 = None
600 if bisection_args1 is not None:
601 self.RunBisectionSearch(bisection_args1, retc2, maybe_output2,
602 self._runner1.id)
603 if bisection_args2 is not None:
604 self.RunBisectionSearch(bisection_args2, retc1, maybe_output1,
605 self._runner2.id)
Aart Bik7593b992016-08-17 16:51:12 -0700606
607 def CleanupTest(self):
608 """Cleans up after a single test run."""
Aart Bik842a4f32016-09-21 15:45:18 -0700609 for file_name in os.listdir(self._jfuzz_dir):
610 file_path = os.path.join(self._jfuzz_dir, file_name)
Aart Bike0347482016-09-20 14:34:13 -0700611 if os.path.isfile(file_path):
612 os.unlink(file_path)
613 elif os.path.isdir(file_path):
614 shutil.rmtree(file_path)
Aart Bik7593b992016-08-17 16:51:12 -0700615
616
617def main():
618 # Handle arguments.
619 parser = argparse.ArgumentParser()
Aart Bikad7d26f2017-12-12 15:58:21 -0800620 parser.add_argument('--num_tests', default=10000, type=int,
621 help='number of tests to run')
Aart Bikb16d4132016-08-19 15:45:11 -0700622 parser.add_argument('--device', help='target device serial number')
Aart Bik7593b992016-08-17 16:51:12 -0700623 parser.add_argument('--mode1', default='ri',
624 help='execution mode 1 (default: ri)')
625 parser.add_argument('--mode2', default='hopt',
626 help='execution mode 2 (default: hopt)')
Aart Bikad7d26f2017-12-12 15:58:21 -0800627 parser.add_argument('--report_script',
628 help='script called for each divergence')
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700629 parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args',
Aart Bikad7d26f2017-12-12 15:58:21 -0800630 action='append',
631 help='argument for jfuzz')
Wojciech Staszkiewicz8569e522016-09-23 18:02:55 -0700632 parser.add_argument('--true_divergence', default=False, action='store_true',
Aart Bikad7d26f2017-12-12 15:58:21 -0800633 help='do not bisect timeout divergences')
634 parser.add_argument('--dexer', default='dx', type=str,
635 help='defines dexer as dx, d8, or jack (default: dx)')
Aart Bik06fa9f02017-12-14 11:15:59 -0800636 parser.add_argument('--debug_info', default=False, action='store_true',
637 help='include debugging info')
Aart Bik7593b992016-08-17 16:51:12 -0700638 args = parser.parse_args()
639 if args.mode1 == args.mode2:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700640 raise FatalError('Identical execution modes given')
Aart Bik842a4f32016-09-21 15:45:18 -0700641 # Run the JFuzz tester.
Aart Bik7c598de2016-12-20 19:06:37 -0800642 with JFuzzTester(args.num_tests,
Aart Bik06fa9f02017-12-14 11:15:59 -0800643 args.device,
644 args.mode1, args.mode2,
645 args.jfuzz_args,
646 args.report_script,
647 args.true_divergence,
648 args.dexer,
649 args.debug_info) as fuzzer:
Aart Bik7593b992016-08-17 16:51:12 -0700650 fuzzer.Run()
651
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700652if __name__ == '__main__':
Aart Bik7593b992016-08-17 16:51:12 -0700653 main()