pw_bloat: Enable running size reports using memory regions
This adds a function to the bloat module which runs Bloaty using a
temporary bloaty config generated from memory region symbols in an
ELF file, avoiding the need for an external config file.
This function is not yet used; a future change will enable it within
bloat tooling.
Change-Id: Id10a32484fedef10fa6da288ca48cbec6bdbe804
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/108912
Reviewed-by: Brandon Vu <brandonvu@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Alexei Frolov <frolv@google.com>
diff --git a/pw_bloat/py/pw_bloat/bloat.py b/pw_bloat/py/pw_bloat/bloat.py
index 0985340..802ad2d 100755
--- a/pw_bloat/py/pw_bloat/bloat.py
+++ b/pw_bloat/py/pw_bloat/bloat.py
@@ -19,12 +19,15 @@
import json
import logging
import os
+from pathlib import Path
import subprocess
import sys
+import tempfile
from typing import Iterable, Optional
import pw_cli.log
+from pw_bloat.bloaty_config import generate_bloaty_config
from pw_bloat.label import from_bloaty_tsv
from pw_bloat.label_output import (BloatTableOutput, LineCharset, RstOutput,
AsciiCharset)
@@ -95,6 +98,48 @@
return subprocess.check_output(cmd)
+class NoMemoryRegions(Exception):
+ """Exception raised if an ELF does not define any memory region symbols."""
+
+
+def memory_regions_size_report(
+ elf: Path,
+ additional_data_sources: Iterable[str] = (),
+ extra_args: Iterable[str] = (),
+) -> str:
+ """Runs a size report on an ELF file using pw_bloat memory region symbols.
+
+ Arguments:
+ elf: The ELF binary on which to run.
+ additional_data_sources: Optional hierarchical data sources to display
+ following the root memory regions.
+ extra_args: Additional command line arguments forwarded to bloaty.
+
+ Returns:
+ The bloaty TSV output detailing the size report.
+
+ Raises:
+ NoMemoryRegions: The ELF does not define memory region symbols.
+ """
+ with tempfile.NamedTemporaryFile() as bloaty_config:
+ with open(elf.resolve(), "rb") as infile, open(bloaty_config.name,
+ "w") as outfile:
+ result = generate_bloaty_config(infile,
+ enable_memoryregions=True,
+ enable_utilization=False,
+ out_file=outfile)
+
+ if not result.has_memoryregions:
+ raise NoMemoryRegions(elf.name)
+
+ return run_bloaty(
+ str(elf.resolve()),
+ bloaty_config.name,
+ data_sources=('memoryregions', *additional_data_sources),
+ extra_args=extra_args,
+ ).decode('utf-8')
+
+
def write_file(filename: str, contents: str, out_dir_file: str) -> None:
path = os.path.join(out_dir_file, filename)
with open(path, 'w') as output_file:
@@ -156,12 +201,11 @@
gn_arg_dict['out_dir'], data_sources,
extra_args)
- default_data_sources = ['segment_names', 'symbols']
+ default_data_sources = ['symbols']
diff_report = ''
rst_diff_report = ''
for curr_diff_binary in gn_arg_dict['binaries']:
-
curr_extra_args = extra_args.copy()
data_sources = default_data_sources
@@ -173,7 +217,7 @@
data_sources = curr_diff_binary['data_sources']
try:
- single_output_base = run_bloaty(curr_diff_binary["base"],
+ single_output_base = run_bloaty(curr_diff_binary['base'],
curr_diff_binary['bloaty_config'],
data_sources=data_sources,
extra_args=curr_extra_args)
@@ -185,7 +229,7 @@
try:
single_output_target = run_bloaty(
- curr_diff_binary["target"],
+ curr_diff_binary['target'],
curr_diff_binary['bloaty_config'],
data_sources=data_sources,
extra_args=curr_extra_args)
diff --git a/pw_bloat/py/pw_bloat/bloaty_config.py b/pw_bloat/py/pw_bloat/bloaty_config.py
index 68e924a..40c5d92 100644
--- a/pw_bloat/py/pw_bloat/bloaty_config.py
+++ b/pw_bloat/py/pw_bloat/bloaty_config.py
@@ -17,7 +17,7 @@
import logging
import re
import sys
-from typing import BinaryIO, Dict, List, Optional, TextIO
+from typing import BinaryIO, Dict, List, NamedTuple, Optional, TextIO
import pw_cli.argument_types
from elftools.elf import elffile # type: ignore
@@ -318,8 +318,26 @@
return '\n'.join(output) + '\n'
-def generate_bloaty_config(elf_file: BinaryIO, enable_memoryregions: bool,
- enable_utilization: bool, out_file: TextIO) -> None:
+class BloatyConfigResult(NamedTuple):
+ has_memoryregions: bool
+ has_utilization: bool
+
+
+def generate_bloaty_config(
+ elf_file: BinaryIO,
+ enable_memoryregions: bool,
+ enable_utilization: bool,
+ out_file: TextIO,
+) -> BloatyConfigResult:
+ """Generates a Bloaty config file from symbols within an ELF.
+
+ Returns:
+ Tuple indicating whether a memoryregions data source, a utilization data
+ source, or both were written.
+ """
+
+ result = [False, False]
+
if enable_memoryregions:
# Enable the "memoryregions" data_source if the user provided the
# required pw_bloat specific symbols in their linker script.
@@ -330,10 +348,14 @@
_LOG.info('memoryregions data_source is provided')
out_file.write(
generate_memoryregions_data_source(segment_to_memory_region))
+ result[0] = True
if enable_utilization:
_LOG.info('utilization data_source is provided')
out_file.write(generate_utilization_data_source())
+ result[1] = True
+
+ return BloatyConfigResult(*result)
def main() -> int: