Status: TEMPORARY — and one of its two reasons has now closed. Everything described here was added on request to support two investigations: the field hard-hang (a board that died with no serial output and needed a power cycle) and the still-unmeasured PREEMPT_RT wakeup latency (
docs/rt-beta-kernel.md). It is not stock parity, it is not part of the P2.1 package manifest (docs/package-manifest.md), and it is expected to be removed once both close. §5 is the exact revert recipe.Update 2026-07-21 — the field hard-hang is RESOLVED and is no longer a reason to keep this block. Root cause: the board was auto-overclocking itself to 1.2 GHz on boot. The forward-ported socfpga cpufreq driver (
linux-patches/0003-cpufreq-cyclone5-de10nano-overclock.patch) was written against 5.15, whereCPUFREQ_BOOST_FREQkept the governor off the boost rows by default whilescaling_max_freqstayed writable up to 1.2 GHz; on 6.18 the defaultpolicy->maxresolves differently, so the overclock became default-on rather than opt-in. Fixed in PR #24 (3fb7f81, merged83ae09a): a proper->set_boost, the 1000/1200 MHz rows flagged boost-only, and an 800 MHz default. The board is stable and serial console is clean.That class of defect is the thing worth remembering: the bug was not in the patch's logic but in a silent semantic change to a kernel flag across the version gap we forward-ported over — it applied cleanly, compiled, and behaved differently. Any other 5.15-era patch we carry can fail the same way.
What that leaves: only the RT latency measurement (
rt-tests) still justifies this block. Do not cite the field hang as an open risk, and do not resurrect the discarded theories (PANIC_ON_OOPS+panic_timeout=0"silent brick", marginal SD-card contact, USB hub brownout) — each was plausible, some were separately fixed, none was the cause.
Two edits, each wrapped in a matching banner pair so both can be found with one grep and deleted with one editor motion. The banners are, verbatim:
# >>> DEBUG TOOLING — TEMPORARY, REMOVE AS ONE BLOCK <<<
...
# >>> END DEBUG TOOLING <<<
(the opening banner in linux.config additionally carries a
(docs/debug-tooling.md) suffix). Because the opening and closing banners are
not mirror images, always search for the substring both share — never for a
full banner line:
$ grep -rn "DEBUG TOOLING" configs/ board/
| Where | What |
|---|---|
configs/mister_de10nano_defconfig |
BR2_PACKAGE_GDB + _GDB_SERVER + _GDB_DEBUGGER, BR2_PACKAGE_STRACE, BR2_PACKAGE_LINUX_TOOLS_PERF (+ _NEEDS_HOST_PYTHON3), BR2_PACKAGE_RT_TESTS |
board/mister/de10nano/linux.config |
CONFIG_COREDUMP=y (was # CONFIG_COREDUMP is not set) |
Nothing else in the tree references any of it.
| Tool | Version | Source |
|---|---|---|
| gdb / gdbserver | 15.2 | BR2_GDB_VERSION default for a GCC >= 9 toolchain (package/gdb/Config.in.host:77) |
| strace | 7.0 | package/strace/strace.mk |
| perf | = the kernel pin (BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE, configs/mister_de10nano_defconfig) |
built from our own kernel's tools/perf, not a standalone release — so it tracks the pin by construction, never separately (§2) |
| rt-tests | 2.8 | package/rt-tests/rt-tests.mk |
| numactl | 2.0.19 | pulled in by rt-tests (select) |
| mpfr | 4.2.2 | pulled in by the full gdb (select) |
numactl and mpfr are transitive and are deliberately not listed in the
defconfig block — listing them would leave them enabled after the block is
deleted, which is exactly the residue this whole arrangement exists to avoid.
Both halves were asked for explicitly, and they serve different flows:
- gdbserver — the normal embedded flow:
gdbserver :2345 ./MiSTeron the device, a host cross-gdb attaching over TCP. - the full on-device debugger — opens a core file on the device itself with
no host toolchain present. That is the realistic flow when a beta tester
reports a hard hang: they can be walked through
gdb /media/fat/MiSTer coreover SSH without installing anything.
The host half of the gdbserver flow is not built by this branch. Only the
target symbols were requested, so there is no arm-linux-gdb in output/host/.
If you want one, that is a separate knob in the Toolchain menu:
BR2_PACKAGE_HOST_GDB=y # cross-gdb for the build host
Add it inside the debug block if you need it. It is a host package (it builds
into output/host/, ships nothing to the device) and it still matches the
^BR2_PACKAGE_ fingerprint filter, so §4's "no cache eviction" conclusion holds
for it too. It was left out only because the on-device debugger already covers
the core-file flow, which is the one that matters for a field report.
The one real trap here. package/gdb/Config.in has:
select BR2_PACKAGE_GDB_SERVER if \
(!BR2_PACKAGE_GDB_DEBUGGER && !BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY)
so BR2_PACKAGE_GDB=y alone implies gdbserver — but that select stops
firing the moment GDB_DEBUGGER=y. Enabling the full debugger without also
listing BR2_PACKAGE_GDB_SERVER=y explicitly would therefore silently remove
gdbserver from the image. Both lines are present and neither is redundant.
BR2_PACKAGE_GDB_DEBUGGER requires BR2_USE_WCHAR, already satisfied here
(glibc + BR2_ENABLE_LOCALE=y). Its selects are GMP / MPFR / readline / zlib;
only MPFR is new.
GDB_TUI and GDB_PYTHON are not enabled — neither was requested and both
only add on-device weight.
No dependencies at all on this toolchain. Nothing subtle.
BR2_PACKAGE_LINUX_TOOLS_PERF selects the BR2_PACKAGE_LINUX_TOOLS meta-symbol,
which is part of the linux package, not a standalone one. Consequences:
- perf is rebuilt whenever the kernel is, and always matches the running kernel's
tools/perf— no version skew; - it is not built by the kernel-only
mister_kernel_defconfig/make rtpath, which sets no packages. RT getsCONFIG_COREDUMPbut not a perf of its own — the single shippedperfis the one from the main image's kernel build, and it is what runs under the RT kernel too. Theperf_event_attrABI is size-versioned and stable in both directions, so a 6.18 perf works against a 7.2 kernel; it will simply not know about anything added after 6.18. Good enough for cyclictest-adjacent work, and worth remembering before blaming the RT kernel for a perf feature that is merely missing from the tool.
_NEEDS_HOST_PYTHON3 is mandatory here, not cosmetic. Since ~6.0 perf
generates pmu-events.c at build time by running
tools/perf/pmu-events/jevents.py, and 6.18.39 still ships that script (verified
present in the unpacked tree). Without the symbol Buildroot never appends
host-python3 to PERF_DEPENDENCIES
(package/linux-tools/linux-tool-perf.mk.in:11-12) and the build is left at the
mercy of whatever python3 the host happens to have.
The kernel side needs nothing from us.
linux-tool-perf.mk.in:199 force-enables CONFIG_PERF_EVENTS at kconfig-fixup
time, and in this tree that fixup is already a no-op — CONFIG_PERF_EVENTS=y,
CONFIG_ARM_PMU=y and CONFIG_HW_PERF_EVENTS=y all resolve on by kconfig
default and are live in the built .config today. They are absent from
linux.config only because savedefconfig omits defaults.
What this perf can and cannot do, taken from the actual build log rather than predicted — Buildroot passed:
NO_LIBELF=1 NO_DWARF=1 NO_LIBUNWIND=1 NO_DEMANGLE=1
NO_LIBTRACEEVENT=1 NO_NEWT=1 NO_SLANG=1 NO_LIBAUDIT=1
NO_LIBPYTHON=1 NO_LIBPERL=1
and perf's own feature probe reported libelf: OFF, libnuma: on, zlib: on.
So:
- ✅
perf stat,perf record/reportwith kernel symbols from/proc/kallsyms, hardware counters, NUMA-aware helpers (libnumais on only because rt-tests dragged in numactl — a free side effect); - ❌ userspace symbol resolution from ELF binaries, DWARF callchains,
perf probe, C++ symbol demangling, tracepoint decoding, the TUI.
Each ❌ traces to one omitted package listed in §3 — elfutils is the one that
buys back most of that list.
Hardware counters really work, they are not silently downgraded to software
events: socfpga.dtsi carries
pmu: pmu@ff111000 {
compatible = "arm,cortex-a9-pmu";
interrupts = <0 176 4>, <0 177 4>;
interrupt-affinity = <&cpu0>, <&cpu1>;
...
};
i.e. a Cortex-A9 PMU node with a per-CPU interrupt for each of the two cores, so
perf stat returns real cycle/instruction counts.
cyclictest and friends — the standard PREEMPT_RT latency harness, and the thing
that finally answers the open TODO in docs/rt-beta-kernel.md: the RT kernel
boots on hardware, but its wakeup latency has never been measured. selects
numactl. hwlatdetect is a Python script and installs because
BR2_PACKAGE_PYTHON3=y.
Note BR2_PACKAGE_LINUX_TOOLS_RTLA (osnoise/timerlat tracers) is not
enabled — not requested, and it would pull in libtracefs.
rt-tests.mk installs the Python hwlatdetect as an absolute symlink:
/usr/bin/hwlatdetect -> /usr/lib/python3.14/site-packages//hwlatdetect.py
but this image is built with Buildroot's default BR2_PACKAGE_PYTHON3_PYC_ONLY=y,
whose target-finalize step byte-compiles every .py and deletes the source.
Verified on the built tree: site-packages/ contains hwlatdetect.pyc and no
hwlatdetect.py, so the symlink dangles and running hwlatdetect on the device
gives No such file or directory. Every other rt-tests binary is a normal C
program and is completely unaffected — this is hwlatdetect alone.
It is left broken deliberately:
- the workaround is one command, since CPython runs a
.pycdirectly —python3 /usr/lib/python3.14/site-packages/hwlatdetect.pyc; - fixing it properly means either flipping
BR2_PACKAGE_PYTHON3_PY_PYC(a global change to how every Python file in the image is installed, to rescue one script) or a post-build hook to repoint the symlink — both far outside a block whose whole value is being deletable in one motion; - and
hwlatdetectmeasures SMI-induced hardware latency, which is an x86 concern. On a Cortex-A9 there are no SMIs. It is close to the least useful thing in the package here.
cyclictest — the tool that actually motivated enabling rt-tests — is a plain
ELF binary and works normally.
A deliberate divergence from stock. Stock's own config has
# CONFIG_COREDUMP is not set (docs/stock-inventory/stock-linux.config:721),
and so did ours until now. Without it the kernel cannot dump core at all, so a
crashing MiSTer binary leaves nothing for gdb to open — which would make
shipping gdb close to pointless.
CONFIG_ELF_CORE (init/Kconfig, depends on COREDUMP, default y) is what
actually writes the ELF core file. It turns on for free and must not get a
line of its own: it is invisible to kconfig while COREDUMP is off, so a
savedefconfig round-trip would drop any line added for it.
linux.config is shared with configs/mister_kernel_defconfig, so the RT/beta
kernel gets coredumps too. That is wanted — RT is the variant under active
on-hardware investigation.
CONFIG_COREDUMP=y only makes cores possible; nothing here changes the
defaults, so out of the box:
-
ulimit -cis0— raise it (ulimit -c unlimited) in the shell or init script that launches the process you want to dump; -
/proc/sys/kernel/core_patterniscore, i.e. a file namedcorein the process's CWD — and for the MiSTer binary that CWD is/, not/media/fat. It is launched from/etc/inittabas::sysinit:/media/fat/MiSTer &and BusyBox
initruns with/as its working directory; the child inherits it, andMain_MiSTernever callschdir()(checked — there is nochdirin its sources). So an unconfigured core lands in the root of the loop-mounted 512 MiB rootfs: it is wiped by the next reflash, it eats the free spacescripts/check-size-budget.shguards, and a core of a process this size can be tens of MB.That makes redirecting
core_patternclose to mandatory in practice rather than a nicety. The device-side one-liner, no rebuild needed:echo '/media/fat/core.%e.%p' > /proc/sys/kernel/core_pattern
/media/fatis the FAT data partition, so it survives a rootfs reflash — but FAT has no sparse files, so the core is written at full size.
Neither knob is changed by this branch: both are runtime state, and persisting
them is a rootfs-overlay sysctl/init change that deserves its own commit — and
its own entry in this document — rather than riding along in a package-selection
one.
Each of these would make one of the tools meaningfully better, and each was left off to keep the revertible delta minimal. They are listed here so the trade-off is a decision on the record rather than an oversight:
| Symbol | What it would buy | Why it is off |
|---|---|---|
BR2_PACKAGE_ELFUTILS |
perf gains libelf/DWARF: userspace symbol resolution from ELF binaries, perf probe, build-id handling. Without it perf builds NO_LIBELF=1 NO_DWARF=1 (linux-tool-perf.mk.in:94-96) and can only symbolize the kernel via /proc/kallsyms. |
Not requested; a sizeable new package. This is the most likely thing to want next — enable it inside the debug block if perf output turns out to be unreadable. |
BR2_PACKAGE_LIBUNWIND |
strace -k (stack trace per syscall) and perf DWARF callchains. |
Not requested. Confirmed off in the build: perf got NO_LIBUNWIND=1. |
BR2_PACKAGE_BINUTILS / BR2_PACKAGE_LIBTRACEEVENT |
perf C++ symbol demangling (NO_DEMANGLE=1 today) and tracepoint decoding (NO_LIBTRACEEVENT=1 today). |
Not requested. Noted because MiSTer is C++, so unmangled symbols in a profile are the first thing you will hit after elfutils. |
BR2_PACKAGE_LINUX_TOOLS_PERF_TUI |
perf's interactive TUI (slang is already =y, so this is nearly free). |
Not requested. |
BR2_PACKAGE_GDB_PYTHON / _TUI |
Python scripting / split-window gdb on the device. | Not requested; on-device weight. |
BR2_PACKAGE_LINUX_TOOLS_RTLA |
osnoise / timerlat tracers, complementing cyclictest. | Not requested; pulls in libtracefs. |
CONFIG_DEBUG_INFO in the kernel |
DWARF for the kernel: perf annotation against kernel source, crash-style analysis. |
Enormous, and the cost lands in the rootfs, not where you would guess: zImage is unaffected (it is objcopy'd from a stripped vmlinux), but every shipped .ko grows by roughly an order of magnitude, and those live in linux.img under the §6 free-space budget. |
- CI cross-toolchain cache: unaffected. The whole defconfig block is
BR2_PACKAGE_*lines, and.github/actions/buildroot-build's toolchain fingerprint filters^($|BR2_PACKAGE_|BR2_LINUX_KERNEL)out before hashing (docs/ci.md#toolchain-fingerprint). Adding these therefore does not evictbr-host-*and does not force a cold ~3 h rebuild.linux.configis not part of that fingerprint either. scripts/check-kernel-defconfig-sync.sh: unaffected. It compares only symbols defined in both defconfigs and theBR2_arm/BR2_ARM_/BR2_cortex/BR2_KERNEL_HEADERS/BR2_TOOLCHAIN_BUILDROOT_choice families. Package symbols are one-sided by design and exempt.- Image size. Measured, not estimated — see §6.
- SD-card installer: no hard cap hit.
scripts/mk-sdcard.shshipslinux.imggzipped and sizes the FAT partition from measured staged content (du -sk --apparent-size), so a fatter rootfs growssdcard.imgrather than overflowing anything. The installer stream-decompresses, so themem=511Mceiling is not approached either. Nothing to do — noted because "we made the rootfs bigger" invites the question. - Attack surface. A passwordless-root image that now also ships a full
debugger and
perf. That is acceptable for a debugging branch on a device with no meaningful secrets, and is a further reason this is temporary.
-
Delete the whole
DEBUG TOOLINGblock fromconfigs/mister_de10nano_defconfig— opening banner (>>> DEBUG TOOLING — TEMPORARY, REMOVE AS ONE BLOCK <<<) through closing banner (>>> END DEBUG TOOLING <<<), inclusive. See §1 for why the two banner lines are not mirror images. -
In
board/mister/de10nano/linux.config, replace the corresponding block with the single original line:# CONFIG_COREDUMP is not set -
Drop the D10 row from
docs/kernel-config-deltas.md§2 and put its intro line back to "Nine changes. Every one is deliberate; every one is cited." (D10 is the only temporary row in that table; it is flagged ⚠ so it cannot be mistaken for a permanent divergence.) -
docs/package-manifest.md, two places:- drop the "⚠ This list is deliberately not identical to the live defconfig"
note directly under the ready-to-paste
BR2_PACKAGE_*list — once the block is gone the manifest and the defconfig agree again, which is what that note says; - in the §5 Drop-list table, restore the
vim, zsh, joe, mc, screen, gdbrow to listinggdbamong the tools that "remain off", and delete thegdb is no longer offsentence appended to it.
- drop the "⚠ This list is deliberately not identical to the live defconfig"
note directly under the ready-to-paste
-
Delete this file, and its entry in
README.md. -
Rebuild:
make mister_de10nano_defconfig # re-resolve output/.config from the defconfig make allA kernel-config change forces a kernel rebuild on its own. The package removals do not need a
make clean: Buildroot leaves stale files inoutput/target/, but the rootfs image is regenerated from it andtarget-finalizere-runs — if you want to be certain nothing survives,rm -rf output/target && make allis the cheap belt-and-braces version (it rebuilds no packages, only re-installs them).
Verify with both greps. The first catches the two config blocks; the second catches the documentation residue (the D10 row, the manifest note and the README entry all name the file, not the banner, so the first grep alone would pass with every one of them still in place):
grep -rn "DEBUG TOOLING" configs/ board/ # must return nothing
grep -rn "debug-tooling" docs/ README.md # must return nothingFrom a full cold make all of this branch, measured with the same
scripts/check-size-budget.sh gate docs/size-budget.md uses (floor: ≥ 15 %
free of the 512 MiB image).
linux.img: 264 MiB used, 248 MiB free — 48.4 % free. PASS, comfortably
above the 15 % floor.
Per binary, stripped, as installed:
| Binary | Size |
|---|---|
usr/bin/gdb |
7.7 MiB |
usr/bin/strace |
2.1 MiB |
usr/bin/perf |
2.5 MiB |
usr/bin/gdbserver |
524 KiB |
usr/bin/cyclictest |
52 KiB |
| the other 14 rt-tests binaries | ~1 MiB combined |
Total delta: +15 MiB of output/target.
That figure needs one correction to be honest, and it is the reason the raw
before/after percentages do not simply subtract. The master build this was
compared against had two kernel module trees staged (6.18.39 and the
7.2.0-rc4 RT tree from a previous make rt, 3.2 MiB), and this branch has only
6.18.39 — no make rt was run in this worktree. Comparing the images
head-on (50.5 % free vs 48.4 %) therefore understates the debug tooling by
crediting it with the absent RT tree. The +15 MiB above is du -sm output/target
on both sides with the RT tree subtracted from master's, which is the
apples-to-apples number.
scripts/check-size-budget.sh output/images/linux.img
du -sh output/target/usr/bin/{gdb,gdbserver,strace,perf,cyclictest}A full cold make all of this branch, in a dedicated worktree, from origin/master:
- both stages built clean (the only
Error … (ignored)lines in the log are Buildroot's usual e2fsprogs/glibc/nettle doc-target noise, present on master too); board/mister/de10nano/post-image.sh's owncheck-linux-img.shpassed every assertion — 512 MiB, labelrootfs, both pinned UUID/hash-seed, the exact 14-feature stock-derived ext4 feature set, and ADR 0015's no-ssh_host_*check;check-initramfspassed;scripts/check-kernel-defconfig-sync.shpasses (23 shared symbols agree);- the built kernel
.configcarriesCONFIG_COREDUMP=yandCONFIG_ELF_CORE=y— confirming §2.5's claim that ELF_CORE arrives free — alongsideCONFIG_PERF_EVENTS=y,CONFIG_ARM_PMU=y,CONFIG_HW_PERF_EVENTS=y; - all six tools are present in
output/target(see §6 for sizes), with the one documented exception ofhwlatdetect's dangling symlink (§2.4).
Not verified: nothing here has been run on hardware. The image builds and passes every static assertion; whether gdb opens a useful core off a real board, and what cyclictest actually reports under the RT kernel, are the next steps and the reason this branch exists.
BR2_REPRODUCIBLE=y is on and .github/workflows/reproducibility.yml builds the
tree twice and compares. None of the four tools is known to embed a build
timestamp or path, but none has been through that workflow yet either — if a
byte-identical rebuild starts failing on this branch, one of these packages is
the first place to look, not the last.
docs/rt-beta-kernel.md— the RT variant whose latency rt-tests exists to measuredocs/kernel-config-deltas.md— every intentional kernel-config divergence;CONFIG_COREDUMPis recorded there toodocs/package-manifest.md— the stock-parity package set this block is explicitly not part ofdocs/ci.md— toolchain fingerprint / cache-key behaviour referenced in §4docs/size-budget.md— the 15 % free-space floor §6 is measured against