36SRC_ROOT = os.path.dirname(
37 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
47BUILD_ROOT_DIR = os.path.abspath(os.path.join(os.path.realpath(__file__),
'..',
'..',
'..',
'..'))
51 parser = argparse.ArgumentParser(
52 description=
'A script that compares before/after malioc analysis results',
58 help=
'The path to a directory tree containing new malioc results in json files.',
64 help=
'The path to a json file containing existing malioc results.',
67 '--after-relative-to-src',
70 'A relative path calculated from the engine src directory to '
71 'a directory tree containing new malioc results in json files'
75 '--before-relative-to-src',
78 'A relative path calculated from the engine src directory to '
79 'a json file containing existing malioc results in json files'
87 help=
'Print a unified diff to stdout when differences are found.',
94 help=
'Write results from the --after tree to the --before file.',
101 help=
'Emit verbose output.',
103 return parser.parse_args(argv)
107 if not args.after
and not args.after_relative_to_src:
108 print(
'--after argument or --after-relative-to-src must be specified.')
111 if not args.before
and not args.before_relative_to_src:
112 print(
'--before argument or --before-relative-to-src must be specified.')
117 args.before = (args.before
or os.path.join(BUILD_ROOT_DIR, args.before_relative_to_src))
118 args.after = (args.after
or os.path.join(BUILD_ROOT_DIR, args.after_relative_to_src))
120 if not args.after
or not os.path.isdir(args.after):
121 print(
'The --after argument must refer to a directory.')
123 if not args.before
or (
not args.update
and not os.path.isfile(args.before)):
124 print(
'The --before argument must refer to an existing file.')
132 performance[
'pipelines'] = performance_json[
'pipelines']
134 longest_path_cycles = performance_json[
'longest_path_cycles']
135 performance[
'longest_path_cycles'] = longest_path_cycles[
'cycle_count']
136 performance[
'longest_path_bound_pipelines'] = longest_path_cycles[
'bound_pipelines']
138 shortest_path_cycles = performance_json[
'shortest_path_cycles']
139 performance[
'shortest_path_cycles'] = shortest_path_cycles[
'cycle_count']
140 performance[
'shortest_path_bound_pipelines'] = shortest_path_cycles[
'bound_pipelines']
142 total_cycles = performance_json[
'total_cycles']
143 performance[
'total_cycles'] = total_cycles[
'cycle_count']
144 performance[
'total_bound_pipelines'] = total_cycles[
'bound_pipelines']
151 with open(json_file,
'r')
as file:
152 json_obj = json.load(file)
154 build_gen_dir = os.path.dirname(malioc_tree)
157 for shader
in json_obj[
'shaders']:
159 if shader[
'hardware'][
'core']
not in CORES:
162 filename = os.path.relpath(shader[
'filename'], build_gen_dir)
163 if filename.startswith(
'../..'):
164 filename = filename[6:]
165 if filename.startswith(
'../'):
166 filename = filename[3:]
167 result[
'filename'] = filename
168 result[
'core'] = shader[
'hardware'][
'core']
169 result[
'type'] = shader[
'shader'][
'type']
170 for prop
in shader[
'properties']:
171 result[prop[
'name']] = prop[
'value']
173 result[
'variants'] = {}
174 for variant
in shader[
'variants']:
176 for prop
in variant[
'properties']:
177 variant_result[prop[
'name']] = prop[
'value']
179 performance_json = variant[
'performance']
181 variant_result[
'performance'] = performance
182 result[
'variants'][variant[
'name']] = variant_result
183 results.append(result)
196 for root, _, files
in os.walk(malioc_tree):
198 if not file.endswith(
'.json'):
200 full_path = os.path.join(root, file)
202 if shader[
'filename']
not in results:
203 results[shader[
'filename']] = {}
204 results[shader[
'filename']][shader[
'core']] = shader
212 formats = [
'{:<{width}{fmt}}' if ele
is not None else '{:<{width}s}' for ele
in lst]
213 sanitized_list = [x
if x
is not None else 'null' for x
in lst]
214 return (sep.join(formats)).
format(width=
'' if width == 0
else width, fmt=fmt, *sanitized_list)
218 cycles = [[
'longest_path_cycles',
'longest_path_bound_pipelines'],
219 [
'shortest_path_cycles',
'shortest_path_bound_pipelines'],
220 [
'total_cycles',
'total_bound_pipelines']]
223 if before[cycle[0]] == after[cycle[0]]:
225 before_cycles = before[cycle[0]]
226 before_bounds = before[cycle[1]]
227 after_cycles = after[cycle[0]]
228 after_bounds = after[cycle[1]]
230 '{} in variant {}\n{}{}\n{:<8}{}{}\n{:<8}{}{}\n'.
format(
248 for variant_name, before_variant
in befores.items():
249 if variant_name
in afters:
250 after_variant = afters[variant_name]
251 for variant_key, before_variant_val
in before_variant.items():
252 after_variant_val = after_variant[variant_key]
253 if variant_key ==
'performance':
255 elif before_variant_val != after_variant_val:
257 'In variant {}:\n {vkey}: {} <- before\n {vkey}: {} <- after'.
format(
271 for key, before_val
in before_shader.items():
272 after_val = after_shader[key]
273 if key ==
'variants':
275 elif key ==
'performance':
277 elif before_val != after_val:
278 differences += [
'{}:\n {} <- before\n {} <- after'.
format(key, before_val, after_val)]
280 if bool(differences):
281 build_gen_dir = os.path.dirname(malioc_tree)
282 filename = before_shader[
'filename']
283 core = before_shader[
'core']
284 typ = before_shader[
'type']
285 print(
'Changes found in shader {} on core {}:'.
format(filename, core))
286 for diff
in differences:
289 '\nFor a full report, run:\n $ malioc --{} --core {} {}/{}\n'.
format(
290 typ.lower(), core, build_gen_dir, filename
294 return bool(differences)
303 if not bool(after_json):
304 print(
'Did not find any malioc results under {}.'.
format(args.after))
309 with open(args.before,
'w')
as file:
310 json.dump(after_json, file, sort_keys=
True, indent=2)
313 with open(args.before,
'r')
as file:
314 before_json = json.load(file)
317 for filename, shaders
in before_json.items():
318 if filename
not in after_json.keys():
319 print(
'Shader "{}" has been removed.'.
format(filename))
322 for core, before_shader
in shaders.items():
323 if core
not in after_json[filename].keys():
325 after_shader = after_json[filename][core]
329 for filename, shaders
in after_json.items():
330 if filename
not in before_json:
336 'There are new shaders, shaders have been removed, or performance '
337 'changes to existing shaders. The golden file must be updated after a '
338 'build of android_debug_unopt using the --malioc-path flag to the '
339 'flutter/tools/gn script.\n\n'
340 '$ ./flutter/impeller/tools/malioc_diff.py --before {} --after {} --update'.
format(
341 args.before, args.after
345 before_lines = json.dumps(before_json, sort_keys=
True, indent=2).splitlines(keepends=
True)
346 after_lines = json.dumps(after_json, sort_keys=
True, indent=2).splitlines(keepends=
True)
347 before_path = os.path.relpath(os.path.abspath(args.before), start=SRC_ROOT)
348 diff = difflib.unified_diff(before_lines, after_lines, fromfile=before_path)
349 print(
'\nYou can alternately apply the diff below:')
350 print(
'patch -p0 <<DONE')
354 return 1
if changed
else 0
357if __name__ ==
'__main__':
358 sys.exit(
main(sys.argv))
uint32_t uint32_t * format
def read_malioc_file(malioc_tree, json_file)
def compare_performance(variant, before, after)
def read_malioc_tree(malioc_tree)
def compare_variants(befores, afters)
def pretty_list(lst, fmt='s', sep='', width=12)
def read_malioc_file_performance(performance_json)
def compare_shaders(malioc_tree, before_shader, after_shader)
def print(*args, **kwargs)