blob: f8bfd8dda7bf7d550d2fd229503827232b6c2114 [file] [log] [blame]
Aart Bikd432acd2018-03-08 11:48:27 -08001#!/usr/bin/env python3
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
Aart Bike0347482016-09-20 14:34:13 -070036from common.common import GetEnvVariableOrError
37from common.common import RunCommand
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -070038from common.common import RunCommandForOutput
Aart Bike0347482016-09-20 14:34:13 -070039from common.common import DeviceTestEnv
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070040
41# Return codes supported by bisection bug search.
42BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT)
Aart Bik7593b992016-08-17 16:51:12 -070043
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070044
Aart Bik06fa9f02017-12-14 11:15:59 -080045def GetExecutionModeRunner(dexer, debug_info, device, mode):
Aart Bik7593b992016-08-17 16:51:12 -070046 """Returns a runner for the given execution mode.
47
48 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -080049 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -080050 debug_info: boolean, if True include debugging info
Aart Bikb16d4132016-08-19 15:45:11 -070051 device: string, target device serial number (or None)
Aart Bik7593b992016-08-17 16:51:12 -070052 mode: string, execution mode
53 Returns:
54 TestRunner with given execution mode
55 Raises:
56 FatalError: error for unknown execution mode
57 """
58 if mode == 'ri':
Aart Bik06fa9f02017-12-14 11:15:59 -080059 return TestRunnerRIOnHost(debug_info)
Aart Bik7593b992016-08-17 16:51:12 -070060 if mode == 'hint':
Aart Bik06fa9f02017-12-14 11:15:59 -080061 return TestRunnerArtIntOnHost(dexer, debug_info)
Aart Bik7593b992016-08-17 16:51:12 -070062 if mode == 'hopt':
Aart Bik06fa9f02017-12-14 11:15:59 -080063 return TestRunnerArtOptOnHost(dexer, debug_info)
Aart Bik7593b992016-08-17 16:51:12 -070064 if mode == 'tint':
Aart Bik06fa9f02017-12-14 11:15:59 -080065 return TestRunnerArtIntOnTarget(dexer, debug_info, device)
Aart Bik7593b992016-08-17 16:51:12 -070066 if mode == 'topt':
Aart Bik06fa9f02017-12-14 11:15:59 -080067 return TestRunnerArtOptOnTarget(dexer, debug_info, device)
Aart Bik7593b992016-08-17 16:51:12 -070068 raise FatalError('Unknown execution mode')
69
Aart Bike0347482016-09-20 14:34:13 -070070
Aart Bik7593b992016-08-17 16:51:12 -070071#
72# Execution mode classes.
73#
74
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070075
Aart Bik7593b992016-08-17 16:51:12 -070076class TestRunner(object):
77 """Abstraction for running a test in a particular execution mode."""
78 __meta_class__ = abc.ABCMeta
79
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070080 @abc.abstractproperty
81 def description(self):
Aart Bik7593b992016-08-17 16:51:12 -070082 """Returns a description string of the execution mode."""
Aart Bik7593b992016-08-17 16:51:12 -070083
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070084 @abc.abstractproperty
85 def id(self):
Aart Bik7593b992016-08-17 16:51:12 -070086 """Returns a short string that uniquely identifies the execution mode."""
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -070087
88 @property
89 def output_file(self):
90 return self.id + '_out.txt'
91
92 @abc.abstractmethod
93 def GetBisectionSearchArgs(self):
94 """Get arguments to pass to bisection search tool.
95
96 Returns:
97 list of strings - arguments for bisection search tool, or None if
98 runner is not bisectable
99 """
Aart Bik7593b992016-08-17 16:51:12 -0700100
101 @abc.abstractmethod
102 def CompileAndRunTest(self):
103 """Compile and run the generated test.
104
105 Ensures that the current Test.java in the temporary directory is compiled
106 and executed under the current execution mode. On success, transfers the
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700107 generated output to the file self.output_file in the temporary directory.
Aart Bik7593b992016-08-17 16:51:12 -0700108
109 Most nonzero return codes are assumed non-divergent, since systems may
110 exit in different ways. This is enforced by normalizing return codes.
111
112 Returns:
113 normalized return code
114 """
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700115
Aart Bik7593b992016-08-17 16:51:12 -0700116
Aart Bik7c598de2016-12-20 19:06:37 -0800117class TestRunnerWithHostCompilation(TestRunner):
118 """Abstract test runner that supports compilation on host."""
119
Aart Bik06fa9f02017-12-14 11:15:59 -0800120 def __init__(self, dexer, debug_info):
Aart Bik7c598de2016-12-20 19:06:37 -0800121 """Constructor for the runner with host compilation.
122
123 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800124 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800125 debug_info: boolean, if True include debugging info
Aart Bik7c598de2016-12-20 19:06:37 -0800126 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800127 self._dexer = dexer
128 self._debug_info = debug_info
Aart Bik7c598de2016-12-20 19:06:37 -0800129
130 def CompileOnHost(self):
Aart Bikad7d26f2017-12-12 15:58:21 -0800131 if self._dexer == 'dx' or self._dexer == 'd8':
Aart Bik06fa9f02017-12-14 11:15:59 -0800132 dbg = '-g' if self._debug_info else '-g:none'
Aart Bik380080d2018-01-12 15:15:01 -0800133 if RunCommand(['javac', '--release=8', dbg, 'Test.java'],
Aart Bik7c598de2016-12-20 19:06:37 -0800134 out=None, err=None, timeout=30) == RetCode.SUCCESS:
Aart Bikad7d26f2017-12-12 15:58:21 -0800135 dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx'
136 retc = RunCommand([dx, '--dex', '--output=classes.dex'] + glob('*.class'),
Aart Bik7c598de2016-12-20 19:06:37 -0800137 out=None, err='dxerr.txt', timeout=30)
138 else:
139 retc = RetCode.NOTCOMPILED
Aart Bikad7d26f2017-12-12 15:58:21 -0800140 else:
141 raise FatalError('Unknown dexer: ' + self._dexer)
Aart Bik7c598de2016-12-20 19:06:37 -0800142 return retc
143
144
Aart Bik7593b992016-08-17 16:51:12 -0700145class TestRunnerRIOnHost(TestRunner):
146 """Concrete test runner of the reference implementation on host."""
147
Aart Bik06fa9f02017-12-14 11:15:59 -0800148 def __init__(self, debug_info):
149 """Constructor for the runner with host compilation.
150
151 Args:
152 debug_info: boolean, if True include debugging info
153 """
154 self._debug_info = debug_info
155
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700156 @property
157 def description(self):
158 return 'RI on host'
159
160 @property
161 def id(self):
162 return 'RI'
Aart Bik7593b992016-08-17 16:51:12 -0700163
164 def CompileAndRunTest(self):
Aart Bik06fa9f02017-12-14 11:15:59 -0800165 dbg = '-g' if self._debug_info else '-g:none'
Aart Bik74478e62018-01-30 09:56:01 -0800166 if RunCommand(['javac', '--release=8', dbg, 'Test.java'],
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700167 out=None, err=None, timeout=30) == RetCode.SUCCESS:
168 retc = RunCommand(['java', 'Test'], self.output_file, err=None)
Aart Bik7593b992016-08-17 16:51:12 -0700169 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700170 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700171 return retc
172
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700173 def GetBisectionSearchArgs(self):
174 return None
Aart Bik7593b992016-08-17 16:51:12 -0700175
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700176
Aart Bik7c598de2016-12-20 19:06:37 -0800177class TestRunnerArtOnHost(TestRunnerWithHostCompilation):
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700178 """Abstract test runner of Art on host."""
179
Aart Bik06fa9f02017-12-14 11:15:59 -0800180 def __init__(self, dexer, debug_info, extra_args=None):
Aart Bik7593b992016-08-17 16:51:12 -0700181 """Constructor for the Art on host tester.
182
183 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800184 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800185 debug_info: boolean, if True include debugging info
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700186 extra_args: list of strings, extra arguments for dalvikvm
Aart Bik7593b992016-08-17 16:51:12 -0700187 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800188 super().__init__(dexer, debug_info)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700189 self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex']
190 if extra_args is not None:
191 self._art_cmd += extra_args
192 self._art_cmd.append('Test')
Aart Bik7593b992016-08-17 16:51:12 -0700193
194 def CompileAndRunTest(self):
Aart Bik7c598de2016-12-20 19:06:37 -0800195 if self.CompileOnHost() == RetCode.SUCCESS:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700196 retc = RunCommand(self._art_cmd, self.output_file, 'arterr.txt')
Aart Bik7593b992016-08-17 16:51:12 -0700197 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700198 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700199 return retc
200
Aart Bik7593b992016-08-17 16:51:12 -0700201
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700202class TestRunnerArtIntOnHost(TestRunnerArtOnHost):
203 """Concrete test runner of interpreter mode Art on host."""
204
Aart Bik06fa9f02017-12-14 11:15:59 -0800205 def __init__(self, dexer, debug_info):
Aart Bik7c598de2016-12-20 19:06:37 -0800206 """Constructor for the Art on host tester (interpreter).
207
208 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800209 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800210 debug_info: boolean, if True include debugging info
Aart Bik7c598de2016-12-20 19:06:37 -0800211 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800212 super().__init__(dexer, debug_info, ['-Xint'])
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700213
214 @property
215 def description(self):
216 return 'Art interpreter on host'
217
218 @property
219 def id(self):
220 return 'HInt'
221
222 def GetBisectionSearchArgs(self):
223 return None
224
225
226class TestRunnerArtOptOnHost(TestRunnerArtOnHost):
227 """Concrete test runner of optimizing compiler mode Art on host."""
228
Aart Bik06fa9f02017-12-14 11:15:59 -0800229 def __init__(self, dexer, debug_info):
Aart Bik7c598de2016-12-20 19:06:37 -0800230 """Constructor for the Art on host tester (optimizing).
231
232 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800233 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800234 debug_info: boolean, if True include debugging info
Aart Bik7c598de2016-12-20 19:06:37 -0800235 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800236 super().__init__(dexer, debug_info, None)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700237
238 @property
239 def description(self):
240 return 'Art optimizing on host'
241
242 @property
243 def id(self):
244 return 'HOpt'
245
246 def GetBisectionSearchArgs(self):
247 cmd_str = CommandListToCommandString(
248 self._art_cmd[0:2] + ['{ARGS}'] + self._art_cmd[2:])
249 return ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
250
251
Aart Bik7c598de2016-12-20 19:06:37 -0800252class TestRunnerArtOnTarget(TestRunnerWithHostCompilation):
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700253 """Abstract test runner of Art on target."""
254
Aart Bik06fa9f02017-12-14 11:15:59 -0800255 def __init__(self, dexer, debug_info, device, extra_args=None):
Aart Bik7593b992016-08-17 16:51:12 -0700256 """Constructor for the Art on target tester.
257
258 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800259 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800260 debug_info: boolean, if True include debugging info
Aart Bikb16d4132016-08-19 15:45:11 -0700261 device: string, target device serial number (or None)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700262 extra_args: list of strings, extra arguments for dalvikvm
Aart Bik7593b992016-08-17 16:51:12 -0700263 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800264 super().__init__(dexer, debug_info)
Aart Bik842a4f32016-09-21 15:45:18 -0700265 self._test_env = DeviceTestEnv('jfuzz_', specific_device=device)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700266 self._dalvik_cmd = ['dalvikvm']
267 if extra_args is not None:
268 self._dalvik_cmd += extra_args
269 self._device = device
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700270 self._device_classpath = None
Aart Bik7593b992016-08-17 16:51:12 -0700271
272 def CompileAndRunTest(self):
Aart Bik7c598de2016-12-20 19:06:37 -0800273 if self.CompileOnHost() == RetCode.SUCCESS:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700274 self._device_classpath = self._test_env.PushClasspath('classes.dex')
275 cmd = self._dalvik_cmd + ['-cp', self._device_classpath, 'Test']
276 (output, retc) = self._test_env.RunCommand(
277 cmd, {'ANDROID_LOG_TAGS': '*:s'})
278 with open(self.output_file, 'w') as run_out:
279 run_out.write(output)
Aart Bik7593b992016-08-17 16:51:12 -0700280 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700281 retc = RetCode.NOTCOMPILED
Aart Bik7593b992016-08-17 16:51:12 -0700282 return retc
283
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700284 def GetBisectionSearchArgs(self):
285 cmd_str = CommandListToCommandString(
286 self._dalvik_cmd + ['-cp',self._device_classpath, 'Test'])
287 cmd = ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
288 if self._device:
289 cmd += ['--device-serial', self._device]
290 else:
291 cmd.append('--device')
292 return cmd
293
294
295class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget):
296 """Concrete test runner of interpreter mode Art on target."""
297
Aart Bik06fa9f02017-12-14 11:15:59 -0800298 def __init__(self, dexer, debug_info, device):
Aart Bik7c598de2016-12-20 19:06:37 -0800299 """Constructor for the Art on target tester (interpreter).
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700300
301 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800302 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800303 debug_info: boolean, if True include debugging info
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700304 device: string, target device serial number (or None)
305 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800306 super().__init__(dexer, debug_info, device, ['-Xint'])
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700307
308 @property
309 def description(self):
310 return 'Art interpreter on target'
311
312 @property
313 def id(self):
314 return 'TInt'
315
316 def GetBisectionSearchArgs(self):
317 return None
318
319
320class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget):
321 """Concrete test runner of optimizing compiler mode Art on target."""
322
Aart Bik06fa9f02017-12-14 11:15:59 -0800323 def __init__(self, dexer, debug_info, device):
Aart Bik7c598de2016-12-20 19:06:37 -0800324 """Constructor for the Art on target tester (optimizing).
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700325
326 Args:
Aart Bikad7d26f2017-12-12 15:58:21 -0800327 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800328 debug_info: boolean, if True include debugging info
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700329 device: string, target device serial number (or None)
330 """
Aart Bik06fa9f02017-12-14 11:15:59 -0800331 super().__init__(dexer, debug_info, device, None)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700332
333 @property
334 def description(self):
335 return 'Art optimizing on target'
336
337 @property
338 def id(self):
339 return 'TOpt'
340
341 def GetBisectionSearchArgs(self):
342 cmd_str = CommandListToCommandString(
343 self._dalvik_cmd + ['-cp', self._device_classpath, 'Test'])
344 cmd = ['--raw-cmd={0}'.format(cmd_str), '--timeout', str(30)]
345 if self._device:
346 cmd += ['--device-serial', self._device]
347 else:
348 cmd.append('--device')
349 return cmd
350
351
Aart Bik7593b992016-08-17 16:51:12 -0700352#
Aart Bike0347482016-09-20 14:34:13 -0700353# Tester class.
Aart Bik7593b992016-08-17 16:51:12 -0700354#
355
Aart Bik7593b992016-08-17 16:51:12 -0700356
Aart Bik842a4f32016-09-21 15:45:18 -0700357class JFuzzTester(object):
358 """Tester that runs JFuzz many times and report divergences."""
Aart Bik7593b992016-08-17 16:51:12 -0700359
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700360 def __init__(self, num_tests, device, mode1, mode2, jfuzz_args,
Aart Bik06fa9f02017-12-14 11:15:59 -0800361 report_script, true_divergence_only, dexer, debug_info):
Aart Bik7593b992016-08-17 16:51:12 -0700362 """Constructor for the tester.
363
364 Args:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700365 num_tests: int, number of tests to run
366 device: string, target device serial number (or None)
367 mode1: string, execution mode for first runner
368 mode2: string, execution mode for second runner
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700369 jfuzz_args: list of strings, additional arguments for jfuzz
370 report_script: string, path to script called for each divergence
Wojciech Staszkiewicz8569e522016-09-23 18:02:55 -0700371 true_divergence_only: boolean, if True don't bisect timeout divergences
Aart Bikad7d26f2017-12-12 15:58:21 -0800372 dexer: string, defines dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800373 debug_info: boolean, if True include debugging info
Aart Bik7593b992016-08-17 16:51:12 -0700374 """
375 self._num_tests = num_tests
Aart Bikb16d4132016-08-19 15:45:11 -0700376 self._device = device
Aart Bik06fa9f02017-12-14 11:15:59 -0800377 self._runner1 = GetExecutionModeRunner(dexer, debug_info, device, mode1)
378 self._runner2 = GetExecutionModeRunner(dexer, debug_info, device, mode2)
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700379 self._jfuzz_args = jfuzz_args
380 self._report_script = report_script
Wojciech Staszkiewicz8569e522016-09-23 18:02:55 -0700381 self._true_divergence_only = true_divergence_only
Aart Bikad7d26f2017-12-12 15:58:21 -0800382 self._dexer = dexer
Aart Bik06fa9f02017-12-14 11:15:59 -0800383 self._debug_info = debug_info
Aart Bik7593b992016-08-17 16:51:12 -0700384 self._save_dir = None
Aart Bike0347482016-09-20 14:34:13 -0700385 self._results_dir = None
Aart Bik842a4f32016-09-21 15:45:18 -0700386 self._jfuzz_dir = None
Aart Bik7593b992016-08-17 16:51:12 -0700387 # Statistics.
388 self._test = 0
389 self._num_success = 0
390 self._num_not_compiled = 0
391 self._num_not_run = 0
392 self._num_timed_out = 0
393 self._num_divergences = 0
394
395 def __enter__(self):
396 """On entry, enters new temp directory after saving current directory.
397
398 Raises:
399 FatalError: error when temp directory cannot be constructed
400 """
401 self._save_dir = os.getcwd()
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700402 self._results_dir = mkdtemp(dir='/tmp/')
Aart Bik842a4f32016-09-21 15:45:18 -0700403 self._jfuzz_dir = mkdtemp(dir=self._results_dir)
404 if self._results_dir is None or self._jfuzz_dir is None:
Aart Bik7593b992016-08-17 16:51:12 -0700405 raise FatalError('Cannot obtain temp directory')
Aart Bik842a4f32016-09-21 15:45:18 -0700406 os.chdir(self._jfuzz_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700407 return self
408
409 def __exit__(self, etype, evalue, etraceback):
410 """On exit, re-enters previously saved current directory and cleans up."""
411 os.chdir(self._save_dir)
Aart Bik842a4f32016-09-21 15:45:18 -0700412 shutil.rmtree(self._jfuzz_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700413 if self._num_divergences == 0:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700414 shutil.rmtree(self._results_dir)
Aart Bik7593b992016-08-17 16:51:12 -0700415
416 def Run(self):
Aart Bik842a4f32016-09-21 15:45:18 -0700417 """Runs JFuzz many times and report divergences."""
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700418 print()
Aart Bik842a4f32016-09-21 15:45:18 -0700419 print('**\n**** JFuzz Testing\n**')
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700420 print()
421 print('#Tests :', self._num_tests)
422 print('Device :', self._device)
423 print('Directory :', self._results_dir)
424 print('Exec-mode1:', self._runner1.description)
425 print('Exec-mode2:', self._runner2.description)
Aart Bikad7d26f2017-12-12 15:58:21 -0800426 print('Dexer :', self._dexer)
Aart Bik06fa9f02017-12-14 11:15:59 -0800427 print('Debug-info:', self._debug_info)
Aart Bik9d537312016-09-15 10:42:02 -0700428 print()
Aart Bik7593b992016-08-17 16:51:12 -0700429 self.ShowStats()
430 for self._test in range(1, self._num_tests + 1):
Aart Bik842a4f32016-09-21 15:45:18 -0700431 self.RunJFuzzTest()
Aart Bik7593b992016-08-17 16:51:12 -0700432 self.ShowStats()
433 if self._num_divergences == 0:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700434 print('\n\nsuccess (no divergences)\n')
Aart Bik7593b992016-08-17 16:51:12 -0700435 else:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700436 print('\n\nfailure (divergences)\n')
Aart Bik7593b992016-08-17 16:51:12 -0700437
438 def ShowStats(self):
439 """Shows current statistics (on same line) while tester is running."""
Aart Bike0347482016-09-20 14:34:13 -0700440 print('\rTests:', self._test,
441 'Success:', self._num_success,
442 'Not-compiled:', self._num_not_compiled,
443 'Not-run:', self._num_not_run,
444 'Timed-out:', self._num_timed_out,
445 'Divergences:', self._num_divergences,
446 end='')
Aart Bik7593b992016-08-17 16:51:12 -0700447 sys.stdout.flush()
448
Aart Bik842a4f32016-09-21 15:45:18 -0700449 def RunJFuzzTest(self):
450 """Runs a single JFuzz test, comparing two execution modes."""
Aart Bik7593b992016-08-17 16:51:12 -0700451 self.ConstructTest()
452 retc1 = self._runner1.CompileAndRunTest()
453 retc2 = self._runner2.CompileAndRunTest()
454 self.CheckForDivergence(retc1, retc2)
455 self.CleanupTest()
456
457 def ConstructTest(self):
Aart Bik842a4f32016-09-21 15:45:18 -0700458 """Use JFuzz to generate next Test.java test.
Aart Bik7593b992016-08-17 16:51:12 -0700459
460 Raises:
Aart Bik842a4f32016-09-21 15:45:18 -0700461 FatalError: error when jfuzz fails
Aart Bik7593b992016-08-17 16:51:12 -0700462 """
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700463 if (RunCommand(['jfuzz'] + self._jfuzz_args, out='Test.java', err=None)
464 != RetCode.SUCCESS):
Aart Bik842a4f32016-09-21 15:45:18 -0700465 raise FatalError('Unexpected error while running JFuzz')
Aart Bik7593b992016-08-17 16:51:12 -0700466
467 def CheckForDivergence(self, retc1, retc2):
468 """Checks for divergences and updates statistics.
469
470 Args:
471 retc1: int, normalized return code of first runner
472 retc2: int, normalized return code of second runner
473 """
474 if retc1 == retc2:
Aart Bik38f63012016-12-14 17:16:10 -0800475 # No divergence in return code.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700476 if retc1 == RetCode.SUCCESS:
Aart Bik7593b992016-08-17 16:51:12 -0700477 # Both compilations and runs were successful, inspect generated output.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700478 runner1_out = self._runner1.output_file
479 runner2_out = self._runner2.output_file
480 if not filecmp.cmp(runner1_out, runner2_out, shallow=False):
Aart Bik38f63012016-12-14 17:16:10 -0800481 # Divergence in output.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700482 self.ReportDivergence(retc1, retc2, is_output_divergence=True)
Aart Bik7593b992016-08-17 16:51:12 -0700483 else:
Aart Bik38f63012016-12-14 17:16:10 -0800484 # No divergence in output.
Aart Bik7593b992016-08-17 16:51:12 -0700485 self._num_success += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700486 elif retc1 == RetCode.TIMEOUT:
Aart Bik7593b992016-08-17 16:51:12 -0700487 self._num_timed_out += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700488 elif retc1 == RetCode.NOTCOMPILED:
Aart Bik7593b992016-08-17 16:51:12 -0700489 self._num_not_compiled += 1
490 else:
491 self._num_not_run += 1
492 else:
Aart Bik38f63012016-12-14 17:16:10 -0800493 # Divergence in return code.
Aart Bikdaacc5e2017-05-16 12:53:49 -0700494 if self._true_divergence_only:
495 # When only true divergences are requested, any divergence in return
496 # code where one is a time out is treated as a regular time out.
497 if RetCode.TIMEOUT in (retc1, retc2):
498 self._num_timed_out += 1
499 return
500 # When only true divergences are requested, a runtime crash in just
501 # the RI is treated as if not run at all.
502 if retc1 == RetCode.ERROR and retc2 == RetCode.SUCCESS:
503 if self._runner1.GetBisectionSearchArgs() is None:
504 self._num_not_run += 1
505 return
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700506 self.ReportDivergence(retc1, retc2, is_output_divergence=False)
Aart Bik7593b992016-08-17 16:51:12 -0700507
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700508 def GetCurrentDivergenceDir(self):
509 return self._results_dir + '/divergence' + str(self._num_divergences)
510
511 def ReportDivergence(self, retc1, retc2, is_output_divergence):
Aart Bik7593b992016-08-17 16:51:12 -0700512 """Reports and saves a divergence."""
513 self._num_divergences += 1
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700514 print('\n' + str(self._num_divergences), end='')
515 if is_output_divergence:
516 print(' divergence in output')
517 else:
518 print(' divergence in return code: ' + retc1.name + ' vs. ' +
519 retc2.name)
Aart Bik7593b992016-08-17 16:51:12 -0700520 # Save.
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700521 ddir = self.GetCurrentDivergenceDir()
522 os.mkdir(ddir)
523 for f in glob('*.txt') + ['Test.java']:
524 shutil.copy(f, ddir)
Aart Bik38f63012016-12-14 17:16:10 -0800525 # Maybe run bisection bug search.
526 if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
527 self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
528 # Call reporting script.
529 if self._report_script:
530 self.RunReportScript(retc1, retc2, is_output_divergence)
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700531
532 def RunReportScript(self, retc1, retc2, is_output_divergence):
533 """Runs report script."""
534 try:
535 title = "Divergence between {0} and {1} (found with fuzz testing)".format(
536 self._runner1.description, self._runner2.description)
537 # Prepare divergence comment.
538 jfuzz_cmd_and_version = subprocess.check_output(
539 ['grep', '-o', 'jfuzz.*', 'Test.java'], universal_newlines=True)
540 (jfuzz_cmd_str, jfuzz_ver) = jfuzz_cmd_and_version.split('(')
541 # Strip right parenthesis and new line.
542 jfuzz_ver = jfuzz_ver[:-2]
543 jfuzz_args = ['\'-{0}\''.format(arg)
544 for arg in jfuzz_cmd_str.strip().split(' -')][1:]
545 wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args]
Aart Bik3a9aa5b2017-07-27 09:47:35 -0700546 repro_cmd_str = (os.path.basename(__file__) +
Aart Bikad7d26f2017-12-12 15:58:21 -0800547 ' --num_tests=1 --dexer=' + self._dexer +
Aart Bikbd82d172017-12-15 11:31:57 -0800548 (' --debug_info ' if self._debug_info else ' ') +
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700549 ' '.join(wrapped_args))
550 comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format(
551 jfuzz_ver, jfuzz_cmd_str, repro_cmd_str)
552 if is_output_divergence:
553 (output, _, _) = RunCommandForOutput(
554 ['diff', self._runner1.output_file, self._runner2.output_file],
555 None, subprocess.PIPE, subprocess.STDOUT)
556 comment += 'Diff:\n' + output
557 else:
558 comment += '{0} vs {1}\n'.format(retc1, retc2)
559 # Prepare report script command.
560 script_cmd = [self._report_script, title, comment]
561 ddir = self.GetCurrentDivergenceDir()
562 bisection_out_files = glob(ddir + '/*_bisection_out.txt')
563 if bisection_out_files:
564 script_cmd += ['--bisection_out', bisection_out_files[0]]
565 subprocess.check_call(script_cmd, stdout=DEVNULL, stderr=DEVNULL)
566 except subprocess.CalledProcessError as err:
567 print('Failed to run report script.\n', err)
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700568
569 def RunBisectionSearch(self, args, expected_retcode, expected_output,
570 runner_id):
571 ddir = self.GetCurrentDivergenceDir()
572 outfile_path = ddir + '/' + runner_id + '_bisection_out.txt'
573 logfile_path = ddir + '/' + runner_id + '_bisection_log.txt'
574 errfile_path = ddir + '/' + runner_id + '_bisection_err.txt'
575 args = list(args) + ['--logfile', logfile_path, '--cleanup']
576 args += ['--expected-retcode', expected_retcode.name]
577 if expected_output:
578 args += ['--expected-output', expected_output]
579 bisection_search_path = os.path.join(
580 GetEnvVariableOrError('ANDROID_BUILD_TOP'),
581 'art/tools/bisection_search/bisection_search.py')
582 if RunCommand([bisection_search_path] + args, out=outfile_path,
583 err=errfile_path, timeout=300) == RetCode.TIMEOUT:
584 print('Bisection search TIMEOUT')
585
586 def MaybeBisectDivergence(self, retc1, retc2, is_output_divergence):
587 bisection_args1 = self._runner1.GetBisectionSearchArgs()
588 bisection_args2 = self._runner2.GetBisectionSearchArgs()
589 if is_output_divergence:
590 maybe_output1 = self._runner1.output_file
591 maybe_output2 = self._runner2.output_file
592 else:
593 maybe_output1 = maybe_output2 = None
594 if bisection_args1 is not None:
595 self.RunBisectionSearch(bisection_args1, retc2, maybe_output2,
596 self._runner1.id)
597 if bisection_args2 is not None:
598 self.RunBisectionSearch(bisection_args2, retc1, maybe_output1,
599 self._runner2.id)
Aart Bik7593b992016-08-17 16:51:12 -0700600
601 def CleanupTest(self):
602 """Cleans up after a single test run."""
Aart Bik842a4f32016-09-21 15:45:18 -0700603 for file_name in os.listdir(self._jfuzz_dir):
604 file_path = os.path.join(self._jfuzz_dir, file_name)
Aart Bike0347482016-09-20 14:34:13 -0700605 if os.path.isfile(file_path):
606 os.unlink(file_path)
607 elif os.path.isdir(file_path):
608 shutil.rmtree(file_path)
Aart Bik7593b992016-08-17 16:51:12 -0700609
610
611def main():
612 # Handle arguments.
613 parser = argparse.ArgumentParser()
Aart Bikad7d26f2017-12-12 15:58:21 -0800614 parser.add_argument('--num_tests', default=10000, type=int,
615 help='number of tests to run')
Aart Bikb16d4132016-08-19 15:45:11 -0700616 parser.add_argument('--device', help='target device serial number')
Aart Bik7593b992016-08-17 16:51:12 -0700617 parser.add_argument('--mode1', default='ri',
618 help='execution mode 1 (default: ri)')
619 parser.add_argument('--mode2', default='hopt',
620 help='execution mode 2 (default: hopt)')
Aart Bikad7d26f2017-12-12 15:58:21 -0800621 parser.add_argument('--report_script',
622 help='script called for each divergence')
Wojciech Staszkiewicz18be7b32016-09-21 15:12:54 -0700623 parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args',
Aart Bikad7d26f2017-12-12 15:58:21 -0800624 action='append',
625 help='argument for jfuzz')
Wojciech Staszkiewicz8569e522016-09-23 18:02:55 -0700626 parser.add_argument('--true_divergence', default=False, action='store_true',
Aart Bikad7d26f2017-12-12 15:58:21 -0800627 help='do not bisect timeout divergences')
628 parser.add_argument('--dexer', default='dx', type=str,
Orion Hodsonf83f84a2018-05-24 09:39:54 +0100629 help='defines dexer as dx or d8 (default: dx)')
Aart Bik06fa9f02017-12-14 11:15:59 -0800630 parser.add_argument('--debug_info', default=False, action='store_true',
631 help='include debugging info')
Aart Bik7593b992016-08-17 16:51:12 -0700632 args = parser.parse_args()
633 if args.mode1 == args.mode2:
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700634 raise FatalError('Identical execution modes given')
Aart Bik842a4f32016-09-21 15:45:18 -0700635 # Run the JFuzz tester.
Aart Bik7c598de2016-12-20 19:06:37 -0800636 with JFuzzTester(args.num_tests,
Aart Bik06fa9f02017-12-14 11:15:59 -0800637 args.device,
638 args.mode1, args.mode2,
639 args.jfuzz_args,
640 args.report_script,
641 args.true_divergence,
642 args.dexer,
643 args.debug_info) as fuzzer:
Aart Bik7593b992016-08-17 16:51:12 -0700644 fuzzer.Run()
645
Wojciech Staszkiewicz0d0fd4a2016-09-07 18:52:52 -0700646if __name__ == '__main__':
Aart Bik7593b992016-08-17 16:51:12 -0700647 main()