1*795d594fSAndroid Build Coastguard Worker#! /usr/bin/env python3 2*795d594fSAndroid Build Coastguard Worker# 3*795d594fSAndroid Build Coastguard Worker# Copyright 2023, The Android Open Source Project 4*795d594fSAndroid Build Coastguard Worker# 5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*795d594fSAndroid Build Coastguard Worker# 9*795d594fSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*795d594fSAndroid Build Coastguard Worker# 11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*795d594fSAndroid Build Coastguard Worker# limitations under the License. 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Workerimport argparse 18*795d594fSAndroid Build Coastguard Workerfrom collections import defaultdict 19*795d594fSAndroid Build Coastguard Workerfrom enum import Enum 20*795d594fSAndroid Build Coastguard Workerimport os 21*795d594fSAndroid Build Coastguard Workerimport re 22*795d594fSAndroid Build Coastguard Worker 23*795d594fSAndroid Build Coastguard Worker 24*795d594fSAndroid Build Coastguard Workerclass SortType(Enum): 25*795d594fSAndroid Build Coastguard Worker NONE = 'none' 26*795d594fSAndroid Build Coastguard Worker SIMPLE = 'simple' 27*795d594fSAndroid Build Coastguard Worker OPT_NEIGHBOURS = 'opt_neighbours' 28*795d594fSAndroid Build Coastguard Worker 29*795d594fSAndroid Build Coastguard Worker 30*795d594fSAndroid Build Coastguard Workerdef merge_same_procnames(entries): 31*795d594fSAndroid Build Coastguard Worker path_regex = r'(.+)_(\d+).txt' 32*795d594fSAndroid Build Coastguard Worker prog = re.compile(path_regex) 33*795d594fSAndroid Build Coastguard Worker 34*795d594fSAndroid Build Coastguard Worker merged_entries = defaultdict(set) 35*795d594fSAndroid Build Coastguard Worker for path, objs in entries: 36*795d594fSAndroid Build Coastguard Worker basename = os.path.basename(path) 37*795d594fSAndroid Build Coastguard Worker m = prog.match(basename) 38*795d594fSAndroid Build Coastguard Worker if m: 39*795d594fSAndroid Build Coastguard Worker merged_entries[m.group(1)].update(objs) 40*795d594fSAndroid Build Coastguard Worker 41*795d594fSAndroid Build Coastguard Worker return sorted(merged_entries.items(), key=lambda x: len(x[1])) 42*795d594fSAndroid Build Coastguard Worker 43*795d594fSAndroid Build Coastguard Worker 44*795d594fSAndroid Build Coastguard Workerdef opt_neighbours(sort_keys): 45*795d594fSAndroid Build Coastguard Worker sort_keys = dict(sort_keys) 46*795d594fSAndroid Build Coastguard Worker res = list() 47*795d594fSAndroid Build Coastguard Worker 48*795d594fSAndroid Build Coastguard Worker # Start with a bin with the lowest process and objects count. 49*795d594fSAndroid Build Coastguard Worker cur_key = min( 50*795d594fSAndroid Build Coastguard Worker sort_keys.items(), key=lambda item: (item[0].bit_count(), len(item[1])) 51*795d594fSAndroid Build Coastguard Worker )[0] 52*795d594fSAndroid Build Coastguard Worker res.append((cur_key, sort_keys[cur_key])) 53*795d594fSAndroid Build Coastguard Worker del sort_keys[cur_key] 54*795d594fSAndroid Build Coastguard Worker 55*795d594fSAndroid Build Coastguard Worker # Find next most similar sort key and update the result. 56*795d594fSAndroid Build Coastguard Worker while sort_keys: 57*795d594fSAndroid Build Coastguard Worker 58*795d594fSAndroid Build Coastguard Worker def jaccard_index(x): 59*795d594fSAndroid Build Coastguard Worker return (x & cur_key).bit_count() / (x | cur_key).bit_count() 60*795d594fSAndroid Build Coastguard Worker 61*795d594fSAndroid Build Coastguard Worker next_key = max(sort_keys.keys(), key=jaccard_index) 62*795d594fSAndroid Build Coastguard Worker res.append((next_key, sort_keys[next_key])) 63*795d594fSAndroid Build Coastguard Worker del sort_keys[next_key] 64*795d594fSAndroid Build Coastguard Worker cur_key = next_key 65*795d594fSAndroid Build Coastguard Worker return res 66*795d594fSAndroid Build Coastguard Worker 67*795d594fSAndroid Build Coastguard Worker 68*795d594fSAndroid Build Coastguard Workerdef process_dirty_entries(entries, sort_type): 69*795d594fSAndroid Build Coastguard Worker dirty_image_objects = [] 70*795d594fSAndroid Build Coastguard Worker 71*795d594fSAndroid Build Coastguard Worker union = set() 72*795d594fSAndroid Build Coastguard Worker for k, v in entries: 73*795d594fSAndroid Build Coastguard Worker union = union.union(v) 74*795d594fSAndroid Build Coastguard Worker 75*795d594fSAndroid Build Coastguard Worker if sort_type == SortType.NONE: 76*795d594fSAndroid Build Coastguard Worker dirty_obj_lines = [obj + '\n' for obj in sorted(union)] 77*795d594fSAndroid Build Coastguard Worker return (dirty_obj_lines, dict()) 78*795d594fSAndroid Build Coastguard Worker 79*795d594fSAndroid Build Coastguard Worker # sort_key -> [objs] 80*795d594fSAndroid Build Coastguard Worker sort_keys = defaultdict(list) 81*795d594fSAndroid Build Coastguard Worker for obj in union: 82*795d594fSAndroid Build Coastguard Worker sort_key = 0 83*795d594fSAndroid Build Coastguard Worker # Nth bit of sort_key is set if this object is dirty in Nth process. 84*795d594fSAndroid Build Coastguard Worker for idx, (k, v) in enumerate(entries): 85*795d594fSAndroid Build Coastguard Worker if obj in v: 86*795d594fSAndroid Build Coastguard Worker sort_key = (sort_key << 1) | 1 87*795d594fSAndroid Build Coastguard Worker else: 88*795d594fSAndroid Build Coastguard Worker sort_key = sort_key << 1 89*795d594fSAndroid Build Coastguard Worker 90*795d594fSAndroid Build Coastguard Worker sort_keys[sort_key].append(obj) 91*795d594fSAndroid Build Coastguard Worker 92*795d594fSAndroid Build Coastguard Worker sort_keys = sorted(sort_keys.items()) 93*795d594fSAndroid Build Coastguard Worker 94*795d594fSAndroid Build Coastguard Worker if sort_type == SortType.OPT_NEIGHBOURS: 95*795d594fSAndroid Build Coastguard Worker sort_keys = opt_neighbours(sort_keys) 96*795d594fSAndroid Build Coastguard Worker 97*795d594fSAndroid Build Coastguard Worker dirty_obj_lines = list() 98*795d594fSAndroid Build Coastguard Worker for idx, (_, objs) in enumerate(sort_keys): 99*795d594fSAndroid Build Coastguard Worker for obj in objs: 100*795d594fSAndroid Build Coastguard Worker dirty_obj_lines.append(obj + ' ' + str(idx) + '\n') 101*795d594fSAndroid Build Coastguard Worker 102*795d594fSAndroid Build Coastguard Worker return (dirty_obj_lines, sort_keys) 103*795d594fSAndroid Build Coastguard Worker 104*795d594fSAndroid Build Coastguard Worker 105*795d594fSAndroid Build Coastguard Workerdef main(): 106*795d594fSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 107*795d594fSAndroid Build Coastguard Worker description=( 108*795d594fSAndroid Build Coastguard Worker 'Create dirty-image-objects file from specified imgdiag output files.' 109*795d594fSAndroid Build Coastguard Worker ), 110*795d594fSAndroid Build Coastguard Worker formatter_class=argparse.ArgumentDefaultsHelpFormatter, 111*795d594fSAndroid Build Coastguard Worker ) 112*795d594fSAndroid Build Coastguard Worker parser.add_argument( 113*795d594fSAndroid Build Coastguard Worker 'imgdiag_files', 114*795d594fSAndroid Build Coastguard Worker nargs='+', 115*795d594fSAndroid Build Coastguard Worker help='imgdiag files to use.', 116*795d594fSAndroid Build Coastguard Worker ) 117*795d594fSAndroid Build Coastguard Worker parser.add_argument( 118*795d594fSAndroid Build Coastguard Worker '--sort-type', 119*795d594fSAndroid Build Coastguard Worker choices=[e.value for e in SortType], 120*795d594fSAndroid Build Coastguard Worker default=SortType.OPT_NEIGHBOURS.value, 121*795d594fSAndroid Build Coastguard Worker help=( 122*795d594fSAndroid Build Coastguard Worker 'Object sorting type. "simple" puts objects with the same usage' 123*795d594fSAndroid Build Coastguard Worker ' pattern in the same bins. "opt_neighbours" also tries to put bins' 124*795d594fSAndroid Build Coastguard Worker ' with similar usage patterns close to each other.' 125*795d594fSAndroid Build Coastguard Worker ), 126*795d594fSAndroid Build Coastguard Worker ) 127*795d594fSAndroid Build Coastguard Worker parser.add_argument( 128*795d594fSAndroid Build Coastguard Worker '--merge-same-procnames', 129*795d594fSAndroid Build Coastguard Worker action=argparse.BooleanOptionalAction, 130*795d594fSAndroid Build Coastguard Worker default=False, 131*795d594fSAndroid Build Coastguard Worker help=( 132*795d594fSAndroid Build Coastguard Worker 'Merge dirty objects from files with the same process name (different' 133*795d594fSAndroid Build Coastguard Worker ' pid). Files are expected to end with "_{pid}.txt"' 134*795d594fSAndroid Build Coastguard Worker ), 135*795d594fSAndroid Build Coastguard Worker ) 136*795d594fSAndroid Build Coastguard Worker parser.add_argument( 137*795d594fSAndroid Build Coastguard Worker '--output-filename', 138*795d594fSAndroid Build Coastguard Worker default='dirty-image-objects.txt', 139*795d594fSAndroid Build Coastguard Worker help='Output file for dirty image objects.', 140*795d594fSAndroid Build Coastguard Worker ) 141*795d594fSAndroid Build Coastguard Worker parser.add_argument( 142*795d594fSAndroid Build Coastguard Worker '--print-stats', 143*795d594fSAndroid Build Coastguard Worker action=argparse.BooleanOptionalAction, 144*795d594fSAndroid Build Coastguard Worker default=False, 145*795d594fSAndroid Build Coastguard Worker help='Print dirty object stats.', 146*795d594fSAndroid Build Coastguard Worker ) 147*795d594fSAndroid Build Coastguard Worker 148*795d594fSAndroid Build Coastguard Worker args = parser.parse_args() 149*795d594fSAndroid Build Coastguard Worker 150*795d594fSAndroid Build Coastguard Worker entries = list() 151*795d594fSAndroid Build Coastguard Worker for path in args.imgdiag_files: 152*795d594fSAndroid Build Coastguard Worker with open(path) as f: 153*795d594fSAndroid Build Coastguard Worker lines = f.readlines() 154*795d594fSAndroid Build Coastguard Worker prefix = 'dirty_obj: ' 155*795d594fSAndroid Build Coastguard Worker lines = [l.strip().removeprefix(prefix) for l in lines if prefix in l] 156*795d594fSAndroid Build Coastguard Worker entries.append((path, set(lines))) 157*795d594fSAndroid Build Coastguard Worker 158*795d594fSAndroid Build Coastguard Worker entries = sorted(entries, key=lambda x: len(x[1])) 159*795d594fSAndroid Build Coastguard Worker 160*795d594fSAndroid Build Coastguard Worker if args.merge_same_procnames: 161*795d594fSAndroid Build Coastguard Worker entries = merge_same_procnames(entries) 162*795d594fSAndroid Build Coastguard Worker 163*795d594fSAndroid Build Coastguard Worker print('Using processes:') 164*795d594fSAndroid Build Coastguard Worker for k, v in entries: 165*795d594fSAndroid Build Coastguard Worker print(f'{k}: {len(v)}') 166*795d594fSAndroid Build Coastguard Worker print() 167*795d594fSAndroid Build Coastguard Worker 168*795d594fSAndroid Build Coastguard Worker dirty_image_objects, sort_keys = process_dirty_entries( 169*795d594fSAndroid Build Coastguard Worker entries=entries, sort_type=SortType(args.sort_type) 170*795d594fSAndroid Build Coastguard Worker ) 171*795d594fSAndroid Build Coastguard Worker 172*795d594fSAndroid Build Coastguard Worker with open(args.output_filename, 'w') as f: 173*795d594fSAndroid Build Coastguard Worker f.writelines(dirty_image_objects) 174*795d594fSAndroid Build Coastguard Worker 175*795d594fSAndroid Build Coastguard Worker if args.print_stats: 176*795d594fSAndroid Build Coastguard Worker print(','.join(k for k, v in entries), ',obj_count') 177*795d594fSAndroid Build Coastguard Worker total_count = 0 178*795d594fSAndroid Build Coastguard Worker for sort_key, objs in sort_keys: 179*795d594fSAndroid Build Coastguard Worker bits_csv = ','.join( 180*795d594fSAndroid Build Coastguard Worker '{sort_key:0{width}b}'.format(sort_key=sort_key, width=len(entries)) 181*795d594fSAndroid Build Coastguard Worker ) 182*795d594fSAndroid Build Coastguard Worker print(bits_csv, ',', len(objs)) 183*795d594fSAndroid Build Coastguard Worker total_count += len(objs) 184*795d594fSAndroid Build Coastguard Worker print('total: ', total_count) 185*795d594fSAndroid Build Coastguard Worker 186*795d594fSAndroid Build Coastguard Worker 187*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__': 188*795d594fSAndroid Build Coastguard Worker main() 189