1 #!/usr/bin/python3 2 # Copyright (C) 2017 The Android Open Source Project 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 # 17 # Generates the src/art/Test988Intrinsics.java file. 18 # Re-run this every time art/compiler/intrinics_list.h is modified. 19 # 20 # $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java 21 # 22 23 import argparse 24 import os 25 import re 26 import collections 27 import sys 28 29 from string import Template 30 31 # Relative path to art/runtime/intrinsics_list.h 32 INTRINSICS_LIST_H = os.path.dirname(os.path.realpath(__file__)) + "/../../runtime/intrinsics_list.h" 33 34 # Macro parameter index to V(). Negative means from the end. 35 IDX_STATIC_OR_VIRTUAL = 1 36 IDX_SIGNATURE = -1 37 IDX_METHOD_NAME = -2 38 IDX_CLASS_NAME = -3 39 40 # Exclude all hidden API. 41 KLASS_BLACK_LIST = ['sun.misc.Unsafe', 'libcore.io.Memory', 'java.lang.StringFactory', 42 'java.lang.invoke.MethodHandle', # invokes are tested by 956-method-handles 43 'java.lang.invoke.VarHandle' ] # TODO(b/65872996): will tested separately 44 METHOD_BLACK_LIST = [('java.lang.ref.Reference', 'getReferent'), 45 ('java.lang.String', 'getCharsNoCheck'), 46 ('java.lang.System', 'arraycopy')] # arraycopy has a manual test. 47 48 # When testing a virtual function, it needs to operate on an instance. 49 # These instances will be created with the following values, 50 # otherwise a default 'new T()' is used. 51 KLASS_INSTANCE_INITIALIZERS = { 52 'java.lang.String' : '"some large string"', 53 'java.lang.StringBuffer' : 'new java.lang.StringBuffer("some large string buffer")', 54 'java.lang.StringBuilder' : 'new java.lang.StringBuilder("some large string builder")', 55 'java.lang.ref.Reference' : 'new java.lang.ref.WeakReference(new Object())' 56 }; 57 58 OUTPUT_TPL = Template(""" 59 /* 60 * Copyright (C) 2017 The Android Open Source Project 61 * 62 * Licensed under the Apache License, Version 2.0 (the "License"); 63 * you may not use this file except in compliance with the License. 64 * You may obtain a copy of the License at 65 * 66 * http://www.apache.org/licenses/LICENSE-2.0 67 * 68 * Unless required by applicable law or agreed to in writing, software 69 * distributed under the License is distributed on an "AS IS" BASIS, 70 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 71 * See the License for the specific language governing permissions and 72 * limitations under the License. 73 */ 74 75 // AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY. 76 // 77 // $$> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java 78 // 79 // RUN ABOVE COMMAND TO REGENERATE THIS FILE. 80 81 package art; 82 83 class Test988Intrinsics { 84 // Pre-initialize *all* instance variables used so that their constructors are not in the trace. 85 $static_fields 86 87 static void initialize() { 88 // Ensure all static variables are initialized. 89 // In addition, pre-load classes here so that we don't see diverging class loading traces. 90 $initialize_classes 91 } 92 93 static void test() { 94 // Call each intrinsic from art/runtime/intrinsics_list.h to make sure they are traced. 95 $test_body 96 } 97 } 98 """) 99 100 JNI_TYPES = { 101 'Z' : 'boolean', 102 'B' : 'byte', 103 'C' : 'char', 104 'S' : 'short', 105 'I' : 'int', 106 'J' : 'long', 107 'F' : 'float', 108 'D' : 'double', 109 'L' : 'object' 110 }; 111 112 debug_printing_enabled = False 113 114 def debug_print(x): 115 if debug_printing_enabled: 116 print(x, file=sys.stderr) 117 118 # Parse JNI sig into a list, e.g. "II" -> ['I', 'I'], '[[IJ' -> ['[[I', 'J'], etc. 119 def sig_to_parameter_type_list(sig): 120 sig = re.sub(r'[(](.*)[)].*', r'\1', sig) 121 122 lst = [] 123 obj = "" 124 is_obj = False 125 is_array = False 126 for x in sig: 127 if is_obj: 128 obj = obj + x 129 if x == ";": 130 is_obj = False 131 lst.append(obj) 132 obj = "" 133 elif is_array: 134 obj = obj + x 135 if x != "[": 136 is_array = False 137 lst.append(obj) 138 obj = "" 139 else: 140 if x == "[": 141 obj = "[" 142 is_array = True 143 elif x == "L": 144 obj = "L" 145 is_obj = True 146 else: 147 lst.append(x) 148 149 return lst 150 151 # Convert a single JNI descriptor into a pretty java name, e.g. "[I" -> "int[]", etc. 152 def javafy_name(kls_name): 153 if kls_name.startswith("L"): 154 kls_name = kls_name.lstrip("L").rstrip(";") 155 return kls_name.replace("/", ".") 156 elif kls_name.startswith("["): 157 array_count = kls_name.count("[") 158 non_array = javafy_name(kls_name.lstrip("[")) 159 return non_array + ("[]" * array_count) 160 161 return JNI_TYPES.get(kls_name, kls_name) 162 163 def extract_staticness(static_or_virtual): 164 if static_or_virtual == "kStatic": 165 return 'static' 166 return 'virtual' # kVirtual, kDirect 167 168 class MethodInfo: 169 def __init__(self, staticness, pretty_params, method, kls): 170 # 'virtual' or 'static' 171 self.staticness = staticness 172 # list of e.g. ['int', 'double', 'java.lang.String'] etc 173 self.parameters = pretty_params 174 # e.g. 'toString' 175 self.method_name = method 176 # e.g. 'java.lang.String' 177 self.klass = kls 178 179 def __str__(self): 180 return "MethodInfo " + str(self.__dict__) 181 182 def dummy_parameters(self): 183 dummy_values = { 184 'boolean' : 'false', 185 'byte' : '(byte)0', 186 'char' : "'x'", 187 'short' : '(short)0', 188 'int' : '0', 189 'long' : '0L', 190 'float' : '0.0f', 191 'double' : '0.0' 192 } 193 194 def object_dummy(name): 195 if name == "java.lang.String": 196 return '"hello"' 197 else: 198 return "(%s)null" %(name) 199 return [ dummy_values.get(param, object_dummy(param)) for param in self.parameters ] 200 201 def dummy_instance_value(self): 202 return KLASS_INSTANCE_INITIALIZERS.get(self.klass, 'new %s()' %(self.klass)) 203 204 def is_blacklisted(self): 205 for blk in KLASS_BLACK_LIST: 206 if self.klass.startswith(blk): 207 return True 208 209 return (self.klass, self.method_name) in METHOD_BLACK_LIST 210 211 # parse the V(...) \ list of items into a MethodInfo 212 def parse_method_info(items): 213 def get_item(idx): 214 return items[idx].strip().strip("\"") 215 216 staticness = get_item(IDX_STATIC_OR_VIRTUAL) 217 sig = get_item(IDX_SIGNATURE) 218 method = get_item(IDX_METHOD_NAME) 219 kls = get_item(IDX_CLASS_NAME) 220 221 debug_print ((sig, method, kls)) 222 223 staticness = extract_staticness(staticness) 224 kls = javafy_name(kls) 225 param_types = sig_to_parameter_type_list(sig) 226 pretty_params = param_types 227 pretty_params = [javafy_name(i) for i in param_types] 228 229 return MethodInfo(staticness, pretty_params, method, kls) 230 231 # parse a line containing ' V(...)' into a MethodInfo 232 def parse_line(line): 233 line = line.strip() 234 if not line.startswith("V("): 235 return None 236 237 line = re.sub(r'V[(](.*)[)]', r'\1', line) 238 debug_print(line) 239 240 items = line.split(",") 241 242 method_info = parse_method_info(items) 243 return method_info 244 245 # Generate all the MethodInfo that we parse from intrinsics_list.h 246 def parse_all_method_infos(): 247 with open(INTRINSICS_LIST_H) as f: 248 for line in f: 249 s = parse_line(line) 250 if s is not None: 251 yield s 252 253 # Format a receiver name. For statics, it's the class name, for receivers, it's an instance variable 254 def format_receiver_name(method_info): 255 receiver = method_info.klass 256 if method_info.staticness == 'virtual': 257 receiver = "instance_" + method_info.klass.replace(".", "_") 258 return receiver 259 260 # Format a dummy call with dummy method parameters to the requested method. 261 def format_call_to(method_info): 262 dummy_args = ", ".join(method_info.dummy_parameters()) 263 receiver = format_receiver_name(method_info) 264 265 return ("%s.%s(%s);" %(receiver, method_info.method_name, dummy_args)) 266 267 # Format a static variable with an instance that could be used as the receiver 268 # (or None for non-static methods). 269 def format_instance_variable(method_info): 270 if method_info.staticness == 'static': 271 return None 272 return "static %s %s = %s;" %(method_info.klass, format_receiver_name(method_info), method_info.dummy_instance_value()) 273 274 def format_initialize_klass(method_info): 275 return "%s.class.toString();" %(method_info.klass) 276 277 def indent_list(lst, indent): 278 return [' ' * indent + i for i in lst] 279 280 def main(): 281 global debug_printing_enabled 282 parser = argparse.ArgumentParser(description='Generate art/test/988-method-trace/src/art/Test988Intrinsics.java') 283 parser.add_argument('-d', '--debug', action='store_true', dest='debug', help='Print extra debugging information to stderr.') 284 parser.add_argument('output_file', nargs='?', metavar='<output-file>', default=sys.stdout, type=argparse.FileType('w'), help='Destination file to write to (default: stdout).') 285 args = parser.parse_args() 286 287 debug_printing_enabled = args.debug 288 289 ##### 290 291 call_str_list = [] 292 instance_variable_dict = collections.OrderedDict() 293 initialize_klass_dict = collections.OrderedDict() 294 for i in parse_all_method_infos(): 295 debug_print(i) 296 if i.is_blacklisted(): 297 debug_print("Blacklisted: " + str(i)) 298 continue 299 300 call_str = format_call_to(i) 301 debug_print(call_str) 302 303 call_str_list.append(call_str) 304 305 instance_variable = format_instance_variable(i) 306 if instance_variable is not None: 307 debug_print(instance_variable) 308 instance_variable_dict[i.klass] = instance_variable 309 310 initialize_klass_dict[i.klass] = format_initialize_klass(i) 311 312 static_fields = indent_list([ value for (key, value) in instance_variable_dict.items() ], 2) 313 test_body = indent_list(call_str_list, 4) 314 initialize_classes = indent_list([ value for (key, value) in initialize_klass_dict.items() ], 4) 315 316 print(OUTPUT_TPL.substitute(static_fields="\n".join(static_fields), 317 test_body="\n".join(test_body), 318 initialize_classes="\n".join(initialize_classes)). 319 strip("\n"), \ 320 file=args.output_file) 321 322 if __name__ == '__main__': 323 main() 324