1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*9e94795aSAndroid Build Coastguard Worker 3*9e94795aSAndroid Build Coastguard Worker"""Tool to find static libraries that maybe should be shared libraries and shared libraries that maybe should be static libraries. 4*9e94795aSAndroid Build Coastguard Worker 5*9e94795aSAndroid Build Coastguard WorkerThis tool only looks at the module-info.json for the current target. 6*9e94795aSAndroid Build Coastguard Worker 7*9e94795aSAndroid Build Coastguard WorkerExample of "class" types for each of the modules in module-info.json 8*9e94795aSAndroid Build Coastguard Worker "EXECUTABLES": 2307, 9*9e94795aSAndroid Build Coastguard Worker "ETC": 9094, 10*9e94795aSAndroid Build Coastguard Worker "NATIVE_TESTS": 10461, 11*9e94795aSAndroid Build Coastguard Worker "APPS": 2885, 12*9e94795aSAndroid Build Coastguard Worker "JAVA_LIBRARIES": 5205, 13*9e94795aSAndroid Build Coastguard Worker "EXECUTABLES/JAVA_LIBRARIES": 119, 14*9e94795aSAndroid Build Coastguard Worker "FAKE": 553, 15*9e94795aSAndroid Build Coastguard Worker "SHARED_LIBRARIES/STATIC_LIBRARIES": 7591, 16*9e94795aSAndroid Build Coastguard Worker "STATIC_LIBRARIES": 11535, 17*9e94795aSAndroid Build Coastguard Worker "SHARED_LIBRARIES": 10852, 18*9e94795aSAndroid Build Coastguard Worker "HEADER_LIBRARIES": 1897, 19*9e94795aSAndroid Build Coastguard Worker "DYLIB_LIBRARIES": 1262, 20*9e94795aSAndroid Build Coastguard Worker "RLIB_LIBRARIES": 3413, 21*9e94795aSAndroid Build Coastguard Worker "ROBOLECTRIC": 39, 22*9e94795aSAndroid Build Coastguard Worker "PACKAGING": 5, 23*9e94795aSAndroid Build Coastguard Worker "PROC_MACRO_LIBRARIES": 36, 24*9e94795aSAndroid Build Coastguard Worker "RENDERSCRIPT_BITCODE": 17, 25*9e94795aSAndroid Build Coastguard Worker "DYLIB_LIBRARIES/RLIB_LIBRARIES": 8, 26*9e94795aSAndroid Build Coastguard Worker "ETC/FAKE": 1 27*9e94795aSAndroid Build Coastguard Worker 28*9e94795aSAndroid Build Coastguard WorkerNone of the "SHARED_LIBRARIES/STATIC_LIBRARIES" are double counted in the 29*9e94795aSAndroid Build Coastguard Workermodules with one class 30*9e94795aSAndroid Build Coastguard WorkerRLIB/ 31*9e94795aSAndroid Build Coastguard Worker 32*9e94795aSAndroid Build Coastguard WorkerAll of these classes have shared_libs and/or static_libs 33*9e94795aSAndroid Build Coastguard Worker "EXECUTABLES", 34*9e94795aSAndroid Build Coastguard Worker "SHARED_LIBRARIES", 35*9e94795aSAndroid Build Coastguard Worker "STATIC_LIBRARIES", 36*9e94795aSAndroid Build Coastguard Worker "SHARED_LIBRARIES/STATIC_LIBRARIES", # cc_library 37*9e94795aSAndroid Build Coastguard Worker "HEADER_LIBRARIES", 38*9e94795aSAndroid Build Coastguard Worker "NATIVE_TESTS", # test modules 39*9e94795aSAndroid Build Coastguard Worker "DYLIB_LIBRARIES", # rust 40*9e94795aSAndroid Build Coastguard Worker "RLIB_LIBRARIES", # rust 41*9e94795aSAndroid Build Coastguard Worker "ETC", # rust_bindgen 42*9e94795aSAndroid Build Coastguard Worker""" 43*9e94795aSAndroid Build Coastguard Worker 44*9e94795aSAndroid Build Coastguard Workerfrom collections import defaultdict 45*9e94795aSAndroid Build Coastguard Worker 46*9e94795aSAndroid Build Coastguard Workerimport json, os, argparse 47*9e94795aSAndroid Build Coastguard Worker 48*9e94795aSAndroid Build Coastguard WorkerANDROID_PRODUCT_OUT = os.environ.get("ANDROID_PRODUCT_OUT") 49*9e94795aSAndroid Build Coastguard Worker# If a shared library is used less than MAX_SHARED_INCLUSIONS times in a target, 50*9e94795aSAndroid Build Coastguard Worker# then it will likely save memory by changing it to a static library 51*9e94795aSAndroid Build Coastguard Worker# This move will also use less storage 52*9e94795aSAndroid Build Coastguard WorkerMAX_SHARED_INCLUSIONS = 2 53*9e94795aSAndroid Build Coastguard Worker# If a static library is used more than MAX_STATIC_INCLUSIONS times in a target, 54*9e94795aSAndroid Build Coastguard Worker# then it will likely save memory by changing it to a shared library 55*9e94795aSAndroid Build Coastguard Worker# This move will also likely use less storage 56*9e94795aSAndroid Build Coastguard WorkerMIN_STATIC_INCLUSIONS = 3 57*9e94795aSAndroid Build Coastguard Worker 58*9e94795aSAndroid Build Coastguard Worker 59*9e94795aSAndroid Build Coastguard Workerdef parse_args(): 60*9e94795aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 61*9e94795aSAndroid Build Coastguard Worker description=( 62*9e94795aSAndroid Build Coastguard Worker "Parse module-info.jso and display information about static and" 63*9e94795aSAndroid Build Coastguard Worker " shared library dependencies." 64*9e94795aSAndroid Build Coastguard Worker ) 65*9e94795aSAndroid Build Coastguard Worker ) 66*9e94795aSAndroid Build Coastguard Worker parser.add_argument( 67*9e94795aSAndroid Build Coastguard Worker "--module", dest="module", help="Print the info for the module." 68*9e94795aSAndroid Build Coastguard Worker ) 69*9e94795aSAndroid Build Coastguard Worker parser.add_argument( 70*9e94795aSAndroid Build Coastguard Worker "--shared", 71*9e94795aSAndroid Build Coastguard Worker dest="print_shared", 72*9e94795aSAndroid Build Coastguard Worker action=argparse.BooleanOptionalAction, 73*9e94795aSAndroid Build Coastguard Worker help=( 74*9e94795aSAndroid Build Coastguard Worker "Print the list of libraries that are shared_libs for fewer than {}" 75*9e94795aSAndroid Build Coastguard Worker " modules.".format(MAX_SHARED_INCLUSIONS) 76*9e94795aSAndroid Build Coastguard Worker ), 77*9e94795aSAndroid Build Coastguard Worker ) 78*9e94795aSAndroid Build Coastguard Worker parser.add_argument( 79*9e94795aSAndroid Build Coastguard Worker "--static", 80*9e94795aSAndroid Build Coastguard Worker dest="print_static", 81*9e94795aSAndroid Build Coastguard Worker action=argparse.BooleanOptionalAction, 82*9e94795aSAndroid Build Coastguard Worker help=( 83*9e94795aSAndroid Build Coastguard Worker "Print the list of libraries that are static_libs for more than {}" 84*9e94795aSAndroid Build Coastguard Worker " modules.".format(MIN_STATIC_INCLUSIONS) 85*9e94795aSAndroid Build Coastguard Worker ), 86*9e94795aSAndroid Build Coastguard Worker ) 87*9e94795aSAndroid Build Coastguard Worker parser.add_argument( 88*9e94795aSAndroid Build Coastguard Worker "--recursive", 89*9e94795aSAndroid Build Coastguard Worker dest="recursive", 90*9e94795aSAndroid Build Coastguard Worker action=argparse.BooleanOptionalAction, 91*9e94795aSAndroid Build Coastguard Worker default=True, 92*9e94795aSAndroid Build Coastguard Worker help=( 93*9e94795aSAndroid Build Coastguard Worker "Gather all dependencies of EXECUTABLES recursvily before calculating" 94*9e94795aSAndroid Build Coastguard Worker " the stats. This eliminates duplicates from multiple libraries" 95*9e94795aSAndroid Build Coastguard Worker " including the same dependencies in a single binary." 96*9e94795aSAndroid Build Coastguard Worker ), 97*9e94795aSAndroid Build Coastguard Worker ) 98*9e94795aSAndroid Build Coastguard Worker parser.add_argument( 99*9e94795aSAndroid Build Coastguard Worker "--both", 100*9e94795aSAndroid Build Coastguard Worker dest="both", 101*9e94795aSAndroid Build Coastguard Worker action=argparse.BooleanOptionalAction, 102*9e94795aSAndroid Build Coastguard Worker default=False, 103*9e94795aSAndroid Build Coastguard Worker help=( 104*9e94795aSAndroid Build Coastguard Worker "Print a list of libraries that are including libraries as both" 105*9e94795aSAndroid Build Coastguard Worker " static and shared" 106*9e94795aSAndroid Build Coastguard Worker ), 107*9e94795aSAndroid Build Coastguard Worker ) 108*9e94795aSAndroid Build Coastguard Worker return parser.parse_args() 109*9e94795aSAndroid Build Coastguard Worker 110*9e94795aSAndroid Build Coastguard Worker 111*9e94795aSAndroid Build Coastguard Workerclass TransitiveHelper: 112*9e94795aSAndroid Build Coastguard Worker 113*9e94795aSAndroid Build Coastguard Worker def __init__(self): 114*9e94795aSAndroid Build Coastguard Worker # keep a list of already expanded libraries so we don't end up in a cycle 115*9e94795aSAndroid Build Coastguard Worker self.visited = defaultdict(lambda: defaultdict(set)) 116*9e94795aSAndroid Build Coastguard Worker 117*9e94795aSAndroid Build Coastguard Worker # module is an object from the module-info dictionary 118*9e94795aSAndroid Build Coastguard Worker # module_info is the dictionary from module-info.json 119*9e94795aSAndroid Build Coastguard Worker # modify the module's shared_libs and static_libs with all of the transient 120*9e94795aSAndroid Build Coastguard Worker # dependencies required from all of the explicit dependencies 121*9e94795aSAndroid Build Coastguard Worker def flattenDeps(self, module, module_info): 122*9e94795aSAndroid Build Coastguard Worker libs_snapshot = dict(shared_libs = set(module.get("shared_libs",{})), static_libs = set(module.get("static_libs",{}))) 123*9e94795aSAndroid Build Coastguard Worker 124*9e94795aSAndroid Build Coastguard Worker for lib_class in ["shared_libs", "static_libs"]: 125*9e94795aSAndroid Build Coastguard Worker for lib in libs_snapshot[lib_class]: 126*9e94795aSAndroid Build Coastguard Worker if not lib or lib not in module_info or lib_class not in module: 127*9e94795aSAndroid Build Coastguard Worker continue 128*9e94795aSAndroid Build Coastguard Worker if lib in self.visited: 129*9e94795aSAndroid Build Coastguard Worker module[lib_class].update(self.visited[lib][lib_class]) 130*9e94795aSAndroid Build Coastguard Worker else: 131*9e94795aSAndroid Build Coastguard Worker res = self.flattenDeps(module_info[lib], module_info) 132*9e94795aSAndroid Build Coastguard Worker module[lib_class].update(res.get(lib_class, {})) 133*9e94795aSAndroid Build Coastguard Worker self.visited[lib][lib_class].update(res.get(lib_class, {})) 134*9e94795aSAndroid Build Coastguard Worker 135*9e94795aSAndroid Build Coastguard Worker return module 136*9e94795aSAndroid Build Coastguard Worker 137*9e94795aSAndroid Build Coastguard Workerdef main(): 138*9e94795aSAndroid Build Coastguard Worker module_info = json.load(open(ANDROID_PRODUCT_OUT + "/module-info.json")) 139*9e94795aSAndroid Build Coastguard Worker 140*9e94795aSAndroid Build Coastguard Worker args = parse_args() 141*9e94795aSAndroid Build Coastguard Worker 142*9e94795aSAndroid Build Coastguard Worker if args.module: 143*9e94795aSAndroid Build Coastguard Worker if args.module not in module_info: 144*9e94795aSAndroid Build Coastguard Worker print("Module {} does not exist".format(args.module)) 145*9e94795aSAndroid Build Coastguard Worker exit(1) 146*9e94795aSAndroid Build Coastguard Worker 147*9e94795aSAndroid Build Coastguard Worker # turn all of the static_libs and shared_libs lists into sets to make them 148*9e94795aSAndroid Build Coastguard Worker # easier to update 149*9e94795aSAndroid Build Coastguard Worker for _, module in module_info.items(): 150*9e94795aSAndroid Build Coastguard Worker module["shared_libs"] = set(module.get("shared_libs", {})) 151*9e94795aSAndroid Build Coastguard Worker module["static_libs"] = set(module.get("static_libs", {})) 152*9e94795aSAndroid Build Coastguard Worker 153*9e94795aSAndroid Build Coastguard Worker includedStatically = defaultdict(set) 154*9e94795aSAndroid Build Coastguard Worker includedSharedly = defaultdict(set) 155*9e94795aSAndroid Build Coastguard Worker includedBothly = defaultdict(set) 156*9e94795aSAndroid Build Coastguard Worker transitive = TransitiveHelper() 157*9e94795aSAndroid Build Coastguard Worker for name, module in module_info.items(): 158*9e94795aSAndroid Build Coastguard Worker if args.recursive: 159*9e94795aSAndroid Build Coastguard Worker # in this recursive mode we only want to see what is included by the executables 160*9e94795aSAndroid Build Coastguard Worker if "EXECUTABLES" not in module["class"]: 161*9e94795aSAndroid Build Coastguard Worker continue 162*9e94795aSAndroid Build Coastguard Worker module = transitive.flattenDeps(module, module_info) 163*9e94795aSAndroid Build Coastguard Worker # filter out fuzzers by their dependency on clang 164*9e94795aSAndroid Build Coastguard Worker if "static_libs" in module: 165*9e94795aSAndroid Build Coastguard Worker if "libclang_rt.fuzzer" in module["static_libs"]: 166*9e94795aSAndroid Build Coastguard Worker continue 167*9e94795aSAndroid Build Coastguard Worker else: 168*9e94795aSAndroid Build Coastguard Worker if "NATIVE_TESTS" in module["class"]: 169*9e94795aSAndroid Build Coastguard Worker # We don't care about how tests are including libraries 170*9e94795aSAndroid Build Coastguard Worker continue 171*9e94795aSAndroid Build Coastguard Worker 172*9e94795aSAndroid Build Coastguard Worker # count all of the shared and static libs included in this module 173*9e94795aSAndroid Build Coastguard Worker if "shared_libs" in module: 174*9e94795aSAndroid Build Coastguard Worker for lib in module["shared_libs"]: 175*9e94795aSAndroid Build Coastguard Worker includedSharedly[lib].add(name) 176*9e94795aSAndroid Build Coastguard Worker if "static_libs" in module: 177*9e94795aSAndroid Build Coastguard Worker for lib in module["static_libs"]: 178*9e94795aSAndroid Build Coastguard Worker includedStatically[lib].add(name) 179*9e94795aSAndroid Build Coastguard Worker 180*9e94795aSAndroid Build Coastguard Worker if "shared_libs" in module and "static_libs" in module: 181*9e94795aSAndroid Build Coastguard Worker intersection = set(module["shared_libs"]).intersection( 182*9e94795aSAndroid Build Coastguard Worker module["static_libs"] 183*9e94795aSAndroid Build Coastguard Worker ) 184*9e94795aSAndroid Build Coastguard Worker if intersection: 185*9e94795aSAndroid Build Coastguard Worker includedBothly[name] = intersection 186*9e94795aSAndroid Build Coastguard Worker 187*9e94795aSAndroid Build Coastguard Worker if args.print_shared: 188*9e94795aSAndroid Build Coastguard Worker print( 189*9e94795aSAndroid Build Coastguard Worker "Shared libraries that are included by fewer than {} modules on a" 190*9e94795aSAndroid Build Coastguard Worker " device:".format(MAX_SHARED_INCLUSIONS) 191*9e94795aSAndroid Build Coastguard Worker ) 192*9e94795aSAndroid Build Coastguard Worker for name, libs in includedSharedly.items(): 193*9e94795aSAndroid Build Coastguard Worker if len(libs) < MAX_SHARED_INCLUSIONS: 194*9e94795aSAndroid Build Coastguard Worker print("{}: {} included by: {}".format(name, len(libs), libs)) 195*9e94795aSAndroid Build Coastguard Worker 196*9e94795aSAndroid Build Coastguard Worker if args.print_static: 197*9e94795aSAndroid Build Coastguard Worker print( 198*9e94795aSAndroid Build Coastguard Worker "Libraries that are included statically by more than {} modules on a" 199*9e94795aSAndroid Build Coastguard Worker " device:".format(MIN_STATIC_INCLUSIONS) 200*9e94795aSAndroid Build Coastguard Worker ) 201*9e94795aSAndroid Build Coastguard Worker for name, libs in includedStatically.items(): 202*9e94795aSAndroid Build Coastguard Worker if len(libs) > MIN_STATIC_INCLUSIONS: 203*9e94795aSAndroid Build Coastguard Worker print("{}: {} included by: {}".format(name, len(libs), libs)) 204*9e94795aSAndroid Build Coastguard Worker 205*9e94795aSAndroid Build Coastguard Worker if args.both: 206*9e94795aSAndroid Build Coastguard Worker allIncludedBothly = set() 207*9e94795aSAndroid Build Coastguard Worker for name, libs in includedBothly.items(): 208*9e94795aSAndroid Build Coastguard Worker allIncludedBothly.update(libs) 209*9e94795aSAndroid Build Coastguard Worker 210*9e94795aSAndroid Build Coastguard Worker print( 211*9e94795aSAndroid Build Coastguard Worker "List of libraries used both statically and shared in the same" 212*9e94795aSAndroid Build Coastguard Worker " processes:\n {}\n\n".format("\n".join(sorted(allIncludedBothly))) 213*9e94795aSAndroid Build Coastguard Worker ) 214*9e94795aSAndroid Build Coastguard Worker print( 215*9e94795aSAndroid Build Coastguard Worker "List of libraries used both statically and shared in any processes:\n {}".format("\n".join(sorted(includedStatically.keys() & includedSharedly.keys())))) 216*9e94795aSAndroid Build Coastguard Worker 217*9e94795aSAndroid Build Coastguard Worker if args.module: 218*9e94795aSAndroid Build Coastguard Worker print(json.dumps(module_info[args.module], default=list, indent=2)) 219*9e94795aSAndroid Build Coastguard Worker print( 220*9e94795aSAndroid Build Coastguard Worker "{} is included in shared_libs {} times by these modules: {}".format( 221*9e94795aSAndroid Build Coastguard Worker args.module, len(includedSharedly[args.module]), 222*9e94795aSAndroid Build Coastguard Worker includedSharedly[args.module] 223*9e94795aSAndroid Build Coastguard Worker ) 224*9e94795aSAndroid Build Coastguard Worker ) 225*9e94795aSAndroid Build Coastguard Worker print( 226*9e94795aSAndroid Build Coastguard Worker "{} is included in static_libs {} times by these modules: {}".format( 227*9e94795aSAndroid Build Coastguard Worker args.module, len(includedStatically[args.module]), 228*9e94795aSAndroid Build Coastguard Worker includedStatically[args.module] 229*9e94795aSAndroid Build Coastguard Worker ) 230*9e94795aSAndroid Build Coastguard Worker ) 231*9e94795aSAndroid Build Coastguard Worker print("Shared libs included by this module that are used in fewer than {} processes:\n{}".format( 232*9e94795aSAndroid Build Coastguard Worker MAX_SHARED_INCLUSIONS, [x for x in module_info[args.module]["shared_libs"] if len(includedSharedly[x]) < MAX_SHARED_INCLUSIONS])) 233*9e94795aSAndroid Build Coastguard Worker 234*9e94795aSAndroid Build Coastguard Worker 235*9e94795aSAndroid Build Coastguard Worker 236*9e94795aSAndroid Build Coastguard Workerif __name__ == "__main__": 237*9e94795aSAndroid Build Coastguard Worker main() 238