Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(611)

Side by Side Diff: tools/generate-runtime-tests.py

Issue 283403002: Add builtin detector to generate-runtime-tests.py (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: rebase Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/v8natives.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 the V8 project authors. All rights reserved. 2 # Copyright 2014 the V8 project authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import itertools 6 import itertools
7 import js2c
7 import multiprocessing 8 import multiprocessing
8 import optparse 9 import optparse
9 import os 10 import os
10 import random 11 import random
11 import re 12 import re
12 import shutil 13 import shutil
13 import signal 14 import signal
14 import string 15 import string
15 import subprocess 16 import subprocess
16 import sys 17 import sys
(...skipping 26 matching lines...) Expand all
43 # TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION 44 # TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION
44 45
45 # Counts of functions in each detection state. These are used to assert 46 # Counts of functions in each detection state. These are used to assert
46 # that the parser doesn't bit-rot. Change the values as needed when you add, 47 # that the parser doesn't bit-rot. Change the values as needed when you add,
47 # remove or change runtime functions, but make sure we don't lose our ability 48 # remove or change runtime functions, but make sure we don't lose our ability
48 # to parse them! 49 # to parse them!
49 EXPECTED_FUNCTION_COUNT = 362 50 EXPECTED_FUNCTION_COUNT = 362
50 EXPECTED_FUZZABLE_COUNT = 329 51 EXPECTED_FUZZABLE_COUNT = 329
51 EXPECTED_CCTEST_COUNT = 6 52 EXPECTED_CCTEST_COUNT = 6
52 EXPECTED_UNKNOWN_COUNT = 5 53 EXPECTED_UNKNOWN_COUNT = 5
54 EXPECTED_BUILTINS_COUNT = 827
53 55
54 56
55 # Don't call these at all. 57 # Don't call these at all.
56 BLACKLISTED = [ 58 BLACKLISTED = [
57 "Abort", # Kills the process. 59 "Abort", # Kills the process.
58 "AbortJS", # Kills the process. 60 "AbortJS", # Kills the process.
59 "CompileForOnStackReplacement", # Riddled with ASSERTs. 61 "CompileForOnStackReplacement", # Riddled with ASSERTs.
60 "IS_VAR", # Not implemented in the runtime. 62 "IS_VAR", # Not implemented in the runtime.
61 "ListNatives", # Not available in Release mode. 63 "ListNatives", # Not available in Release mode.
62 "SetAllocationTimeout", # Too slow for fuzzing. 64 "SetAllocationTimeout", # Too slow for fuzzing.
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 # 'ffoo12345678901234567890'.substr(1) 293 # 'ffoo12345678901234567890'.substr(1)
292 return self._Variable(name, "\"%s\".substr(1)" % s) 294 return self._Variable(name, "\"%s\".substr(1)" % s)
293 295
294 def _ConsString(self, name): 296 def _ConsString(self, name):
295 s1 = self._RawRandomString(8, 15) 297 s1 = self._RawRandomString(8, 15)
296 s2 = self._RawRandomString(8, 15) 298 s2 = self._RawRandomString(8, 15)
297 # 'foo12345' + (function() { return 'bar12345';})() 299 # 'foo12345' + (function() { return 'bar12345';})()
298 return self._Variable(name, 300 return self._Variable(name,
299 "\"%s\" + (function() { return \"%s\";})()" % (s1, s2)) 301 "\"%s\" + (function() { return \"%s\";})()" % (s1, s2))
300 302
301 def _ExternalString(self, name):
302 # Needs --expose-externalize-string.
303 return None
304
305 def _InternalizedString(self, name): 303 def _InternalizedString(self, name):
306 return self._Variable(name, "\"%s\"" % self._RawRandomString(0, 20)) 304 return self._Variable(name, "\"%s\"" % self._RawRandomString(0, 20))
307 305
308 def _String(self, name, recursion_budget): 306 def _String(self, name, recursion_budget):
309 die = random.random() 307 die = random.random()
310 if die < 0.5: 308 if die < 0.5:
311 string = random.choice(self.USUAL_SUSPECT_PROPERTIES) 309 string = random.choice(self.USUAL_SUSPECT_PROPERTIES)
312 return self._Variable(name, "\"%s\"" % string) 310 return self._Variable(name, "\"%s\"" % string)
313 elif die < 0.6: 311 elif die < 0.6:
314 number_name = name + "_number" 312 number_name = name + "_number"
(...skipping 596 matching lines...) Expand 10 before | Expand all | Expand 10 after
911 909
912 if function.TryParseArg(line): 910 if function.TryParseArg(line):
913 continue 911 continue
914 912
915 if line == FUNCTIONEND: 913 if line == FUNCTIONEND:
916 if function is not None: 914 if function is not None:
917 functions.append(function) 915 functions.append(function)
918 function = None 916 function = None
919 return functions 917 return functions
920 918
919
920 # Hack: This must have the same fields as class Function above, because the
921 # two are used polymorphically in RunFuzzer(). We could use inheritance...
922 class Builtin(object):
923 def __init__(self, match):
924 self.name = match.group(1)
925 args = match.group(2)
926 self.argslength = 0 if args == "" else args.count(",") + 1
927 self.inline = ""
928 self.args = {}
929 if self.argslength > 0:
930 args = args.split(",")
931 for i in range(len(args)):
932 # a = args[i].strip() # TODO: filter out /* comments */ first.
933 a = ""
934 self.args[i] = Arg("Object", a, i)
935
936 def __str__(self):
937 return "%s(%d)" % (self.name, self.argslength)
938
939
940 def FindJSBuiltins():
941 PATH = "src"
942 fileslist = []
943 for (root, dirs, files) in os.walk(PATH):
944 for f in files:
945 if f.endswith(".js"):
946 fileslist.append(os.path.join(root, f))
947 builtins = []
948 regexp = re.compile("^function (\w+)\s*\((.*?)\) {")
949 matches = 0
950 for filename in fileslist:
951 with open(filename, "r") as f:
952 file_contents = f.read()
953 file_contents = js2c.ExpandInlineMacros(file_contents)
954 lines = file_contents.split("\n")
955 partial_line = ""
956 for line in lines:
957 if line.startswith("function") and not '{' in line:
958 partial_line += line.rstrip()
959 continue
960 if partial_line:
961 partial_line += " " + line.strip()
962 if '{' in line:
963 line = partial_line
964 partial_line = ""
965 else:
966 continue
967 match = regexp.match(line)
968 if match:
969 builtins.append(Builtin(match))
970 return builtins
971
972
921 # Classifies runtime functions. 973 # Classifies runtime functions.
922 def ClassifyFunctions(functions): 974 def ClassifyFunctions(functions):
923 # Can be fuzzed with a JavaScript testcase. 975 # Can be fuzzed with a JavaScript testcase.
924 js_fuzzable_functions = [] 976 js_fuzzable_functions = []
925 # We have enough information to fuzz these, but they need inputs that 977 # We have enough information to fuzz these, but they need inputs that
926 # cannot be created or passed around in JavaScript. 978 # cannot be created or passed around in JavaScript.
927 cctest_fuzzable_functions = [] 979 cctest_fuzzable_functions = []
928 # This script does not have enough information about these. 980 # This script does not have enough information about these.
929 unknown_functions = [] 981 unknown_functions = []
930 982
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
1011 shutil.rmtree(BASEPATH) # Re-generate everything. 1063 shutil.rmtree(BASEPATH) # Re-generate everything.
1012 os.makedirs(BASEPATH) 1064 os.makedirs(BASEPATH)
1013 for f in functions: 1065 for f in functions:
1014 GenerateJSTestcaseForFunction(f) 1066 GenerateJSTestcaseForFunction(f)
1015 1067
1016 1068
1017 def _SaveFileName(save_path, process_id, save_file_index): 1069 def _SaveFileName(save_path, process_id, save_file_index):
1018 return "%s/fuzz_%d_%d.js" % (save_path, process_id, save_file_index) 1070 return "%s/fuzz_%d_%d.js" % (save_path, process_id, save_file_index)
1019 1071
1020 1072
1073 def _GetFuzzableRuntimeFunctions():
1074 functions = FindRuntimeFunctions()
1075 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \
1076 ClassifyFunctions(functions)
1077 return js_fuzzable_functions
1078
1079
1080 FUZZ_TARGET_LISTS = {
1081 "runtime": _GetFuzzableRuntimeFunctions,
1082 "builtins": FindJSBuiltins,
1083 }
1084
1085
1021 def RunFuzzer(process_id, options, stop_running): 1086 def RunFuzzer(process_id, options, stop_running):
1087 MAX_SLEEP_TIME = 0.1
1088 INITIAL_SLEEP_TIME = 0.001
1089 SLEEP_TIME_FACTOR = 1.25
1022 base_file_name = "/dev/shm/runtime_fuzz_%d" % process_id 1090 base_file_name = "/dev/shm/runtime_fuzz_%d" % process_id
1023 test_file_name = "%s.js" % base_file_name 1091 test_file_name = "%s.js" % base_file_name
1024 stderr_file_name = "%s.out" % base_file_name 1092 stderr_file_name = "%s.out" % base_file_name
1025 save_file_index = 0 1093 save_file_index = 0
1026 while os.path.exists(_SaveFileName(options.save_path, process_id, 1094 while os.path.exists(_SaveFileName(options.save_path, process_id,
1027 save_file_index)): 1095 save_file_index)):
1028 save_file_index += 1 1096 save_file_index += 1
1029 MAX_SLEEP_TIME = 0.1
1030 INITIAL_SLEEP_TIME = 0.001
1031 SLEEP_TIME_FACTOR = 1.5
1032 1097
1033 functions = FindRuntimeFunctions() 1098 targets = FUZZ_TARGET_LISTS[options.fuzz_target]()
1034 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \
1035 ClassifyFunctions(functions)
1036
1037 try: 1099 try:
1038 for i in range(options.num_tests): 1100 for i in range(options.num_tests):
1039 if stop_running.is_set(): break 1101 if stop_running.is_set(): break
1040 function = random.choice(js_fuzzable_functions) # TODO: others too 1102 function = None
1041 if function.argslength == 0: continue 1103 while function is None or function.argslength == 0:
1104 function = random.choice(targets)
1042 args = [] 1105 args = []
1043 definitions = [] 1106 definitions = []
1044 gen = Generator() 1107 gen = Generator()
1045 for i in range(function.argslength): 1108 for i in range(function.argslength):
1046 arg = function.args[i] 1109 arg = function.args[i]
1047 argname = "arg%d%s" % (i, arg.name) 1110 argname = "arg%d%s" % (i, arg.name)
1048 args.append(argname) 1111 args.append(argname)
1049 definitions += gen.RandomVariable(argname, arg.type, simple=False) 1112 definitions += gen.RandomVariable(argname, arg.type, simple=False)
1050 testcase = _GenerateTestcase(function, definitions, args, True) 1113 testcase = _GenerateTestcase(function, definitions, args, True)
1051 with open(test_file_name, "w") as f: 1114 with open(test_file_name, "w") as f:
(...skipping 27 matching lines...) Expand all
1079 if line.strip() == "# Allocation failed - process out of memory": 1142 if line.strip() == "# Allocation failed - process out of memory":
1080 oom = True 1143 oom = True
1081 break 1144 break
1082 if oom: continue 1145 if oom: continue
1083 save_name = _SaveFileName(options.save_path, process_id, 1146 save_name = _SaveFileName(options.save_path, process_id,
1084 save_file_index) 1147 save_file_index)
1085 shutil.copyfile(test_file_name, save_name) 1148 shutil.copyfile(test_file_name, save_name)
1086 save_file_index += 1 1149 save_file_index += 1
1087 except KeyboardInterrupt: 1150 except KeyboardInterrupt:
1088 stop_running.set() 1151 stop_running.set()
1089 except Exception, e:
1090 print e
1091 finally: 1152 finally:
1092 os.remove(test_file_name) 1153 if os.path.exists(test_file_name):
1093 os.remove(stderr_file_name) 1154 os.remove(test_file_name)
1155 if os.path.exists(stderr_file_name):
1156 os.remove(stderr_file_name)
1094 1157
1095 1158
1096 def BuildOptionParser(): 1159 def BuildOptionParser():
1097 usage = """Usage: %%prog [options] ACTION 1160 usage = """Usage: %%prog [options] ACTION
1098 1161
1099 where ACTION can be: 1162 where ACTION can be:
1100 1163
1101 info Print diagnostic info. 1164 info Print diagnostic info.
1102 check Check that runtime functions can be parsed as expected, and that 1165 check Check that runtime functions can be parsed as expected, and that
1103 test cases exist. 1166 test cases exist.
1104 generate Parse source code for runtime functions, and auto-generate 1167 generate Parse source code for runtime functions, and auto-generate
1105 test cases for them. Warning: this will nuke and re-create 1168 test cases for them. Warning: this will nuke and re-create
1106 %(path)s. 1169 %(path)s.
1107 fuzz Generate fuzz tests, run them, save those that crashed (see options). 1170 fuzz Generate fuzz tests, run them, save those that crashed (see options).
1108 """ % {"path": os.path.relpath(BASEPATH)} 1171 """ % {"path": os.path.relpath(BASEPATH)}
1109 1172
1110 o = optparse.OptionParser(usage=usage) 1173 o = optparse.OptionParser(usage=usage)
1111 o.add_option("--binary", default="out/x64.debug/d8", 1174 o.add_option("--binary", default="out/x64.debug/d8",
1112 help="d8 binary used for running fuzz tests (default: %default)") 1175 help="d8 binary used for running fuzz tests (default: %default)")
1176 o.add_option("--fuzz-target", default="runtime",
1177 help="Set of functions targeted by fuzzing. Allowed values: "
1178 "%s (default: %%default)" % ", ".join(FUZZ_TARGET_LISTS))
1113 o.add_option("-n", "--num-tests", default=1000, type="int", 1179 o.add_option("-n", "--num-tests", default=1000, type="int",
1114 help="Number of fuzz tests to generate per worker process" 1180 help="Number of fuzz tests to generate per worker process"
1115 " (default: %default)") 1181 " (default: %default)")
1116 o.add_option("--save-path", default="~/runtime_fuzz_output", 1182 o.add_option("--save-path", default="~/runtime_fuzz_output",
1117 help="Path to directory where failing tests will be stored" 1183 help="Path to directory where failing tests will be stored"
1118 " (default: %default)") 1184 " (default: %default)")
1119 o.add_option("--timeout", default=20, type="int", 1185 o.add_option("--timeout", default=20, type="int",
1120 help="Timeout for each fuzz test (in seconds, default:" 1186 help="Timeout for each fuzz test (in seconds, default:"
1121 "%default)") 1187 "%default)")
1122 return o 1188 return o
1123 1189
1124 1190
1191 def ProcessOptions(options, args):
1192 options.save_path = os.path.expanduser(options.save_path)
1193 if options.fuzz_target not in FUZZ_TARGET_LISTS:
1194 print("Invalid fuzz target: %s" % options.fuzz_target)
1195 return False
1196 if len(args) != 1 or args[0] == "help":
1197 return False
1198 return True
1199
1200
1125 def Main(): 1201 def Main():
1126 parser = BuildOptionParser() 1202 parser = BuildOptionParser()
1127 (options, args) = parser.parse_args() 1203 (options, args) = parser.parse_args()
1128 options.save_path = os.path.expanduser(options.save_path)
1129 1204
1130 if len(args) != 1 or args[0] == "help": 1205 if not ProcessOptions(options, args):
1131 parser.print_help() 1206 parser.print_help()
1132 return 1 1207 return 1
1133 action = args[0] 1208 action = args[0]
1134 1209
1135 functions = FindRuntimeFunctions() 1210 functions = FindRuntimeFunctions()
1136 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ 1211 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \
1137 ClassifyFunctions(functions) 1212 ClassifyFunctions(functions)
1213 builtins = FindJSBuiltins()
1138 1214
1139 if action == "test": 1215 if action == "test":
1140 gen = Generator() 1216 print("put your temporary debugging code here")
1141 vartype = "JSTypedArray"
1142 print("simple: %s" % gen.RandomVariable("x", vartype, True))
1143 for i in range(10):
1144 print("----")
1145 print("%s" % "\n".join(gen.RandomVariable("x", vartype, False)))
1146 return 0 1217 return 0
1147 1218
1148 if action == "info": 1219 if action == "info":
1149 print("%d functions total; js_fuzzable_functions: %d, " 1220 print("%d functions total; js_fuzzable_functions: %d, "
1150 "cctest_fuzzable_functions: %d, unknown_functions: %d" 1221 "cctest_fuzzable_functions: %d, unknown_functions: %d"
1151 % (len(functions), len(js_fuzzable_functions), 1222 % (len(functions), len(js_fuzzable_functions),
1152 len(cctest_fuzzable_functions), len(unknown_functions))) 1223 len(cctest_fuzzable_functions), len(unknown_functions)))
1224 print("%d JavaScript builtins" % len(builtins))
1153 print("unknown functions:") 1225 print("unknown functions:")
1154 for f in unknown_functions: 1226 for f in unknown_functions:
1155 print(f) 1227 print(f)
1156 return 0 1228 return 0
1157 1229
1158 if action == "check": 1230 if action == "check":
1159 errors = 0 1231 errors = 0
1160 1232
1161 def CheckCount(actual, expected, description): 1233 def CheckCount(actual, expected, description):
1162 if len(actual) != expected: 1234 if len(actual) != expected:
1163 print("Expected to detect %d %s, but found %d." % ( 1235 print("Expected to detect %d %s, but found %d." % (
1164 expected, description, len(actual))) 1236 expected, description, len(actual)))
1165 print("If this change is intentional, please update the expectations" 1237 print("If this change is intentional, please update the expectations"
1166 " at the top of %s." % THIS_SCRIPT) 1238 " at the top of %s." % THIS_SCRIPT)
1167 return 1 1239 return 1
1168 return 0 1240 return 0
1169 1241
1170 errors += CheckCount(functions, EXPECTED_FUNCTION_COUNT, 1242 errors += CheckCount(functions, EXPECTED_FUNCTION_COUNT,
1171 "functions in total") 1243 "functions in total")
1172 errors += CheckCount(js_fuzzable_functions, EXPECTED_FUZZABLE_COUNT, 1244 errors += CheckCount(js_fuzzable_functions, EXPECTED_FUZZABLE_COUNT,
1173 "JavaScript-fuzzable functions") 1245 "JavaScript-fuzzable functions")
1174 errors += CheckCount(cctest_fuzzable_functions, EXPECTED_CCTEST_COUNT, 1246 errors += CheckCount(cctest_fuzzable_functions, EXPECTED_CCTEST_COUNT,
1175 "cctest-fuzzable functions") 1247 "cctest-fuzzable functions")
1176 errors += CheckCount(unknown_functions, EXPECTED_UNKNOWN_COUNT, 1248 errors += CheckCount(unknown_functions, EXPECTED_UNKNOWN_COUNT,
1177 "functions with incomplete type information") 1249 "functions with incomplete type information")
1250 errors += CheckCount(builtins, EXPECTED_BUILTINS_COUNT,
1251 "JavaScript builtins")
1178 1252
1179 def CheckTestcasesExisting(functions): 1253 def CheckTestcasesExisting(functions):
1180 errors = 0 1254 errors = 0
1181 for f in functions: 1255 for f in functions:
1182 if not os.path.isfile(os.path.join(BASEPATH, f.Filename())): 1256 if not os.path.isfile(os.path.join(BASEPATH, f.Filename())):
1183 print("Missing testcase for %s, please run '%s generate'" % 1257 print("Missing testcase for %s, please run '%s generate'" %
1184 (f.name, THIS_SCRIPT)) 1258 (f.name, THIS_SCRIPT))
1185 errors += 1 1259 errors += 1
1186 files = filter(lambda filename: not filename.startswith("."), 1260 files = filter(lambda filename: not filename.startswith("."),
1187 os.listdir(BASEPATH)) 1261 os.listdir(BASEPATH))
1188 if (len(files) != len(functions)): 1262 if (len(files) != len(functions)):
1189 unexpected_files = set(files) - set([f.Filename() for f in functions]) 1263 unexpected_files = set(files) - set([f.Filename() for f in functions])
1190 for f in unexpected_files: 1264 for f in unexpected_files:
1191 print("Unexpected testcase: %s" % os.path.join(BASEPATH, f)) 1265 print("Unexpected testcase: %s" % os.path.join(BASEPATH, f))
1192 errors += 1 1266 errors += 1
1193 print("Run '%s generate' to automatically clean these up." 1267 print("Run '%s generate' to automatically clean these up."
1194 % THIS_SCRIPT) 1268 % THIS_SCRIPT)
1195 return errors 1269 return errors
1196 1270
1197 errors += CheckTestcasesExisting(js_fuzzable_functions) 1271 errors += CheckTestcasesExisting(js_fuzzable_functions)
1198 1272
1273 def CheckNameClashes(runtime_functions, builtins):
1274 errors = 0
1275 runtime_map = {}
1276 for f in runtime_functions:
1277 runtime_map[f.name] = 1
1278 for b in builtins:
1279 if b.name in runtime_map:
1280 print("Builtin/Runtime_Function name clash: %s" % b.name)
1281 errors += 1
1282 return errors
1283
1284 errors += CheckNameClashes(functions, builtins)
1285
1199 if errors > 0: 1286 if errors > 0:
1200 return 1 1287 return 1
1201 print("Generated runtime tests: all good.") 1288 print("Generated runtime tests: all good.")
1202 return 0 1289 return 0
1203 1290
1204 if action == "generate": 1291 if action == "generate":
1205 GenerateTestcases(js_fuzzable_functions) 1292 GenerateTestcases(js_fuzzable_functions)
1206 return 0 1293 return 0
1207 1294
1208 if action == "fuzz": 1295 if action == "fuzz":
(...skipping 10 matching lines...) Expand all
1219 for i in range(len(processes)): 1306 for i in range(len(processes)):
1220 processes[i].join() 1307 processes[i].join()
1221 except KeyboardInterrupt: 1308 except KeyboardInterrupt:
1222 stop_running.set() 1309 stop_running.set()
1223 for i in range(len(processes)): 1310 for i in range(len(processes)):
1224 processes[i].join() 1311 processes[i].join()
1225 return 0 1312 return 0
1226 1313
1227 if __name__ == "__main__": 1314 if __name__ == "__main__":
1228 sys.exit(Main()) 1315 sys.exit(Main())
OLDNEW
« no previous file with comments | « src/v8natives.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698