mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Update the linker script path in membrowse-targets.json and the cuncurrency condition, to prevent breaking the commit chain Signed-off-by: Michael Rogov Papernov <michael@membrowse.com>
252 lines
9.9 KiB
YAML
252 lines
9.9 KiB
YAML
name: MemBrowse Memory Report
|
|
|
|
on:
|
|
pull_request:
|
|
push:
|
|
branches:
|
|
- master
|
|
- "releases/*"
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# Per-PR group so superseded PR pushes cancel; per-SHA on push so master
|
|
# commits never share a group. A shared refs/heads/master group lets a burst
|
|
# of pushes evict each other while pending (GitHub keeps only one pending run
|
|
# per group), leaving a commit with no report and breaking the MemBrowse
|
|
# parent chain for every commit that bases on it.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
jobs:
|
|
# Detect whether any source files (i.e. non-doc/CODEOWNERS) changed.
|
|
# When only docs/CODEOWNERS change we skip the build and post an "identical"
|
|
# MemBrowse report instead.
|
|
changes-filter:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
source: ${{ steps.filter.outputs.source }}
|
|
steps:
|
|
# Shallow checkout; the base commit needed for the diff is fetched
|
|
# explicitly below instead of cloning the repo's full history.
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 2
|
|
# Detect whether any non-doc/CODEOWNERS source files changed.
|
|
# Implemented with plain git instead of a third-party paths-filter
|
|
# action, since Apache's GitHub Actions allowlist forbids actions that
|
|
# aren't GitHub-created or explicitly permitted.
|
|
- id: filter
|
|
env:
|
|
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
|
|
HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
|
run: |
|
|
# On the very first push to a branch, github.event.before is all-zeros.
|
|
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
|
echo "source=true" >> "$GITHUB_OUTPUT"
|
|
echo "No usable base SHA; treating as source change."
|
|
exit 0
|
|
fi
|
|
# The shallow checkout may not contain BASE_SHA (multi-commit pushes,
|
|
# long-lived PR branches). Fetch just that commit; fall back to a full
|
|
# unshallow only if the targeted fetch fails.
|
|
if ! git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
|
|
git fetch --depth=1 origin "$BASE_SHA" 2>/dev/null \
|
|
|| git fetch --unshallow origin 2>/dev/null \
|
|
|| git fetch --unshallow 2>/dev/null || true
|
|
fi
|
|
changed=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
|
|
echo "Changed files:"
|
|
echo "$changed"
|
|
# Drop paths that should NOT count as source changes; if anything
|
|
# survives, real source changed.
|
|
source=$(echo "$changed" | grep -vE \
|
|
-e '^AUTHORS$' \
|
|
-e '^CONTRIBUTING\.md$' \
|
|
-e '(^|/)CODEOWNERS$' \
|
|
-e '^Documentation/' \
|
|
-e '^tools/ci/docker/linux/' \
|
|
-e '^tools/codeowners/[^/]+$' \
|
|
|| true)
|
|
if [ -n "$source" ]; then
|
|
echo "source=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "source=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
load-targets:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- id: set-matrix
|
|
run: echo "matrix=$(jq -c '.' .github/membrowse-targets.json)" >> $GITHUB_OUTPUT
|
|
|
|
# Post an "identical" MemBrowse report when only docs/CODEOWNERS changed.
|
|
# Only runs on push events (master/releases/tags) to keep the baseline
|
|
# complete; PR events don't produce identical markers.
|
|
identical:
|
|
needs: [changes-filter, load-targets]
|
|
if: needs.changes-filter.outputs.source == 'false' && github.event_name == 'push'
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include: ${{ fromJson(needs.load-targets.outputs.matrix) }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 2
|
|
- uses: actions/setup-python@v6
|
|
with:
|
|
python-version: '3.11'
|
|
- name: Install membrowse
|
|
run: pip install --no-cache-dir membrowse
|
|
- name: Upload identical - ${{ matrix.target_name }}
|
|
env:
|
|
MEMBROWSE_API_KEY: ${{ secrets.MEMBROWSE_API_KEY }}
|
|
MEMBROWSE_API_URL: ${{ vars.MEMBROWSE_API_URL }}
|
|
TARGET_NAME: ${{ matrix.target_name }}
|
|
# Base on the previous branch tip, not the (often unreported mid-PR)
|
|
# git parent.
|
|
BEFORE_SHA: ${{ github.event.before }}
|
|
run: |
|
|
ARGS=(--upload --github
|
|
--target-name "$TARGET_NAME"
|
|
--api-key "$MEMBROWSE_API_KEY"
|
|
--identical)
|
|
if [ -n "$MEMBROWSE_API_URL" ]; then
|
|
ARGS+=(--api-url "$MEMBROWSE_API_URL")
|
|
fi
|
|
# Override parent with previous tip; skip on branch creation (0s).
|
|
if [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
|
|
ARGS+=(--base-sha "$BEFORE_SHA")
|
|
fi
|
|
membrowse report "${ARGS[@]}"
|
|
|
|
# Build target with debug symbols + linker map, then upload MemBrowse report.
|
|
# Uses the NuttX CI Docker image so toolchains, kconfig-frontends, etc. are
|
|
# already installed.
|
|
analyze:
|
|
needs: [changes-filter, load-targets]
|
|
if: needs.changes-filter.outputs.source == 'true'
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
DOCKER_BUILDKIT: 1
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include: ${{ fromJson(needs.load-targets.outputs.matrix) }}
|
|
steps:
|
|
- name: Checkout nuttx repo
|
|
uses: actions/checkout@v7
|
|
with:
|
|
path: sources/nuttx
|
|
fetch-depth: 2
|
|
|
|
- name: Checkout nuttx-apps repo
|
|
uses: actions/checkout@v7
|
|
with:
|
|
repository: apache/nuttx-apps
|
|
path: sources/apps
|
|
fetch-depth: 1
|
|
|
|
- name: Docker Login
|
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Docker Pull
|
|
run: docker pull ghcr.io/apache/nuttx/apache-nuttx-ci-linux
|
|
|
|
- name: Build ${{ matrix.target_name }}
|
|
uses: ./sources/nuttx/.github/actions/ci-container
|
|
with:
|
|
run: |
|
|
git config --global --add safe.directory /github/workspace/sources/nuttx
|
|
git config --global --add safe.directory /github/workspace/sources/apps
|
|
cd /github/workspace/sources/nuttx
|
|
./tools/configure.sh -l ${{ matrix.board_config }}
|
|
echo CONFIG_DEBUG_SYMBOLS=y >> .config
|
|
echo CONFIG_DEBUG_LINK_MAP=y >> .config
|
|
if [ -n "${{ matrix.config_overrides }}" ]; then
|
|
kconfig-tweak ${{ matrix.config_overrides }}
|
|
fi
|
|
make olddefconfig
|
|
# Preserve preprocessed linker scripts (.ld.tmp) so MemBrowse can
|
|
# read them. Arch Makefiles delete these immediately after linking.
|
|
find arch -name Makefile -exec sed -i '/DELFILE, $(addsuffix .tmp,$(ARCHSCRIPT))/d' {} +
|
|
make -j$(nproc)
|
|
|
|
# MemBrowse action runs from $GITHUB_WORKSPACE and discovers git via CWD,
|
|
# but nuttx is checked out to sources/nuttx/ (so apps can sit beside it).
|
|
# Expose the repo's .git at the workspace root so git metadata resolves.
|
|
- name: Expose nuttx .git to workspace root
|
|
run: ln -sfn sources/nuttx/.git .git
|
|
|
|
- name: Prefix linker script paths
|
|
id: ld
|
|
run: |
|
|
prefixed=""
|
|
for p in ${{ matrix.ld }}; do
|
|
prefixed="$prefixed sources/nuttx/$p"
|
|
done
|
|
paths=$(echo $prefixed | xargs)
|
|
echo "paths=$paths" >> "$GITHUB_OUTPUT"
|
|
echo "Resolved ld paths: [$paths]"
|
|
for p in $paths; do
|
|
if [ -e "$p" ]; then
|
|
echo " OK $p ($(stat -c '%s bytes, owner=%U:%G' "$p" 2>/dev/null))"
|
|
else
|
|
echo " MISS $p"
|
|
echo " ls dir:"
|
|
ls -la "$(dirname "$p")" 2>&1 | head -30
|
|
fi
|
|
done
|
|
|
|
- uses: actions/setup-python@v6
|
|
with:
|
|
python-version: '3.11'
|
|
- name: Install membrowse
|
|
run: pip install --no-cache-dir membrowse
|
|
- name: MemBrowse analysis - ${{ matrix.target_name }}
|
|
env:
|
|
MEMBROWSE_API_KEY: ${{ secrets.MEMBROWSE_API_KEY }}
|
|
MEMBROWSE_API_URL: ${{ vars.MEMBROWSE_API_URL }}
|
|
TARGET_NAME: ${{ matrix.target_name }}
|
|
ELF: sources/nuttx/${{ matrix.elf }}
|
|
LD_PATHS: ${{ steps.ld.outputs.paths }}
|
|
MAP_FILE: ${{ matrix.map_file != '' && format('sources/nuttx/{0}', matrix.map_file) || '' }}
|
|
LINKER_VARS: ${{ matrix.linker_vars }}
|
|
# Push only: base on the previous branch tip, not the (often
|
|
# unreported mid-PR) git parent. Empty on PRs (--github bases on the
|
|
# PR target tip).
|
|
BEFORE_SHA: ${{ github.event_name == 'push' && github.event.before || '' }}
|
|
run: |
|
|
set -o pipefail
|
|
ARGS=("$ELF" "$LD_PATHS"
|
|
--upload --github
|
|
--target-name "$TARGET_NAME"
|
|
--api-key "$MEMBROWSE_API_KEY")
|
|
if [ -n "$MEMBROWSE_API_URL" ]; then
|
|
ARGS+=(--api-url "$MEMBROWSE_API_URL")
|
|
fi
|
|
# Override parent with previous tip; skip on branch creation (0s).
|
|
if [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
|
|
ARGS+=(--base-sha "$BEFORE_SHA")
|
|
fi
|
|
if [ -n "$MAP_FILE" ]; then
|
|
ARGS+=(--map-file "$MAP_FILE")
|
|
fi
|
|
if [ -n "$LINKER_VARS" ]; then
|
|
set -f
|
|
for var in $LINKER_VARS; do
|
|
ARGS+=(--def "$var")
|
|
done
|
|
set +f
|
|
fi
|
|
membrowse --verbose INFO report "${ARGS[@]}"
|