|
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
|
2 # Use of this source code is governed by a BSD-style license that can be |
|
3 # found in the LICENSE file. |
|
4 |
|
5 """ |
|
6 Classes in this file define additional actions that need to be taken to run a |
|
7 test under some kind of runtime error detection tool. |
|
8 |
|
9 The interface is intended to be used as follows. |
|
10 |
|
11 1. For tests that simply run a native process (i.e. no activity is spawned): |
|
12 |
|
13 Call tool.CopyFiles(). |
|
14 Prepend test command line with tool.GetTestWrapper(). |
|
15 |
|
16 2. For tests that spawn an activity: |
|
17 |
|
18 Call tool.CopyFiles(). |
|
19 Call tool.SetupEnvironment(). |
|
20 Run the test as usual. |
|
21 Call tool.CleanUpEnvironment(). |
|
22 """ |
|
23 |
|
24 import os.path |
|
25 import sys |
|
26 |
|
27 from constants import CHROME_DIR |
|
28 |
|
29 |
|
30 def SetChromeTimeoutScale(adb, scale): |
|
31 """Sets the timeout scale in /data/local/tmp/chrome_timeout_scale to scale.""" |
|
32 path = '/data/local/tmp/chrome_timeout_scale' |
|
33 if not scale or scale == 1.0: |
|
34 # Delete if scale is None/0.0/1.0 since the default timeout scale is 1.0 |
|
35 adb.RunShellCommand('rm %s' % path) |
|
36 else: |
|
37 adb.SetFileContents(path, '%f' % scale) |
|
38 |
|
39 |
|
40 class BaseTool(object): |
|
41 """A tool that does nothing.""" |
|
42 |
|
43 def GetTestWrapper(self): |
|
44 """Returns a string that is to be prepended to the test command line.""" |
|
45 return '' |
|
46 |
|
47 def GetUtilWrapper(self): |
|
48 """Returns the wrapper name for the utilities. |
|
49 |
|
50 Returns: |
|
51 A string that is to be prepended to the command line of utility |
|
52 processes (forwarder, etc.). |
|
53 """ |
|
54 return '' |
|
55 |
|
56 def CopyFiles(self): |
|
57 """Copies tool-specific files to the device, create directories, etc.""" |
|
58 pass |
|
59 |
|
60 def SetupEnvironment(self): |
|
61 """Sets up the system environment for a test. |
|
62 |
|
63 This is a good place to set system properties. |
|
64 """ |
|
65 pass |
|
66 |
|
67 def CleanUpEnvironment(self): |
|
68 """Cleans up environment.""" |
|
69 pass |
|
70 |
|
71 def GetTimeoutScale(self): |
|
72 """Returns a multiplier that should be applied to timeout values.""" |
|
73 return 1.0 |
|
74 |
|
75 def NeedsDebugInfo(self): |
|
76 """Whether this tool requires debug info. |
|
77 |
|
78 Returns: |
|
79 True if this tool can not work with stripped binaries. |
|
80 """ |
|
81 return False |
|
82 |
|
83 |
|
84 class AddressSanitizerTool(BaseTool): |
|
85 """AddressSanitizer tool.""" |
|
86 |
|
87 WRAPPER_PATH = '/system/bin/asanwrapper' |
|
88 |
|
89 def __init__(self, adb): |
|
90 self._adb = adb |
|
91 self._wrap_properties = ['wrap.com.google.android.apps.ch', |
|
92 'wrap.org.chromium.native_test'] |
|
93 |
|
94 def CopyFiles(self): |
|
95 """Copies ASan tools to the device.""" |
|
96 files = ['system/lib/libasan_preload.so', |
|
97 'system/bin/asanwrapper', |
|
98 'system/bin/asan/app_process', |
|
99 'system/bin/linker'] |
|
100 android_product_out = os.environ['ANDROID_PRODUCT_OUT'] |
|
101 self._adb.MakeSystemFolderWritable() |
|
102 for f in files: |
|
103 self._adb.PushIfNeeded(os.path.join(android_product_out, f), |
|
104 os.path.join('/', f)) |
|
105 |
|
106 def GetTestWrapper(self): |
|
107 return AddressSanitizerTool.WRAPPER_PATH |
|
108 |
|
109 def GetUtilWrapper(self): |
|
110 """Returns the wrapper for utilities, such as forwarder. |
|
111 |
|
112 AddressSanitizer wrapper must be added to all instrumented binaries, |
|
113 including forwarder and the like. This can be removed if such binaries |
|
114 were built without instrumentation. """ |
|
115 return AddressSanitizerTool.WRAPPER_PATH |
|
116 |
|
117 def SetupEnvironment(self): |
|
118 for prop in self._wrap_properties: |
|
119 self._adb.RunShellCommand('setprop %s "logwrapper %s"' % ( |
|
120 prop, self.GetTestWrapper())) |
|
121 SetChromeTimeoutScale(self._adb, self.GetTimeoutScale()) |
|
122 |
|
123 def CleanUpEnvironment(self): |
|
124 for prop in self._wrap_properties: |
|
125 self._adb.RunShellCommand('setprop %s ""' % (prop,)) |
|
126 SetChromeTimeoutScale(self._adb, None) |
|
127 |
|
128 def GetTimeoutScale(self): |
|
129 # Very slow startup. |
|
130 return 20.0 |
|
131 |
|
132 |
|
133 class ValgrindTool(BaseTool): |
|
134 """Base abstract class for Valgrind tools.""" |
|
135 |
|
136 VG_DIR = '/data/local/tmp/valgrind' |
|
137 VGLOGS_DIR = '/data/local/tmp/vglogs' |
|
138 |
|
139 def __init__(self, adb): |
|
140 self._adb = adb |
|
141 # exactly 31 chars, SystemProperties::PROP_NAME_MAX |
|
142 self._wrap_properties = ['wrap.com.google.android.apps.ch', |
|
143 'wrap.org.chromium.native_test'] |
|
144 |
|
145 def CopyFiles(self): |
|
146 """Copies Valgrind tools to the device.""" |
|
147 self._adb.RunShellCommand('rm -r %s; mkdir %s' % |
|
148 (ValgrindTool.VG_DIR, ValgrindTool.VG_DIR)) |
|
149 self._adb.RunShellCommand('rm -r %s; mkdir %s' % |
|
150 (ValgrindTool.VGLOGS_DIR, |
|
151 ValgrindTool.VGLOGS_DIR)) |
|
152 files = self.GetFilesForTool() |
|
153 for f in files: |
|
154 self._adb.PushIfNeeded(os.path.join(CHROME_DIR, f), |
|
155 os.path.join(ValgrindTool.VG_DIR, |
|
156 os.path.basename(f))) |
|
157 |
|
158 def SetupEnvironment(self): |
|
159 """Sets up device environment.""" |
|
160 self._adb.RunShellCommand('chmod 777 /data/local/tmp') |
|
161 for prop in self._wrap_properties: |
|
162 self._adb.RunShellCommand('setprop %s "logwrapper %s"' % ( |
|
163 prop, self.GetTestWrapper())) |
|
164 SetChromeTimeoutScale(self._adb, self.GetTimeoutScale()) |
|
165 |
|
166 def CleanUpEnvironment(self): |
|
167 """Cleans up device environment.""" |
|
168 for prop in self._wrap_properties: |
|
169 self._adb.RunShellCommand('setprop %s ""' % (prop,)) |
|
170 SetChromeTimeoutScale(self._adb, None) |
|
171 |
|
172 def GetFilesForTool(self): |
|
173 """Returns a list of file names for the tool.""" |
|
174 raise NotImplementedError() |
|
175 |
|
176 def NeedsDebugInfo(self): |
|
177 """Whether this tool requires debug info. |
|
178 |
|
179 Returns: |
|
180 True if this tool can not work with stripped binaries. |
|
181 """ |
|
182 return True |
|
183 |
|
184 |
|
185 class MemcheckTool(ValgrindTool): |
|
186 """Memcheck tool.""" |
|
187 |
|
188 def __init__(self, adb): |
|
189 super(MemcheckTool, self).__init__(adb) |
|
190 |
|
191 def GetFilesForTool(self): |
|
192 """Returns a list of file names for the tool.""" |
|
193 return ['tools/valgrind/android/vg-chrome-wrapper.sh', |
|
194 'tools/valgrind/memcheck/suppressions.txt', |
|
195 'tools/valgrind/memcheck/suppressions_android.txt'] |
|
196 |
|
197 def GetTestWrapper(self): |
|
198 """Returns a string that is to be prepended to the test command line.""" |
|
199 return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper.sh' |
|
200 |
|
201 def GetTimeoutScale(self): |
|
202 """Returns a multiplier that should be applied to timeout values.""" |
|
203 return 30 |
|
204 |
|
205 |
|
206 class TSanTool(ValgrindTool): |
|
207 """ThreadSanitizer tool. See http://code.google.com/p/data-race-test .""" |
|
208 |
|
209 def __init__(self, adb): |
|
210 super(TSanTool, self).__init__(adb) |
|
211 |
|
212 def GetFilesForTool(self): |
|
213 """Returns a list of file names for the tool.""" |
|
214 return ['tools/valgrind/android/vg-chrome-wrapper-tsan.sh', |
|
215 'tools/valgrind/tsan/suppressions.txt', |
|
216 'tools/valgrind/tsan/suppressions_android.txt', |
|
217 'tools/valgrind/tsan/ignores.txt'] |
|
218 |
|
219 def GetTestWrapper(self): |
|
220 """Returns a string that is to be prepended to the test command line.""" |
|
221 return ValgrindTool.VG_DIR + '/' + 'vg-chrome-wrapper-tsan.sh' |
|
222 |
|
223 def GetTimeoutScale(self): |
|
224 """Returns a multiplier that should be applied to timeout values.""" |
|
225 return 30.0 |
|
226 |
|
227 |
|
228 TOOL_REGISTRY = { |
|
229 'memcheck': lambda x: MemcheckTool(x), |
|
230 'memcheck-renderer': lambda x: MemcheckTool(x), |
|
231 'tsan': lambda x: TSanTool(x), |
|
232 'tsan-renderer': lambda x: TSanTool(x), |
|
233 'asan': lambda x: AddressSanitizerTool(x), |
|
234 } |
|
235 |
|
236 |
|
237 def CreateTool(tool_name, adb): |
|
238 """Creates a tool with the specified tool name. |
|
239 |
|
240 Args: |
|
241 tool_name: Name of the tool to create. |
|
242 adb: ADB interface the tool will use. |
|
243 Returns: |
|
244 A tool for the specified tool_name. |
|
245 """ |
|
246 if not tool_name: |
|
247 return BaseTool() |
|
248 |
|
249 ctor = TOOL_REGISTRY.get(tool_name) |
|
250 if ctor: |
|
251 return ctor(adb) |
|
252 else: |
|
253 print 'Unknown tool %s, available tools: %s' % ( |
|
254 tool_name, ', '.join(sorted(TOOL_REGISTRY.keys()))) |
|
255 sys.exit(1) |