441f3c72d7
* The output.property looks like this: {"canvaskit.wasm": 7394679} (from https://ci.chromium.org/raw/build/logs.chromium.org/skia/5236eb95c4490b11/+/annotations) * Refactored scripts to avoid hardcoding the magic_seperator and total_size_bytes. * Added this capability to all 4 infra/bots/buildstats/* scripts. * Added missing tests for analyze_web_file and analyze_cpp_lib. The output.property will be eventually consumed by a Gerrit plugin to display change in bytes in the UI. Note: buildstats_cpp.py is not currently used by any bot. But if it is in the future it will work. No-Tree-Checks: true Bug: skia:11744 Change-Id: I593bae8ff73c35089cc2785a1cc4bc5b630007c4 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/377840 Commit-Queue: Ravi Mistry <rmistry@google.com> Reviewed-by: Kevin Lubick <kjlubick@google.com>
409 lines
12 KiB
Python
409 lines
12 KiB
Python
# Copyright 2018 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
# Recipe which analyzes a compiled binary for information (e.g. file size)
|
|
|
|
import ast
|
|
|
|
DEPS = [
|
|
'checkout',
|
|
'env',
|
|
'recipe_engine/context',
|
|
'recipe_engine/file',
|
|
'recipe_engine/path',
|
|
'recipe_engine/properties',
|
|
'recipe_engine/python',
|
|
'recipe_engine/raw_io',
|
|
'recipe_engine/step',
|
|
'run',
|
|
'vars',
|
|
]
|
|
|
|
|
|
MAGIC_SEPERATOR = '#$%^&*'
|
|
TOTAL_SIZE_BYTES_KEY = "total_size_bytes"
|
|
|
|
|
|
def add_binary_size_output_property(result, source, binary_size):
|
|
result.presentation.properties['binary_size_plugin'] = {source: binary_size}
|
|
|
|
|
|
def RunSteps(api):
|
|
api.vars.setup()
|
|
|
|
checkout_root = api.checkout.default_checkout_root
|
|
api.checkout.bot_update(checkout_root=checkout_root)
|
|
|
|
out_dir = api.vars.swarming_out_dir
|
|
# Any binaries to scan should be here.
|
|
bin_dir = api.vars.build_dir
|
|
|
|
api.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
|
|
|
|
analyzed = 0
|
|
with api.context(cwd=bin_dir):
|
|
files = api.file.glob_paths(
|
|
'find WASM binaries',
|
|
bin_dir,
|
|
'*.wasm',
|
|
test_data=['pathkit.wasm'])
|
|
analyzed += len(files)
|
|
if files:
|
|
analyze_wasm_file(api, checkout_root, out_dir, files)
|
|
|
|
files = api.file.glob_paths(
|
|
'find JS files',
|
|
bin_dir,
|
|
'*.js',
|
|
test_data=['pathkit.js'])
|
|
analyzed += len(files)
|
|
if files:
|
|
analyze_web_file(api, checkout_root, out_dir, files)
|
|
|
|
files = api.file.glob_paths(
|
|
'find JS mem files',
|
|
bin_dir,
|
|
'*.js.mem',
|
|
test_data=['pathkit.js.mem'])
|
|
analyzed += len(files)
|
|
if files:
|
|
analyze_web_file(api, checkout_root, out_dir, files)
|
|
|
|
files = api.file.glob_paths(
|
|
'find flutter library',
|
|
bin_dir,
|
|
'libflutter.so',
|
|
test_data=['libflutter.so'])
|
|
analyzed += len(files)
|
|
if files:
|
|
analyze_flutter_lib(api, checkout_root, out_dir, files)
|
|
|
|
files = api.file.glob_paths(
|
|
'find skia library',
|
|
bin_dir,
|
|
'libskia.so',
|
|
test_data=['libskia.so'])
|
|
analyzed += len(files)
|
|
if files:
|
|
analyze_cpp_lib(api, checkout_root, out_dir, files)
|
|
|
|
files = api.file.glob_paths(
|
|
'find skottie_tool',
|
|
bin_dir,
|
|
'skottie_tool',
|
|
test_data=['skottie_tool'])
|
|
analyzed += len(files)
|
|
if files:
|
|
make_treemap(api, checkout_root, out_dir, files)
|
|
|
|
files = api.file.glob_paths(
|
|
'find dm',
|
|
bin_dir,
|
|
'dm',
|
|
test_data=['dm'])
|
|
analyzed += len(files)
|
|
if files:
|
|
make_treemap(api, checkout_root, out_dir, files)
|
|
|
|
if not analyzed: # pragma: nocover
|
|
raise Exception('No files were analyzed!')
|
|
|
|
|
|
def keys_and_props(api):
|
|
keys = []
|
|
for k in sorted(api.vars.builder_cfg.keys()):
|
|
if not k in ['role']:
|
|
keys.extend([k, api.vars.builder_cfg[k]])
|
|
keystr = ' '.join(keys)
|
|
|
|
props = [
|
|
'gitHash', api.properties['revision'],
|
|
'swarming_bot_id', api.vars.swarming_bot_id,
|
|
'swarming_task_id', api.vars.swarming_task_id,
|
|
]
|
|
|
|
if api.vars.is_trybot:
|
|
props.extend([
|
|
'issue', str(api.vars.issue),
|
|
'patchset', str(api.vars.patchset),
|
|
'patch_storage', api.vars.patch_storage,
|
|
])
|
|
propstr = ' '.join(props)
|
|
return (keystr, propstr)
|
|
|
|
|
|
# Get the raw and gzipped size of the given file
|
|
def analyze_web_file(api, checkout_root, out_dir, files):
|
|
(keystr, propstr) = keys_and_props(api)
|
|
|
|
for f in files:
|
|
skia_dir = checkout_root.join('skia')
|
|
with api.context(cwd=skia_dir):
|
|
script = skia_dir.join('infra', 'bots', 'buildstats',
|
|
'buildstats_web.py')
|
|
step_data = api.run(api.python, 'Analyze %s' % f, script=script,
|
|
args=[f, out_dir, keystr, propstr, TOTAL_SIZE_BYTES_KEY,
|
|
MAGIC_SEPERATOR],
|
|
stdout=api.raw_io.output())
|
|
if step_data and step_data.stdout:
|
|
sections = step_data.stdout.split(MAGIC_SEPERATOR)
|
|
result = api.step.active_result
|
|
logs = result.presentation.logs
|
|
logs['perf_json'] = sections[1].split('\n')
|
|
|
|
add_binary_size_output_property(result, api.path.basename(f), (
|
|
ast.literal_eval(sections[1])
|
|
.get('results', {})
|
|
.get(api.path.basename(f), {})
|
|
.get('default', {})
|
|
.get(TOTAL_SIZE_BYTES_KEY, {})))
|
|
|
|
|
|
# Get the raw size and a few metrics from bloaty
|
|
def analyze_cpp_lib(api, checkout_root, out_dir, files):
|
|
(keystr, propstr) = keys_and_props(api)
|
|
bloaty_exe = api.path['start_dir'].join('bloaty', 'bloaty')
|
|
|
|
for f in files:
|
|
skia_dir = checkout_root.join('skia')
|
|
with api.context(cwd=skia_dir):
|
|
script = skia_dir.join('infra', 'bots', 'buildstats',
|
|
'buildstats_cpp.py')
|
|
step_data = api.run(api.python, 'Analyze %s' % f, script=script,
|
|
args=[f, out_dir, keystr, propstr, bloaty_exe, TOTAL_SIZE_BYTES_KEY,
|
|
MAGIC_SEPERATOR],
|
|
stdout=api.raw_io.output())
|
|
if step_data and step_data.stdout:
|
|
sections = step_data.stdout.split(MAGIC_SEPERATOR)
|
|
result = api.step.active_result
|
|
logs = result.presentation.logs
|
|
logs['perf_json'] = sections[2].split('\n')
|
|
|
|
add_binary_size_output_property(result, api.path.basename(f), (
|
|
ast.literal_eval(sections[2])
|
|
.get('results', {})
|
|
.get(api.path.basename(f), {})
|
|
.get('default', {})
|
|
.get(TOTAL_SIZE_BYTES_KEY, {})))
|
|
|
|
|
|
# Get the size of skia in flutter and a few metrics from bloaty
|
|
def analyze_flutter_lib(api, checkout_root, out_dir, files):
|
|
(keystr, propstr) = keys_and_props(api)
|
|
bloaty_exe = api.path['start_dir'].join('bloaty', 'bloaty')
|
|
|
|
for f in files:
|
|
|
|
skia_dir = checkout_root.join('skia')
|
|
with api.context(cwd=skia_dir):
|
|
stripped = api.vars.build_dir.join('libflutter_stripped.so')
|
|
script = skia_dir.join('infra', 'bots', 'buildstats',
|
|
'buildstats_flutter.py')
|
|
config = "skia_in_flutter"
|
|
lib_name = "libflutter.so"
|
|
step_data = api.run(api.python, 'Analyze flutter', script=script,
|
|
args=[stripped, out_dir, keystr, propstr, bloaty_exe,
|
|
f, config, TOTAL_SIZE_BYTES_KEY, lib_name,
|
|
MAGIC_SEPERATOR],
|
|
stdout=api.raw_io.output())
|
|
if step_data and step_data.stdout:
|
|
sections = step_data.stdout.split(MAGIC_SEPERATOR)
|
|
result = api.step.active_result
|
|
logs = result.presentation.logs
|
|
# Skip section 0 because it's everything before first print,
|
|
# which is probably the empty string.
|
|
logs['bloaty_file_symbol_short'] = sections[1].split('\n')
|
|
logs['bloaty_file_symbol_full'] = sections[2].split('\n')
|
|
logs['bloaty_symbol_file_short'] = sections[3].split('\n')
|
|
logs['bloaty_symbol_file_full'] = sections[4].split('\n')
|
|
logs['perf_json'] = sections[5].split('\n')
|
|
|
|
add_binary_size_output_property(result, lib_name, (
|
|
ast.literal_eval(sections[5])
|
|
.get('results', {})
|
|
.get(lib_name, {})
|
|
.get(config, {})
|
|
.get(TOTAL_SIZE_BYTES_KEY, {})))
|
|
|
|
|
|
# Get the size of skia in flutter and a few metrics from bloaty
|
|
def analyze_wasm_file(api, checkout_root, out_dir, files):
|
|
(keystr, propstr) = keys_and_props(api)
|
|
bloaty_exe = api.path['start_dir'].join('bloaty', 'bloaty')
|
|
|
|
for f in files:
|
|
|
|
skia_dir = checkout_root.join('skia')
|
|
with api.context(cwd=skia_dir):
|
|
script = skia_dir.join('infra', 'bots', 'buildstats',
|
|
'buildstats_wasm.py')
|
|
step_data = api.run(api.python, 'Analyze wasm', script=script,
|
|
args=[f, out_dir, keystr, propstr, bloaty_exe,
|
|
TOTAL_SIZE_BYTES_KEY, MAGIC_SEPERATOR],
|
|
stdout=api.raw_io.output())
|
|
if step_data and step_data.stdout:
|
|
sections = step_data.stdout.split(MAGIC_SEPERATOR)
|
|
result = api.step.active_result
|
|
logs = result.presentation.logs
|
|
# Skip section 0 because it's everything before first print,
|
|
# which is probably the empty string.
|
|
logs['bloaty_symbol_short'] = sections[1].split('\n')
|
|
logs['bloaty_symbol_full'] = sections[2].split('\n')
|
|
logs['perf_json'] = sections[3].split('\n')
|
|
add_binary_size_output_property(result, api.path.basename(f), (
|
|
ast.literal_eval(sections[3])
|
|
.get('results', {})
|
|
.get(api.path.basename(f), {})
|
|
.get('default', {})
|
|
.get(TOTAL_SIZE_BYTES_KEY, {})))
|
|
|
|
|
|
# make a zip file containing an HTML treemap of the files
|
|
def make_treemap(api, checkout_root, out_dir, files):
|
|
for f in files:
|
|
env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'}
|
|
with api.env(env):
|
|
skia_dir = checkout_root.join('skia')
|
|
with api.context(cwd=skia_dir):
|
|
script = skia_dir.join('infra', 'bots', 'buildstats',
|
|
'make_treemap.py')
|
|
api.run(api.python, 'Make code size treemap %s' % f,
|
|
script=script,
|
|
args=[f, out_dir],
|
|
stdout=api.raw_io.output())
|
|
|
|
|
|
def GenTests(api):
|
|
builder = 'BuildStats-Debian10-EMCC-wasm-Release-PathKit'
|
|
yield (
|
|
api.test('normal_bot') +
|
|
api.properties(buildername=builder,
|
|
repository='https://skia.googlesource.com/skia.git',
|
|
revision='abc123',
|
|
swarm_out_dir='[SWARM_OUT_DIR]',
|
|
path_config='kitchen') +
|
|
api.step_data('get swarming bot id',
|
|
stdout=api.raw_io.output('skia-bot-123')) +
|
|
api.step_data('get swarming task id',
|
|
stdout=api.raw_io.output('123456abc')) +
|
|
api.step_data('Analyze [START_DIR]/build/pathkit.js.mem',
|
|
stdout=api.raw_io.output(sample_web)) +
|
|
api.step_data('Analyze [START_DIR]/build/libskia.so',
|
|
stdout=api.raw_io.output(sample_cpp)) +
|
|
api.step_data('Analyze wasm',
|
|
stdout=api.raw_io.output(sample_wasm)) +
|
|
api.step_data('Analyze flutter',
|
|
stdout=api.raw_io.output(sample_flutter))
|
|
)
|
|
|
|
yield (
|
|
api.test('trybot') +
|
|
api.properties(buildername=builder,
|
|
repository='https://skia.googlesource.com/skia.git',
|
|
revision='abc123',
|
|
swarm_out_dir='[SWARM_OUT_DIR]',
|
|
patch_repo='https://skia.googlesource.com/skia.git',
|
|
path_config='kitchen') +
|
|
api.step_data('get swarming bot id',
|
|
stdout=api.raw_io.output('skia-bot-123')) +
|
|
api.step_data('get swarming task id',
|
|
stdout=api.raw_io.output('123456abc')) +
|
|
api.properties(patch_storage='gerrit') +
|
|
api.properties.tryserver(
|
|
buildername=builder,
|
|
gerrit_project='skia',
|
|
gerrit_url='https://skia-review.googlesource.com/',
|
|
) +
|
|
api.step_data('Analyze [START_DIR]/build/pathkit.js.mem',
|
|
stdout=api.raw_io.output(sample_web)) +
|
|
api.step_data('Analyze [START_DIR]/build/libskia.so',
|
|
stdout=api.raw_io.output(sample_cpp)) +
|
|
api.step_data('Analyze wasm',
|
|
stdout=api.raw_io.output(sample_wasm)) +
|
|
api.step_data('Analyze flutter',
|
|
stdout=api.raw_io.output(sample_flutter))
|
|
)
|
|
|
|
sample_web = """
|
|
Report A
|
|
Total size: 50 bytes
|
|
#$%^&*
|
|
{
|
|
"some": "json",
|
|
"results": {
|
|
"pathkit.js.mem": {
|
|
"default": {
|
|
"total_size_bytes": 7391117,
|
|
"gzip_size_bytes": 2884841
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
sample_cpp = """
|
|
#$%^&*
|
|
Report A
|
|
Total size: 50 bytes
|
|
#$%^&*
|
|
{
|
|
"some": "json",
|
|
"results": {
|
|
"libskia.so": {
|
|
"default": {
|
|
"total_size_bytes": 7391117,
|
|
"gzip_size_bytes": 2884841
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
sample_wasm = """
|
|
#$%^&*
|
|
Report A
|
|
Total size: 50 bytes
|
|
#$%^&*
|
|
Report B
|
|
Total size: 60 bytes
|
|
#$%^&*
|
|
{
|
|
"some": "json",
|
|
"results": {
|
|
"pathkit.wasm": {
|
|
"default": {
|
|
"total_size_bytes": 7391117,
|
|
"gzip_size_bytes": 2884841
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
sample_flutter = """
|
|
#$%^&*
|
|
Report A
|
|
Total size: 50 bytes
|
|
#$%^&*
|
|
Report B
|
|
Total size: 60 bytes
|
|
#$%^&*
|
|
Report C
|
|
Total size: 70 bytes
|
|
#$%^&*
|
|
Report D
|
|
Total size: 80 bytes
|
|
#$%^&*
|
|
{
|
|
"some": "json",
|
|
"results": {
|
|
"libflutter.so": {
|
|
"skia_in_flutter": {
|
|
"total_size_bytes": 1256676
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|