Home | History | Annotate | Download | only in 988-method-trace
      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