• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5# Recipe which analyzes a compiled binary for information (e.g. file size)
6
7DEPS = [
8  'checkout',
9  'env',
10  'recipe_engine/context',
11  'recipe_engine/file',
12  'recipe_engine/path',
13  'recipe_engine/properties',
14  'recipe_engine/python',
15  'recipe_engine/raw_io',
16  'recipe_engine/step',
17  'run',
18  'vars',
19]
20
21def RunSteps(api):
22  api.vars.setup()
23
24  checkout_root = api.checkout.default_checkout_root
25  api.checkout.bot_update(checkout_root=checkout_root)
26
27  out_dir = api.vars.swarming_out_dir
28  # Any binaries to scan should be here.
29  bin_dir = api.vars.build_dir
30
31  api.file.ensure_directory('mkdirs out_dir', out_dir, mode=0777)
32
33  analyzed = 0
34  with api.context(cwd=bin_dir):
35    files = api.file.glob_paths(
36        'find WASM binaries',
37        bin_dir,
38        '*.wasm',
39        test_data=['pathkit.wasm'])
40    analyzed += len(files)
41    if files:
42      analyze_wasm_file(api, checkout_root, out_dir, files)
43
44    files = api.file.glob_paths(
45        'find JS files',
46        bin_dir,
47        '*.js',
48        test_data=['pathkit.js'])
49    analyzed += len(files)
50    if files:
51      analyze_web_file(api, checkout_root, out_dir, files)
52
53    files = api.file.glob_paths(
54        'find JS mem files',
55        bin_dir,
56        '*.js.mem',
57        test_data=['pathkit.js.mem'])
58    analyzed += len(files)
59    if files:
60      analyze_web_file(api, checkout_root, out_dir, files)
61
62    files = api.file.glob_paths(
63        'find flutter library',
64        bin_dir,
65        'libflutter.so',
66        test_data=['libflutter.so'])
67    analyzed += len(files)
68    if files:
69      analyze_flutter_lib(api, checkout_root, out_dir, files)
70
71    files = api.file.glob_paths(
72        'find skia library',
73        bin_dir,
74        'libskia.so',
75        test_data=['libskia.so'])
76    analyzed += len(files)
77    if files:
78      analyze_cpp_lib(api, checkout_root, out_dir, files)
79
80    files = api.file.glob_paths(
81        'find skottie_tool',
82        bin_dir,
83        'skottie_tool',
84        test_data=['skottie_tool'])
85    analyzed += len(files)
86    if files:
87      make_treemap(api, checkout_root, out_dir, files)
88
89  if not analyzed: # pragma: nocover
90    raise Exception('No files were analyzed!')
91
92
93def keys_and_props(api):
94  keys = []
95  keys_blacklist = ['role']
96  for k in sorted(api.vars.builder_cfg.keys()):
97      if not k in keys_blacklist:
98        keys.extend([k, api.vars.builder_cfg[k]])
99  keystr = ' '.join(keys)
100
101  props = [
102    'gitHash', api.properties['revision'],
103    'swarming_bot_id', api.vars.swarming_bot_id,
104    'swarming_task_id', api.vars.swarming_task_id,
105  ]
106
107  if api.vars.is_trybot:
108    props.extend([
109      'issue',    str(api.vars.issue),
110      'patchset', str(api.vars.patchset),
111      'patch_storage', api.vars.patch_storage,
112    ])
113  propstr = ' '.join(props)
114  return (keystr, propstr)
115
116
117# Get the raw and gzipped size of the given file
118def analyze_web_file(api, checkout_root, out_dir, files):
119  (keystr, propstr) = keys_and_props(api)
120
121  for f in files:
122    skia_dir = checkout_root.join('skia')
123    with api.context(cwd=skia_dir):
124      script = skia_dir.join('infra', 'bots', 'buildstats',
125                             'buildstats_web.py')
126      api.run(api.python, 'Analyze %s' % f, script=script,
127          args=[f, out_dir, keystr, propstr])
128
129
130# Get the raw size and a few metrics from bloaty
131def analyze_cpp_lib(api, checkout_root, out_dir, files):
132  (keystr, propstr) = keys_and_props(api)
133  bloaty_exe = api.path['start_dir'].join('bloaty', 'bloaty')
134
135  for f in files:
136    skia_dir = checkout_root.join('skia')
137    with api.context(cwd=skia_dir):
138      script = skia_dir.join('infra', 'bots', 'buildstats',
139                             'buildstats_cpp.py')
140      api.run(api.python, 'Analyze %s' % f, script=script,
141          args=[f, out_dir, keystr, propstr, bloaty_exe])
142
143
144# Get the size of skia in flutter and a few metrics from bloaty
145def analyze_flutter_lib(api, checkout_root, out_dir, files):
146  (keystr, propstr) = keys_and_props(api)
147  bloaty_exe = api.path['start_dir'].join('bloaty', 'bloaty')
148
149  for f in files:
150
151    skia_dir = checkout_root.join('skia')
152    with api.context(cwd=skia_dir):
153      stripped = api.vars.build_dir.join('libflutter_stripped.so')
154      script = skia_dir.join('infra', 'bots', 'buildstats',
155                             'buildstats_flutter.py')
156      step_data = api.run(api.python, 'Analyze flutter', script=script,
157                         args=[stripped, out_dir, keystr, propstr, bloaty_exe,
158                               f],
159                         stdout=api.raw_io.output())
160      if step_data and step_data.stdout:
161        magic_seperator = '#$%^&*'
162        sections = step_data.stdout.split(magic_seperator)
163        result = api.step.active_result
164        logs = result.presentation.logs
165        # Skip section 0 because it's everything before first print,
166        # which is probably the empty string.
167        logs['bloaty_file_symbol_short'] = sections[1].split('\n')
168        logs['bloaty_file_symbol_full']  = sections[2].split('\n')
169        logs['bloaty_symbol_file_short'] = sections[3].split('\n')
170        logs['bloaty_symbol_file_full']  = sections[4].split('\n')
171        logs['perf_json'] = sections[5].split('\n')
172
173
174# Get the size of skia in flutter and a few metrics from bloaty
175def analyze_wasm_file(api, checkout_root, out_dir, files):
176  (keystr, propstr) = keys_and_props(api)
177  bloaty_exe = api.path['start_dir'].join('bloaty', 'bloaty')
178
179  for f in files:
180
181    skia_dir = checkout_root.join('skia')
182    with api.context(cwd=skia_dir):
183      script = skia_dir.join('infra', 'bots', 'buildstats',
184                             'buildstats_wasm.py')
185      step_data = api.run(api.python, 'Analyze wasm', script=script,
186                          args=[f, out_dir, keystr, propstr, bloaty_exe],
187                          stdout=api.raw_io.output())
188      if step_data and step_data.stdout:
189        magic_seperator = '#$%^&*'
190        sections = step_data.stdout.split(magic_seperator)
191        result = api.step.active_result
192        logs = result.presentation.logs
193        # Skip section 0 because it's everything before first print,
194        # which is probably the empty string.
195        logs['bloaty_symbol_short'] = sections[1].split('\n')
196        logs['bloaty_symbol_full']  = sections[2].split('\n')
197        logs['perf_json']           = sections[3].split('\n')
198
199
200# make a zip file containing an HTML treemap of the files
201def make_treemap(api, checkout_root, out_dir, files):
202  for f in files:
203    env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'}
204    with api.env(env):
205      skia_dir = checkout_root.join('skia')
206      with api.context(cwd=skia_dir):
207        script = skia_dir.join('infra', 'bots', 'buildstats',
208                               'make_treemap.py')
209        api.run(api.python, 'Make code size treemap',
210                             script=script,
211                             args=[f, out_dir],
212                             stdout=api.raw_io.output())
213
214
215def GenTests(api):
216  builder = 'BuildStats-Debian9-EMCC-wasm-Release-PathKit'
217  yield (
218    api.test('normal_bot') +
219    api.properties(buildername=builder,
220                   repository='https://skia.googlesource.com/skia.git',
221                   revision='abc123',
222                   swarm_out_dir='[SWARM_OUT_DIR]',
223                   path_config='kitchen') +
224    api.step_data('get swarming bot id',
225        stdout=api.raw_io.output('skia-bot-123')) +
226    api.step_data('get swarming task id',
227        stdout=api.raw_io.output('123456abc')) +
228    api.step_data('Analyze wasm',
229        stdout=api.raw_io.output(sample_wasm)) +
230    api.step_data('Analyze flutter',
231          stdout=api.raw_io.output(sample_flutter))
232  )
233
234  yield (
235    api.test('trybot') +
236    api.properties(buildername=builder,
237                   repository='https://skia.googlesource.com/skia.git',
238                   revision='abc123',
239                   swarm_out_dir='[SWARM_OUT_DIR]',
240                   patch_repo='https://skia.googlesource.com/skia.git',
241                   path_config='kitchen') +
242    api.step_data('get swarming bot id',
243        stdout=api.raw_io.output('skia-bot-123')) +
244    api.step_data('get swarming task id',
245        stdout=api.raw_io.output('123456abc')) +
246    api.properties(patch_storage='gerrit') +
247    api.properties.tryserver(
248        buildername=builder,
249        gerrit_project='skia',
250        gerrit_url='https://skia-review.googlesource.com/',
251      ) +
252    api.step_data('Analyze wasm',
253        stdout=api.raw_io.output(sample_wasm)) +
254    api.step_data('Analyze flutter',
255          stdout=api.raw_io.output(sample_flutter))
256  )
257
258sample_wasm = """
259#$%^&*
260Report A
261    Total size: 50 bytes
262#$%^&*
263Report B
264    Total size: 60 bytes
265#$%^&*
266{
267  "some": "json"
268}
269"""
270
271sample_flutter = """
272#$%^&*
273Report A
274    Total size: 50 bytes
275#$%^&*
276Report B
277    Total size: 60 bytes
278#$%^&*
279Report C
280    Total size: 70 bytes
281#$%^&*
282Report D
283    Total size: 80 bytes
284#$%^&*
285{
286  "some": "json"
287}
288"""
289