| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- #!/usr/bin/env python3
- #
- # Copyright (c) 2021 Project CHIP Authors
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- #
- """
- This is similar to scripts/tools/memory/report_summary.py, but generates
- a specific output format with a simplified interface for use in github
- workflows.
- Usage: gh_sizes.py ‹platform› ‹config› ‹target› ‹binary› [‹output›] [‹option›…]
- ‹platform› - Platform name, corresponding to a config file
- in scripts/tools/memory/platform/
- ‹config› - Configuration identification string.
- ‹target› - Build artifact identification string.
- ‹binary› - Binary build artifact.
- ‹output› - Output name or directory.
- ‹option›… - Other options as for report_summary.
- This script also expects certain environment variables, which can be set in a
- github workflow as follows:
- - name: Set up environment for size reports
- if: ${{ !env.ACT }}
- env:
- GH_CONTEXT: ${{ toJson(github) }}
- run: gh_sizes_environment.py "${GH_CONTEXT}"
- Default output file is {platform}-{configname}-{buildname}-sizes.json in the
- binary's directory. This file has the form:
- {
- "platform": "‹platform›",
- "config": "‹config›",
- "target": "‹target›",
- "time": 1317645296,
- "input": "‹binary›",
- "event": "pull_request",
- "hash": "496620796f752063616e20726561642074686973",
- "parent": "20796f752061726520746f6f20636c6f73652e0a",
- "pr": 12345,
- "by": "section",
- "ref": "refs/pull/12345/merge"
- "frames": {
- "section": [
- {"section": ".bss", "size": 260496},
- {"section": ".data", "size": 1648},
- {"section": ".text", "size": 740236}
- ],
- "wr": [
- {"wr": 0, "size": 262144},
- {"wr": 1, "size": 74023}
- ]
- }
- }
- """
- import datetime
- import logging
- import os
- import pathlib
- import sys
- import memdf.collect
- import memdf.report
- import memdf.select
- import memdf.util
- import numpy as np # type: ignore
- from memdf import Config, ConfigDescription, DFs, SectionDF, SegmentDF
- PLATFORM_CONFIG_DIR = pathlib.Path('scripts/tools/memory/platform')
- CONFIG: ConfigDescription = {
- 'event': {
- 'help': 'Github workflow event name',
- 'metavar': 'NAME',
- 'default': os.environ.get('GITHUB_EVENT_NAME'),
- },
- 'pr': {
- 'help': 'Github PR number',
- 'metavar': 'NUMBER',
- 'default': int(os.environ.get('GH_EVENT_PR', '0')),
- },
- 'hash': {
- 'help': 'Current commit hash',
- 'metavar': 'HASH',
- 'default': os.environ.get('GH_EVENT_HASH'),
- },
- 'parent': {
- 'help': 'Parent commit hash',
- 'metavar': 'HASH',
- 'default': os.environ.get('GH_EVENT_PARENT'),
- },
- 'ref': {
- 'help': 'Target ref',
- 'metavar': 'REF',
- 'default': os.environ.get('GH_EVENT_REF'),
- },
- 'timestamp': {
- 'help': 'Build timestamp',
- 'metavar': 'TIME',
- 'default': int(float(
- os.environ.get('GH_EVENT_TIMESTAMP')
- or datetime.datetime.now().timestamp())),
- },
- }
- def main(argv):
- status = 0
- try:
- _, platform, config_name, target_name, binary, *args = argv
- except ValueError:
- program = pathlib.Path(argv[0])
- logging.error(
- """
- Usage: %s platform config target binary [output] [options]
- This is intended for use in github workflows.
- For other purposes, a general program for the same operations is
- %s/report_summary.py
- """, program.name, program.parent)
- return 1
- try:
- config_file = pathlib.Path(platform)
- if config_file.is_file():
- platform = config_file.stem
- else:
- config_file = (PLATFORM_CONFIG_DIR / platform).with_suffix('.cfg')
- output_base = f'{platform}-{config_name}-{target_name}-sizes.json'
- if args and not args[0].startswith('-'):
- out, *args = args
- output = pathlib.Path(out)
- if out.endswith('/') and not output.exists():
- output.mkdir(parents=True)
- if output.is_dir():
- output = output / output_base
- else:
- output = pathlib.Path(binary).parent / output_base
- config = Config().init({
- **memdf.util.config.CONFIG,
- **memdf.collect.CONFIG,
- **memdf.select.CONFIG,
- **memdf.report.OUTPUT_CONFIG,
- **CONFIG,
- })
- config.put('output.file', output)
- config.put('output.format', 'json_records')
- if config_file.is_file():
- config.read_config_file(config_file)
- else:
- logging.warning('Missing config file: %s', config_file)
- config.parse([argv[0]] + args)
- config.put('output.metadata.platform', platform)
- config.put('output.metadata.config', config_name)
- config.put('output.metadata.target', target_name)
- config.put('output.metadata.time', config['timestamp'])
- config.put('output.metadata.input', binary)
- config.put('output.metadata.by', 'section')
- for key in ['event', 'hash', 'parent', 'pr', 'ref']:
- if value := config[key]:
- config.putl(['output', 'metadata', key], value)
- collected: DFs = memdf.collect.collect_files(config, [binary])
- # Aggregate loaded segments, by writable (flash) or not (RAM).
- segments = collected[SegmentDF.name]
- segments['segment'] = segments.index
- segments['wr'] = ((segments['flags'] & 2) != 0).convert_dtypes(
- convert_boolean=False, convert_integer=True)
- segment_summary = segments[segments['type'] == 'PT_LOAD'][[
- 'wr', 'size'
- ]].groupby('wr').aggregate(np.sum).reset_index().astype(
- {'size': np.int64})
- segment_summary.attrs['name'] = "wr"
- sections = collected[SectionDF.name]
- sections = sections.join(on='segment',
- how='left',
- other=segments,
- rsuffix='-segment')
- section_summary = sections[['section', 'size',
- 'wr']].sort_values(by='section')
- section_summary.attrs['name'] = "section"
- summaries = {
- 'section': section_summary,
- 'memory': segment_summary,
- }
- # Write configured (json) report to the output file.
- memdf.report.write_dfs(config, summaries)
- # Write text report to stdout.
- memdf.report.write_dfs(config,
- summaries,
- sys.stdout,
- 'simple',
- floatfmt='.0f')
- except Exception as exception:
- raise exception
- return status
- if __name__ == '__main__':
- sys.exit(main(sys.argv))
|