xref: /aosp_15_r20/art/imgdiag/create_dirty_image_objects.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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