mirror of
https://fuchsia.googlesource.com/third_party/pigweed.googlesource.com/pigweed/pigweed
synced 2024-09-21 14:16:26 +00:00
6f561b99b6
This change updates the bloat script to exit if a Bloaty invocation fails. Change-Id: Ica1205f8eb9ecf2a8afef56ba6f3e10140e02da2
204 lines
6.5 KiB
Python
Executable File
204 lines
6.5 KiB
Python
Executable File
# Copyright 2019 The Pigweed 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
|
|
#
|
|
# https://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.
|
|
"""
|
|
bloat is a script which generates a size report card for binary files.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
from typing import List, Iterable, Optional
|
|
|
|
from binary_diff import BinaryDiff
|
|
import bloat_output
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
"""Parses the script's arguments."""
|
|
def delimited_list(delimiter: str, items: Optional[int] = None):
|
|
def _parser(arg: str):
|
|
args = arg.split(delimiter)
|
|
|
|
if items and len(args) != items:
|
|
raise argparse.ArgumentTypeError(
|
|
'Argument must be a '
|
|
f'{delimiter}-delimited list with {items} items: "{arg}"')
|
|
|
|
return args
|
|
|
|
return _parser
|
|
|
|
parser = argparse.ArgumentParser(
|
|
'Generate a size report card for binaries')
|
|
parser.add_argument('--bloaty-config',
|
|
type=str,
|
|
required=True,
|
|
help='Data source configuration for Bloaty')
|
|
parser.add_argument('--full',
|
|
action='store_true',
|
|
help='Display full bloat breakdown by symbol')
|
|
parser.add_argument('--labels',
|
|
type=delimited_list(';'),
|
|
default='',
|
|
help='Labels for output binaries')
|
|
parser.add_argument('--out-dir',
|
|
type=str,
|
|
required=True,
|
|
help='Directory in which to write output files')
|
|
parser.add_argument('--target',
|
|
type=str,
|
|
required=True,
|
|
help='Build target name')
|
|
parser.add_argument('--title',
|
|
type=str,
|
|
default='pw_bloat',
|
|
help='Report title')
|
|
parser.add_argument('--source-filter',
|
|
type=str,
|
|
help='Bloaty data source filter')
|
|
parser.add_argument('diff_targets',
|
|
type=delimited_list(';', 2),
|
|
nargs='+',
|
|
metavar='DIFF_TARGET',
|
|
help='Binary;base pairs to process')
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
def run_bloaty(
|
|
filename: str,
|
|
config: str,
|
|
base_file: Optional[str] = None,
|
|
data_sources: Iterable[str] = (),
|
|
extra_args: Iterable[str] = ()
|
|
) -> bytes:
|
|
"""Executes a Bloaty size report on some binary file(s).
|
|
|
|
Args:
|
|
filename: Path to the binary.
|
|
config: Path to Bloaty config file.
|
|
base_file: Path to a base binary. If provided, a size diff is performed.
|
|
data_sources: List of Bloaty data sources for the report.
|
|
extra_args: Additional command-line arguments to pass to Bloaty.
|
|
|
|
Returns:
|
|
Binary output of the Bloaty invocation.
|
|
|
|
Raises:
|
|
subprocess.CalledProcessError: The Bloaty invocation failed.
|
|
"""
|
|
|
|
# TODO(frolv): Point the default bloaty path to a prebuilt in Pigweed.
|
|
default_bloaty = 'bloaty'
|
|
bloaty_path = os.getenv('BLOATY_PATH', default_bloaty)
|
|
|
|
# yapf: disable
|
|
cmd = [
|
|
bloaty_path,
|
|
'-c', config,
|
|
'-d', ','.join(data_sources),
|
|
'--domain', 'vm',
|
|
filename,
|
|
*extra_args
|
|
]
|
|
# yapf: enable
|
|
|
|
if base_file is not None:
|
|
cmd.extend(['--', base_file])
|
|
|
|
return subprocess.check_output(cmd)
|
|
|
|
|
|
def main() -> int:
|
|
"""Program entry point."""
|
|
|
|
args = parse_args()
|
|
|
|
base_binaries: List[str] = []
|
|
diff_binaries: List[str] = []
|
|
|
|
try:
|
|
for binary, base in args.diff_targets:
|
|
diff_binaries.append(binary)
|
|
base_binaries.append(base)
|
|
except RuntimeError as err:
|
|
print(f'{sys.argv[0]}: {err}', file=sys.stderr)
|
|
return 1
|
|
|
|
data_sources = ['segment_names']
|
|
if args.full:
|
|
data_sources.append('fullsymbols')
|
|
|
|
# TODO(frolv): CSV output is disabled for full reports as the default Bloaty
|
|
# breakdown is printed. This script should be modified to print a custom
|
|
# symbol breakdown in full reports.
|
|
extra_args = [] if args.full else ['--csv']
|
|
if args.source_filter:
|
|
extra_args.extend(['--source-filter', args.source_filter])
|
|
|
|
diffs: List[BinaryDiff] = []
|
|
report = []
|
|
|
|
for i, binary in enumerate(diff_binaries):
|
|
binary_name = args.labels[i] if i < len(
|
|
args.labels) else os.path.basename(binary)
|
|
try:
|
|
output = run_bloaty(binary, args.bloaty_config, base_binaries[i],
|
|
data_sources, extra_args)
|
|
if not output:
|
|
continue
|
|
|
|
# TODO(frolv): Remove when custom output for full mode is added.
|
|
if args.full:
|
|
report.append(binary_name)
|
|
report.append('-' * len(binary_name))
|
|
report.append(output.decode())
|
|
continue
|
|
|
|
# Ignore the first row as it displays column names.
|
|
bloaty_csv = output.decode().splitlines()[1:]
|
|
diffs.append(BinaryDiff.from_csv(binary_name, bloaty_csv))
|
|
except subprocess.CalledProcessError:
|
|
print(f'{sys.argv[0]}: failed to run diff on {binary}',
|
|
file=sys.stderr)
|
|
return 1
|
|
|
|
def write_file(filename: str, contents: str) -> None:
|
|
path = os.path.join(args.out_dir, filename)
|
|
with open(path, 'w') as output_file:
|
|
output_file.write(contents)
|
|
print(f'Output written to {path}')
|
|
|
|
# TODO(frolv): Remove when custom output for full mode is added.
|
|
if not args.full:
|
|
out = bloat_output.TableOutput(args.title,
|
|
diffs,
|
|
charset=bloat_output.LineCharset)
|
|
report.append(out.diff())
|
|
|
|
rst = bloat_output.RstOutput(diffs)
|
|
write_file(f'{args.target}.rst', rst.diff())
|
|
|
|
complete_output = '\n'.join(report) + '\n'
|
|
write_file(f'{args.target}.txt', complete_output)
|
|
print(complete_output)
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|