Skip to content

Commit cf20f0e

Browse files
committed
feat(extensions): add kernel-debug-tiers
Cumulative kernel debug-information tiers for headless boards that need on-device debugging through the serial console: KERNEL_DEBUG_TIER=0 no-op (extension loaded but kernel-side disabled) KERNEL_DEBUG_TIER=1 printk timestamps + lockup/hung-task detection (default — cheap, no board prerequisites) KERNEL_DEBUG_TIER=2 + pstore/ramoops (needs DT or bootarg reservation) KERNEL_DEBUG_TIER=3 + KGDB/KDB over serial (needs kgdboc= bootarg) By itself the extension does nothing visible at runtime; it bakes enough information into the kernel image that operator-console facilities like SysRq tracebacks and KGDB sessions are actually useful instead of streams of hex addresses. Pair with `sysrq-serial-trigger` (separate extension) to also enable the operator-control surface itself. extension_prepare_config validates the tier value, hard-fails with a helpful message on KERNEL_BTF=no + KERNEL_DEBUG_TIER>=1 (BTF is required for hung-task tracebacks and KGDB symbol resolution; either set KERNEL_BTF=yes or KERNEL_DEBUG_TIER=0 to opt out of the kernel side), and forces KERNEL_BTF=yes for tier>=1 when not explicitly set. Signed-off-by: Igor Velkov <325961+iav@users.noreply.github.com>
1 parent 2ab6962 commit cf20f0e

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

