A tool for analyzing memory footprint in embedded firmware. MemBrowse extracts detailed memory information from ELF files and linker scripts, providing symbol-level analysis with source file mapping for multiple architectures. Use it standalone for local analysis or integrate with MemBrowse for historical analysis and CI integration.
- Architecture Agnostic: Works with architectures that produce ELFs with DWARF debug format
- Source File Mapping: Symbols are mapped to their definition source files
- Memory Region Extraction: Memory region capacity and layout are extracted from GNU LD linker scripts
- Cloud Integration: Upload reports to MemBrowse for historical tracking, diffs, monitoring and CI gating
MemBrowse provides GitHub Actions for CI integration.
Create a Github action for PR analysis that will call membrowse/membrowse-action:
name: Memory Analysis
on: [push, pull_request]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build firmware
run: make all # your build commands
- name: Analyze memory
id: analyze
uses: membrowse/membrowse-action@v1
with:
elf: build/firmware.elf # your elf
ld: "src/linker.ld" # your ld scripts
target_name: stm32f4 # the target name will be recognized by Membrowse
api_key: ${{ secrets.MEMBROWSE_API_KEY }}
- name: Post PR comment
if: github.event_name == 'pull_request'
uses: membrowse/membrowse-action/comment-action@v1
with:
json_files: ${{ steps.analyze.outputs.report_path }}
# Optional: use a custom Jinja2 template for the comment
# comment_template: .github/membrowse-comment.j2The comment action posts a memory report to the PR showing changes between the PR branch and the base branch. The report includes memory region utilization changes (e.g. FLASH, RAM), section-level deltas (e.g. .text, .bss, .data), and symbol-level changes — added, removed, modified, and moved symbols. If budget alerts are configured on MemBrowse, any exceeded budgets are highlighted in the comment.
You can customize the comment format by providing a Jinja2 template via the comment_template input. Your template receives a targets list (each with regions, sections, symbols, and alerts) and a top-level has_alerts boolean. See the default template for reference.
For getting historical build data from day one upload the last N commits by
Creating an Onboard Github action in your repo that will call membrowse/membrowse-action/onboard-action:
name: Onboard to MemBrowse
on: workflow_dispatch
jobs:
onboard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Historical analysis
uses: membrowse/membrowse-action/onboard-action@v1
with:
num_commits: 100
build_script: "make clean && make" # your build commands
elf: build/firmware.elf # your elf file
ld: "components.ld memory.ld" #your ld scripts
target_name: my-target # the target name will be recognized by Membrowse
api_key: ${{ secrets.MEMBROWSE_API_KEY }}If you use Claude Code, you can automatically set up MemBrowse integration using the membrowse-integrate skill.
First, add the MemBrowse plugin to Claude Code:
/plugin marketplace add membrowse@membrowse-action
Then run the skill in your project:
/membrowse-integrate
This will:
- Analyze your project's build system and targets
- Verify builds and linker scripts work locally
- Create
membrowse-targets.jsonconfiguration - Set up GitHub Actions workflows for PR analysis and onboarding
- Add a MemBrowse badge to your README
pip install membrowse# Clone and install in editable mode
git clone https://github.com/membrowse/membrowse-action.git
cd membrowse-action
pip install -e .The simplest way to analyze your firmware (local mode - no upload):
# Generate a human-readable report (default)
membrowse report \
build/firmware.elf \
"src/linker.ld src/memory.ld"
# Output JSON format instead
membrowse report \
build/firmware.elf \
"src/linker.ld src/memory.ld" \
--json
# Show all symbols (not just top 20)
membrowse report \
build/firmware.elf \
"src/linker.ld src/memory.ld" \
--all-symbols
# With verbose output to see progress messages
membrowse -v INFO report \
build/firmware.elf \
"src/linker.ld src/memory.ld"By default, this generates a human-readable report with memory regions, sections, and top symbols. Use --json to output structured JSON data instead. Use -v INFO or -v DEBUG before the subcommand to see progress messages (default is WARNING which only shows warnings and errors).
Example output:
ELF Metadata: build/firmware.elf | Arch: ELF32 | Machine: EM_ARM | Entry: 0x0802015d | Type: ET_EXEC
=======================================================================================================================================
Region Address Range Size Used Free Utilization
--------------------------------------------------------------------------------------------------------------------------------------------
FLASH 0x08000000-0x08100000 1,048,576 bytes 365,192 bytes 683,384 bytes [██████░░░░░░░░░░░░░░] 34.8%
└─ FLASH_START 0x08000000-0x08004000 16,384 bytes 14,708 bytes 1,676 bytes [█████████████████░░░] 89.8%
• .isr_vector 392 bytes
• .isr_extratext 14,316 bytes
└─ FLASH_FS 0x08004000-0x08020000 114,688 bytes 0 bytes 114,688 bytes [░░░░░░░░░░░░░░░░░░░░] 0.0%
└─ FLASH_TEXT 0x08020000-0x08100000 917,504 bytes 350,484 bytes 567,020 bytes [███████░░░░░░░░░░░░░] 38.2%
• .text 350,476 bytes
• .ARM 8 bytes
RAM 0x20000000-0x20020000 131,072 bytes 26,960 bytes 104,112 bytes [████░░░░░░░░░░░░░░░░] 20.6%
• .data 52 bytes
• .bss 8,476 bytes
• .heap 16,384 bytes
• .stack 2,048 bytes
Top 20 Largest Symbols
======================
Name Address Size Type Section Source
--------------------------------------------------------------------------------------------------------------------------------------------
usb_device 0x20000a30 5,444 bytes OBJECT .bss usb.c
mp_qstr_const_pool 0x08062b70 4,692 bytes OBJECT .text qstr.c
mp_execute_bytecode 0x080392f9 4,208 bytes FUNC .text vm.c
fresh_pybcdc_inf 0x0806ffaa 2,598 bytes OBJECT .text factoryreset.c
emit_inline_thumb_op 0x0802ac25 2,476 bytes FUNC .text emitinlinethumb.c
mp_qstr_const_hashes 0x08061b36 2,334 bytes OBJECT .text qstr.c
stm_module_globals_table 0x08073478 2,096 bytes OBJECT .text modstm.c
stm32_help_text 0x08072366 2,067 bytes OBJECT .text help.c
mp_lexer_to_next 0x080229ed 1,768 bytes FUNC .text lexer.c
f_mkfs 0x080020ed 1,564 bytes FUNC .isr_extratext ff.c
...
# Upload mode - uploads report to MemBrowse platform (https://membrowse.com)
membrowse report \
build/firmware.elf \
"src/linker.ld" \
--upload \
--target-name esp32 \
--api-key your-membrowse-api-key
# GitHub Actions mode - auto-detects Git metadata from CI environment
membrowse report \
build/firmware.elf \
"src/linker.ld" \
--upload \
--github \
--target-name esp32 \
--api-key your-membrowse-api-keyWhen uploading, MemBrowse will fail the build (exit code 1) if budget alerts are detected. Use --dont-fail-on-alerts to continue despite alerts.
Analyzes memory footprints across multiple commits and uploads them to MemBrowse:
# Analyze and upload the last 50 commits
membrowse onboard \
50 \
"make clean && make all" \
build/firmware.elf \
"STM32F746ZGTx_FLASH.ld" \
stm32f4 \
your-membrowse-api-keyMemBrowse is with toolchains that produce ELF files and uses GNU LD linker scripts. If you found that you're not getting optimal results please contact us: support@membrowse.com We are actively working on improving Membrowse.
See LICENSE file for details.
- Issues: https://github.com/membrowse/membrowse-action/issues
- Documentation: This README and inline code documentation
- MemBrowse Support: support@membrowse.com