Skip to content

Commit dd1b3f5

Browse files
committed
gmentci: add PR stack usage report via puncover
Add scripts and a CI workflow step that compare static stack usage between a PR and its merge base. On each PR, the workflow rebuilds with `CONFIG_STACK_USAGE=y`, runs puncover via `stack_threads.py`, and posts a sticky comment with a per-thread table showing configured size, max static depth, delta vs base, and remaining margin. Thread entry points are discovered from `K_THREAD_DEFINE` in app sources; Zephyr/NCS subsystem stacks are resolved via `rg` in the west tree with a small fallback map. Work queues list configured size only since puncover can't attribute depth per queue. Signed-off-by: Simen S. Røstad <simen.rostad@nordicsemi.no>
1 parent 47257dd commit dd1b3f5

3 files changed

Lines changed: 665 additions & 0 deletions

File tree

.github/workflows/build.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,30 @@ jobs:
280280
header: app-size-report
281281
path: ${{ github.workspace }}/size-comment.md
282282

283+
- name: Install puncover
284+
if: github.event_name == 'pull_request'
285+
run: pip install puncover --break-system-packages
286+
287+
- name: Generate PR stack usage comment for thingy91x image
288+
if: github.event_name == 'pull_request'
289+
env:
290+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
291+
BOARD: thingy91x/nrf9151/ns
292+
PR_APP_DIR: ${{ github.workspace }}/asset-tracker-template/app
293+
STACK_COMMENT_PATH: ${{ github.workspace }}/stack-comment.md
294+
CI_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
295+
run: |
296+
SDK_BIN=$(ls -d /opt/toolchains/zephyr-sdk-*/arm-zephyr-eabi/bin 2>/dev/null | head -1)
297+
[ -n "$SDK_BIN" ] && export PATH="$SDK_BIN:$PATH"
298+
bash asset-tracker-template/scripts/ci/pr-stack-usage.sh
299+
300+
- name: Post PR stack usage sticky comment for thingy91x image
301+
if: github.event_name == 'pull_request'
302+
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
303+
with:
304+
header: stack-usage-report
305+
path: ${{ github.workspace }}/stack-comment.md
306+
283307
- name: Upload artifacts
284308
uses: actions/upload-artifact@v4
285309
with:

scripts/ci/pr-stack-usage.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env bash
2+
# Generate a sticky-comment-ready markdown body with puncover static stack usage
3+
# for ATT module threads (PR vs merge base).
4+
#
5+
# Required env vars:
6+
# BASE_SHA, BOARD, STACK_COMMENT_PATH
7+
# PR_APP_DIR path to app/ in this checkout (default: <repo>/app)
8+
# Optional:
9+
# CI_RUN_URL
10+
11+
set -euo pipefail
12+
: "${BASE_SHA:?}" "${BOARD:?}" "${STACK_COMMENT_PATH:?}"
13+
14+
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
15+
WORKSPACE_DIR="$(cd "$REPO_ROOT/.." && pwd)"
16+
PR_APP_DIR="${PR_APP_DIR:-$REPO_ROOT/app}"
17+
# Separate from the release build dir so we do not disturb CI firmware artifacts.
18+
PR_STACK_BUILD_DIR="${PR_STACK_BUILD_DIR:-$PR_APP_DIR/build-stack-analysis}"
19+
BASELINE_STACK_BUILD_DIR="${BASELINE_STACK_BUILD_DIR:-$PR_APP_DIR/build-baseline-stack}"
20+
STACK_DIR="${STACK_DIR:-$(dirname "$STACK_COMMENT_PATH")/stack}"
21+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
22+
# Copied before any `git checkout` of the merge base — those commits may not contain these scripts.
23+
CI_SCRIPTS_DIR="$STACK_DIR/ci-scripts"
24+
STACK_THREADS_PY="$CI_SCRIPTS_DIR/stack_threads.py"
25+
26+
mkdir -p "$STACK_DIR" "$CI_SCRIPTS_DIR" "$(dirname "$STACK_COMMENT_PATH")"
27+
cp "$SCRIPT_DIR/stack_threads.py" "$STACK_THREADS_PY"
28+
PR_SHA=$(git -C "$REPO_ROOT" rev-parse HEAD)
29+
30+
write_failure_comment() {
31+
echo "Stack usage report failed. See [CI run](${CI_RUN_URL:-}) for logs." >"$STACK_COMMENT_PATH"
32+
}
33+
34+
stack_build() {
35+
local build_dir="$1"
36+
rm -rf "$build_dir"
37+
(cd "$PR_APP_DIR" && west build -p --sysbuild -b "$BOARD" -d "$build_dir" -- \
38+
-DCONFIG_STACK_USAGE=y 2>&1)
39+
}
40+
41+
run_report() {
42+
local elf="$1" build_dir="$2" config="$3" out="$4" app_dir="$5"
43+
python3 "$STACK_THREADS_PY" report \
44+
"$elf" "$build_dir/app" "$config" "$out" "$app_dir" \
45+
--west "$WORKSPACE_DIR"
46+
}
47+
48+
if ! stack_build "$PR_STACK_BUILD_DIR" | tee "$STACK_DIR/pr_stack_build.log"; then
49+
write_failure_comment
50+
exit 0
51+
fi
52+
53+
if ! run_report \
54+
"$PR_STACK_BUILD_DIR/app/zephyr/zephyr.elf" \
55+
"$PR_STACK_BUILD_DIR" \
56+
"$PR_STACK_BUILD_DIR/app/zephyr/.config" \
57+
"$STACK_DIR/pr_stack.json" \
58+
"$PR_APP_DIR"; then
59+
write_failure_comment
60+
exit 0
61+
fi
62+
63+
git -C "$REPO_ROOT" fetch --depth=1 origin "$BASE_SHA"
64+
git -C "$REPO_ROOT" checkout --detach "$BASE_SHA"
65+
(cd "$WORKSPACE_DIR" && west update -o=--depth=1 -n)
66+
67+
if ! stack_build "$BASELINE_STACK_BUILD_DIR" | tee "$STACK_DIR/baseline_stack_build.log"; then
68+
git -C "$REPO_ROOT" checkout --detach "$PR_SHA"
69+
write_failure_comment
70+
exit 0
71+
fi
72+
73+
if ! run_report \
74+
"$BASELINE_STACK_BUILD_DIR/app/zephyr/zephyr.elf" \
75+
"$BASELINE_STACK_BUILD_DIR" \
76+
"$BASELINE_STACK_BUILD_DIR/app/zephyr/.config" \
77+
"$STACK_DIR/baseline_stack.json" \
78+
"$PR_APP_DIR"; then
79+
git -C "$REPO_ROOT" checkout --detach "$PR_SHA"
80+
write_failure_comment
81+
exit 0
82+
fi
83+
84+
git -C "$REPO_ROOT" checkout --detach "$PR_SHA"
85+
86+
if ! python3 "$STACK_THREADS_PY" format-comment \
87+
"$STACK_DIR/pr_stack.json" \
88+
"$STACK_DIR/baseline_stack.json" \
89+
"$PR_APP_DIR" \
90+
--config "$PR_STACK_BUILD_DIR/app/zephyr/.config" \
91+
--west "$WORKSPACE_DIR" \
92+
--ci-url "${CI_RUN_URL:-}" \
93+
>"$STACK_COMMENT_PATH"; then
94+
write_failure_comment
95+
exit 0
96+
fi

0 commit comments

Comments
 (0)