1.. _module-pw_bloat: 2 3-------- 4pw_bloat 5-------- 6The bloat module provides tools and helpers around using 7`Bloaty McBloatface <https://github.com/google/bloaty>`_ including generating 8generate size report cards for output binaries through Pigweed's GN build 9system. 10 11Bloat report cards allow tracking the memory usage of a system over time as code 12changes are made and provide a breakdown of which parts of the code have the 13largest size impact. 14 15.. _bloat-howto: 16 17Defining size reports 18===================== 19Size reports are defined using the GN template ``pw_size_report``. The template 20requires at least two executable targets on which to perform a size diff. The 21base for the size diff can be specified either globally through the top-level 22``base`` argument, or individually per-binary within the ``binaries`` list. 23 24**Arguments** 25 26* ``title``: Title for the report card. 27* ``base``: Optional default base target for all listed binaries. 28* ``binaries``: List of binaries to size diff. Each binary specifies a target, 29 a label for the diff, and optionally a base target that overrides the default 30 base. 31* ``source_filter``: Optional regex to filter labels in the diff output. 32* ``full_report``: Boolean flag indicating whether to output a full report of 33 all symbols in the binary, or a summary of the segment size changes. Default 34 false. 35 36.. code:: 37 38 import("$dir_pw_bloat/bloat.gni") 39 40 executable("empty_base") { 41 sources = [ "empty_main.cc" ] 42 } 43 44 executable("hello_world_printf") { 45 sources = [ "hello_printf.cc" ] 46 } 47 48 executable("hello_world_iostream") { 49 sources = [ "hello_iostream.cc" ] 50 } 51 52 pw_size_report("my_size_report") { 53 title = "Hello world program using printf vs. iostream" 54 base = ":empty_base" 55 binaries = [ 56 { 57 target = ":hello_world_printf" 58 label = "Hello world using printf" 59 }, 60 { 61 target = ":hello_world_iostream" 62 label = "Hello world using iostream" 63 }, 64 ] 65 } 66 67Size reports are typically included in ReST documentation, as described in 68`Documentation integration`_. Size reports may also be printed in the build 69output if desired. To enable this in the GN build, set the 70``pw_bloat_SHOW_SIZE_REPORTS`` build arg to ``true``. 71 72Documentation integration 73========================= 74Bloat reports are easy to add to documentation files. All ``pw_size_report`` 75targets output a file containing a tabular report card. This file can be 76imported directly into a ReST documentation file using the ``include`` 77directive. 78 79For example, the ``simple_bloat_loop`` and ``simple_bloat_function`` size 80reports under ``//pw_bloat/examples`` are imported into this file as follows: 81 82.. code:: rst 83 84 Simple bloat loop example 85 ^^^^^^^^^^^^^^^^^^^^^^^^^ 86 .. include:: examples/simple_bloat_loop 87 88 Simple bloat function example 89 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 90 .. include:: examples/simple_bloat_function 91 92Resulting in this output: 93 94Simple bloat loop example 95^^^^^^^^^^^^^^^^^^^^^^^^^ 96.. include:: examples/simple_bloat_loop 97 98Simple bloat function example 99^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 100.. include:: examples/simple_bloat_function 101 102Additional Bloaty data sources 103============================== 104`Bloaty McBloatface <https://github.com/google/bloaty>`_ by itself cannot help 105answer some questions which embedded developers frequently face such as 106understanding how much space is left. To address this, Pigweed provides Python 107tooling (``pw_bloat.bloaty_config``) to generate bloaty configuration files 108based on the final ELF files through small tweaks in the linker scripts to 109expose extra information. 110 111See the sections below on how to enable the additional data sections through 112modifications in your linker script(s). 113 114As an example to generate the helper configuration which enables additional data 115sources for ``example.elf`` if you've updated your linker script(s) accordingly, 116simply run 117``python -m pw_bloaty.bloaty_config example.elf > example.bloaty``. The 118``example.bloaty`` can then be used with bloaty using the ``-c`` flag, for 119example 120``bloaty -c example.bloaty example.elf --domain vm -d memoryregions,utilization`` 121which may return something like: 122 123.. code-block:: 124 125 84.2% 1023Ki FLASH 126 94.2% 963Ki Free space 127 5.8% 59.6Ki Used space 128 15.8% 192Ki RAM 129 100.0% 192Ki Used space 130 0.0% 512 VECTOR_TABLE 131 96.9% 496 Free space 132 3.1% 16 Used space 133 0.0% 0 Not resident in memory 134 NAN% 0 Used space 135 136 137``utilization`` data source 138^^^^^^^^^^^^^^^^^^^^^^^^^^^ 139The most common question many embedded developers face when using ``bloaty`` is 140how much space you are using and how much space is left. To correctly answer 141this, section sizes must be used in order to correctly account for section 142alignment requirements. 143 144The generated ``utilization`` data source will work with any ELF file, where 145``Used Space`` is reported for the sum of virtual memory size of all sections. 146 147In order for ``Free Space`` to be reported, your linker scripts must include 148properly aligned sections which span the unused remaining space for the relevant 149memory region with the ``unused_space`` string anywhere in their name. This 150typically means creating a trailing section which is pinned to span to the end 151of the memory region. 152 153For example imagine this partial example GNU LD linker script: 154 155.. code-block:: 156 157 MEMORY 158 { 159 FLASH(rx) : \ 160 ORIGIN = PW_BOOT_FLASH_BEGIN, \ 161 LENGTH = PW_BOOT_FLASH_SIZE 162 RAM(rwx) : \ 163 ORIGIN = PW_BOOT_RAM_BEGIN, \ 164 LENGTH = PW_BOOT_RAM_SIZE 165 } 166 167 SECTIONS 168 { 169 /* Main executable code. */ 170 .code : ALIGN(8) 171 { 172 /* Application code. */ 173 *(.text) 174 *(.text*) 175 KEEP(*(.init)) 176 KEEP(*(.fini)) 177 178 . = ALIGN(8); 179 /* Constants.*/ 180 *(.rodata) 181 *(.rodata*) 182 } >FLASH 183 184 /* Explicitly initialized global and static data. (.data)*/ 185 .static_init_ram : ALIGN(8) 186 { 187 *(.data) 188 *(.data*) 189 . = ALIGN(8); 190 } >RAM AT> FLASH 191 192 /* Zero initialized global/static data. (.bss) */ 193 .zero_init_ram : ALIGN(8) 194 { 195 *(.bss) 196 *(.bss*) 197 *(COMMON) 198 . = ALIGN(8); 199 } >RAM 200 } 201 202Could be modified as follows enable ``Free Space`` reporting: 203 204.. code-block:: 205 206 MEMORY 207 { 208 FLASH(rx) : ORIGIN = PW_BOOT_FLASH_BEGIN, LENGTH = PW_BOOT_FLASH_SIZE 209 RAM(rwx) : ORIGIN = PW_BOOT_RAM_BEGIN, LENGTH = PW_BOOT_RAM_SIZE 210 } 211 212 SECTIONS 213 { 214 /* Main executable code. */ 215 .code : ALIGN(8) 216 { 217 /* Application code. */ 218 *(.text) 219 *(.text*) 220 KEEP(*(.init)) 221 KEEP(*(.fini)) 222 223 . = ALIGN(8); 224 /* Constants.*/ 225 *(.rodata) 226 *(.rodata*) 227 } >FLASH 228 229 /* Explicitly initialized global and static data. (.data)*/ 230 .static_init_ram : ALIGN(8) 231 { 232 *(.data) 233 *(.data*) 234 . = ALIGN(8); 235 } >RAM AT> FLASH 236 237 /* Zero initialized global/static data. (.bss). */ 238 .zero_init_ram : ALIGN(8) 239 { 240 *(.bss) 241 *(.bss*) 242 *(COMMON) 243 . = ALIGN(8); 244 } >RAM 245 246 /* 247 * Do not declare any output sections after this comment. This area is 248 * reserved only for declaring unused sections of memory. These sections are 249 * used by pw_bloat.bloaty_config to create the utilization data source for 250 * bloaty. 251 */ 252 .FLASH.unused_space (NOLOAD) : ALIGN(8) 253 { 254 . = ABSOLUTE(ORIGIN(FLASH) + LENGTH(FLASH)); 255 } >FLASH 256 257 .RAM.unused_space (NOLOAD) : ALIGN(8) 258 { 259 . = ABSOLUTE(ORIGIN(RAM) + LENGTH(RAM)); 260 } >RAM 261 } 262 263``memoryregions`` data source 264^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 265Understanding how symbols, sections, and other data sources can be attributed 266back to the memory regions defined in your linker script is another common 267problem area. Unfortunately the ELF format does not include the original memory 268regions, meaning ``bloaty`` can not do this today by itself. In addition, it's 269relatively common that there are multiple memory regions which alias to the same 270memory but through different buses which could make attribution difficult. 271 272Instead of taking the less portable and brittle approach to parse ``*.map`` 273files, ``pw_bloat.bloaty_config`` consumes symbols which are defined in the 274linker script with a special format to extract this information from the ELF 275file: ``pw_bloat_config_memory_region_NAME_{start,end}{_N,}``. 276 277These symbols are then used to determine how to map segments to these memory 278regions. Note that segments must be used in order to account for inter-section 279padding which are not attributed against any sections. 280 281As an example, if you have a single view in the single memory region named 282``FLASH``, then you should produce the following two symbols in your linker 283script: 284 285.. code-block:: 286 287 pw_bloat_config_memory_region_FLASH_start = ORIGIN(FLASH); 288 pw_bloat_config_memory_region_FLASH_end = ORIGIN(FLASH) + LENGTH(FLASH); 289 290As another example, if you have two aliased memory regions (``DCTM`` and 291``ITCM``) into the same effective memory named you'd like to call ``RAM``, then 292you should produce the following four symbols in your linker script: 293 294.. code-block:: 295 296 pw_bloat_config_memory_region_RAM_start_0 = ORIGIN(ITCM); 297 pw_bloat_config_memory_region_RAM_end_0 = ORIGIN(ITCM) + LENGTH(ITCM); 298 pw_bloat_config_memory_region_RAM_start_1 = ORIGIN(DTCM); 299 pw_bloat_config_memory_region_RAM_end_1 = ORIGIN(DTCM) + LENGTH(DTCM); 300