extensions/kernel-debug-tiers.sh

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env bash
2+
#
3+
# SPDX-License-Identifier: GPL-2.0
4+
# Copyright (c) 2026 Igor Velkov
5+
# This file is a part of the Armbian Build Framework https://github.com/armbian/build/
6+
#
7+
# Enable cumulative kernel debug-information tiers for headless boards that
8+
# need to be debugged through their serial console. On its own this extension
9+
# does nothing visible at runtime — it just bakes enough information into the
10+
# kernel that the operator-console facilities (Magic SysRq, KGDB, pstore) and
11+
# any tracebacks they print are actually useful rather than streams of hex
12+
# addresses.
13+
#
14+
# Pair with the `sysrq-serial-trigger` extension (kconfig + sysctl + u-boot
15+
# autoboot delay) to actually have an operator-controllable surface; that
16+
# extension turns the facilities ON, this one makes them MEANINGFUL.
17+
#
18+
# Tiers are cumulative: KERNEL_DEBUG_TIER=N enables every tier ≤ N.
19+
#
20+
# 0 no-op (extension stays loaded, kernel side disabled — keeps the
21+
# extension declarable in a shared config without forcing the cost on
22+
# every board, e.g. when one board needs BTF=no for RAM reasons)
23+
# 1 printk timestamps + lockup/hung-task detection + stack guards
24+
# Cost: a handful of bytes per printk, a few cycles per scheduler tick.
25+
# No board prerequisites. Default tier.
26+
# 2 + pstore/ramoops (persistent dmesg through reboot)
27+
# Cost: same as tier 1 plus a reserved memory region. Without a DT
28+
# `/reserved-memory/ramoops` node or `ramoops.mem_address=…` bootargs,
29+
# the modules load but have nowhere to write — silent no-op.
30+
# 3 + KGDB / KDB over serial
31+
# Cost: same as tier 2 plus larger kernel image. Needs `kgdboc=<port>,<baud>`
32+
# in bootargs; without it the dispatcher is in-kernel but unreachable.
33+
#
34+
# BTF requirement: from tier 1 onwards this extension needs DEBUG_INFO_BTF=y
35+
# (Armbian's default unless KERNEL_BTF=no). Without BTF, hung-task tracebacks
36+
# and KGDB symbol output collapse to address-only — the whole point of the
37+
# extension is then defeated. So an explicit `KERNEL_BTF=no` (in board/family
38+
# conf or on the build CLI) combined with KERNEL_DEBUG_TIER>=1 is treated as
39+
# a hard error: pick one, not both.
40+
#
41+
# Usage — add to your userpatches config (e.g. `userpatches/config-my.conf`):
42+
#
43+
# enable_extension "kernel-debug-tiers"
44+
# KERNEL_DEBUG_TIER=2 # default 1 if unset
45+
#
46+
# Or pass on the build CLI for a one-off run:
47+
#
48+
# ./compile.sh build BOARD=helios64 BRANCH=edge \
49+
# ENABLE_EXTENSIONS=kernel-debug-tiers KERNEL_DEBUG_TIER=3
50+
51+
function extension_prepare_config__kernel_debug_tiers() {
52+
declare tier="${KERNEL_DEBUG_TIER:-1}"
53+
case "${tier}" in
54+
0 | 1 | 2 | 3) ;;
55+
*) exit_with_error "${EXTENSION}: KERNEL_DEBUG_TIER must be 0, 1, 2 or 3" "got '${tier}'" ;;
56+
esac
57+
58+
# Tier 0 is a deliberate no-op — extension declared but kernel side off.
59+
# Skip the BTF check so a shared config can switch BTF on/off per board.
60+
if [[ "${tier}" == "0" ]]; then
61+
display_alert "${EXTENSION}: KERNEL_DEBUG_TIER=0" "extension loaded but kernel-side debug disabled" "info"
62+
return 0
63+
fi
64+
65+
# Hard-fail on conflicting BTF preference. Armbian disables BTF (and all
66+
# DEBUG_INFO) when KERNEL_BTF=no — that strips the very symbols this
67+
# extension's tracebacks rely on.
68+
if [[ "${KERNEL_BTF}" == "no" ]]; then
69+
exit_with_error \
70+
"${EXTENSION}: KERNEL_BTF=no conflicts with KERNEL_DEBUG_TIER=${tier}" \
71+
"BTF is required for hung-task tracebacks and KGDB symbol resolution; either set KERNEL_BTF=yes (or leave it unset for Armbian's default) or set KERNEL_DEBUG_TIER=0 to disable this extension's kernel side"
72+
fi
73+
74+
# Make BTF explicit so Armbian's default-BTF-on path is unambiguous in logs
75+
# even when the user didn't set KERNEL_BTF themselves.
76+
declare -g KERNEL_BTF="${KERNEL_BTF:-yes}"
77+
display_alert "${EXTENSION}: KERNEL_DEBUG_TIER=${tier}" "BTF=${KERNEL_BTF}" "info"
78+
}
79+
80+
# Tier 1: low-cost diagnostics. printk timestamps and PRINTK_CALLER add a few
81+
# bytes per line; stack-end check and lockup detectors cost a few cycles per
82+
# scheduler tick. Cheap enough to ship on a headless server with serial debug.
83+
function custom_kernel_config__kernel_debug_tier1() {
84+
if [[ "${KERNEL_DEBUG_TIER:-1}" -lt 1 ]]; then
85+
return 0
86+
fi
87+
display_alert "${EXTENSION}: tier 1" "printk timestamps + lockup/hung-task detection" "info"
88+
opts_y+=(
89+
"PRINTK_TIME"
90+
"PRINTK_CALLER"
91+
"DETECT_HUNG_TASK"
92+
"SOFTLOCKUP_DETECTOR"
93+
"SCHED_STACK_END_CHECK"
94+
)
95+
# Default is 120s upstream; explicit so the value shows up in .config.
96+
opts_val["DEFAULT_HUNG_TASK_TIMEOUT"]="120"
97+
}
98+
99+
# Tier 2: pstore/ramoops — kernel writes its last printk before crash to a
100+
# reserved memory region; userspace reads /sys/fs/pstore/dmesg-ramoops-0
101+
# after the reboot. Catch: needs a memory region reserved either via
102+
# `/reserved-memory/ramoops { ... }` in the device tree, or via bootargs
103+
# `ramoops.mem_address=<phys> ramoops.mem_size=0x100000 ramoops.console_size=0x40000`.
104+
# Without that the modules load but have nowhere to write — silent no-op.
105+
function custom_kernel_config__kernel_debug_tier2_pstore() {
106+
if [[ "${KERNEL_DEBUG_TIER:-1}" -lt 2 ]]; then
107+
return 0
108+
fi
109+
display_alert "${EXTENSION}: tier 2 (pstore/ramoops)" "needs DT or bootarg reservation, otherwise no-op" "info"
110+
opts_y+=("PSTORE" "PSTORE_CONSOLE" "PSTORE_RAM" "PSTORE_DEFLATE_COMPRESS")
111+
}
112+
113+
# Tier 3: KGDB/KDB over the serial console. SysRq+g drops into the KDB shell
114+
# on the same line as the kernel printk — `bt`, `dmesg`, `lsmod`, `dis`,
115+
# watchpoints. Needs `kgdboc=<console>,<baud>` in bootargs (e.g.
116+
# `kgdboc=ttyS2,1500000` on Helios64) for the dispatcher to attach to the
117+
# port; without that the kconfig is enabled but unreachable.
118+
function custom_kernel_config__kernel_debug_tier3_kgdb() {
119+
if [[ "${KERNEL_DEBUG_TIER:-1}" -lt 3 ]]; then
120+
return 0
121+
fi
122+
display_alert "${EXTENSION}: tier 3 (KGDB)" "remember kgdboc=<port>,<baud> in bootargs" "info"
123+
opts_y+=(
124+
"KGDB"
125+
"KGDB_SERIAL_CONSOLE"
126+
"KGDB_KDB"
127+
"KGDB_LOW_LEVEL_TRAP"
128+
)
129+
# 0xFF = enable every KDB command (memory peek, register inspect, single
130+
# step, etc.). On a locked-down production box this can be masked down —
131+
# bits are documented in Documentation/dev-tools/kgdb.rst.
132+
# shellcheck disable=SC2034 # opts_val is read by armbian_kernel_config_apply_opts_from_arrays
133+
opts_val["KDB_DEFAULT_ENABLE"]="0xFF"
134+
}

0 commit comments

Comments
 (0)