| name | linux-debugging |
| description | Use when debugging embedded Linux systems targeting automotive IVI, HUD, and RSE devices. Covers serial console setup, kernel log analysis (dmesg/printk), remote debugging with gdbserver, JTAG via OpenOCD, ftrace, perf basics, /proc and /sys inspection, and reading kernel oops/panic messages.
|
| argument-hint | <symptom-or-subsystem> [crash|hang|perf|driver] |
Embedded Linux Debugging
Practices for diagnosing and fixing issues on embedded automotive IVI, HUD,
and RSE Linux targets.
When to Use This Skill
- Investigating a kernel crash (oops, panic, BUG).
- Tracing a suspected kernel-level performance regression.
- Debugging a device driver or kernel module remotely.
- Inspecting hardware state via
/proc and /sys.
Serial Console
The serial console (ttyS0, ttyAMA0, ttyUSB0, etc.) is the first diagnostic tool.
picocom -b 115200 /dev/ttyUSB0
minicom -b 115200 -D /dev/ttyUSB0
Kernel command line must include console=<tty>,<baud>:
console=ttyAMA0,115200
Add ignore_loglevel or set loglevel=8 temporarily during bring-up to see all printk messages.
Kernel Log — dmesg and printk
dmesg
dmesg -T
dmesg -w
dmesg --level=err,warn
dmesg -C
Dynamic debug — enable pr_debug() at runtime
echo "file drivers/mydriver/mydriver.c +p" > /sys/kernel/debug/dynamic_debug/control
echo "module mydriver +p" > /sys/kernel/debug/dynamic_debug/control
echo "module mydriver -p" > /sys/kernel/debug/dynamic_debug/control
Requires CONFIG_DYNAMIC_DEBUG=y.
Kernel Oops Analysis
A kernel oops appears on the serial console or in dmesg:
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = (ptrval)
[00000000] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT SMP ARM
Modules linked in: mydriver
PC is at mydriver_read+0x28/0x6c [mydriver]
LR is at vfs_read+0x78/0x1c4
...
Key fields:
| Field | Meaning |
|---|
PC is at | Instruction that caused the fault |
LR | Link register — return address (where we came from) |
Oops: 5 | Fault status code (architecture-specific) |
#1 | This is the first oops since boot |
Call trace | Stack frames at time of fault |
Decoding with addr2line
aarch64-linux-gnu-addr2line -e vmlinux -f ffffffc0108abc28
aarch64-linux-gnu-addr2line -e mydriver.o -f 0x28
scripts/decode_stacktrace.sh (kernel tree)
dmesg | ./scripts/decode_stacktrace.sh vmlinux /path/to/modules
Remote Debugging with gdbserver
gdbserver :2345 /usr/bin/my-app
gdbserver --attach :2345 <pid>
aarch64-linux-gnu-gdb my-app
(gdb) target remote <target-ip>:2345
(gdb) break main
(gdb) continue
(gdb) bt
Kernel debugging with KGDB
Requires CONFIG_KGDB=y, CONFIG_KGDB_SERIAL_CONSOLE=y, and the kernel command line:
kgdboc=ttyAMA0,115200 kgdbwait
On the host:
aarch64-linux-gnu-gdb vmlinux
(gdb) target remote /dev/ttyUSB1
JTAG Debugging with OpenOCD
openocd -f interface/ftdi/olimex-arm-usb-ocd-h.cfg -f target/stm32f4x.cfg
arm-none-eabi-gdb vmlinux
(gdb) target remote localhost:3333
(gdb) monitor reset halt
(gdb) load
(gdb) continue
OpenOCD listens on TCP port 3333 (GDB), 4444 (telnet CLI), 6666 (Tcl RPC) by default.
ftrace — Kernel Function Tracer
ftrace is accessed via the tracefs filesystem, typically mounted at /sys/kernel/debug/tracing/.
mount -t debugfs none /sys/kernel/debug
cd /sys/kernel/debug/tracing
cat available_tracers
echo function > current_tracer
echo "mydriver_*" > set_ftrace_filter
echo 1 > tracing_on
echo 0 > tracing_on
cat trace
echo > set_ftrace_filter
echo nop > current_tracer
trace_printk (from kernel code)
trace_printk("mydriver: entered %s, value=%d\n", __func__, value);
Read with cat /sys/kernel/debug/tracing/trace.
perf — Performance Analysis
perf record -a -g sleep 10
perf report
perf top -a
perf stat -e cache-misses,cache-references ./my-app
perf trace -e 'syscalls:sys_enter_read' ./my-app
Requires CONFIG_PERF_EVENTS=y in the kernel and perf binary built from kernel tools/perf/.
/proc — Process and Kernel State
cat /proc/cpuinfo
cat /proc/meminfo
cat /proc/interrupts
cat /proc/modules
ls -la /proc/<pid>/fd
cat /proc/<pid>/maps
cat /proc/cmdline
cat /proc/kmsg
/sys — Sysfs Hardware State
ls /sys/class/net/
cat /sys/class/net/eth0/operstate
echo 42 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio42/direction
echo 1 > /sys/class/gpio/gpio42/value
ls /sys/class/i2c-adapter/
cat /sys/power/state
echo mem > /sys/power/state
Common Debugging Scenarios
Driver not probing
dmesg | grep mydriver
find /sys/firmware/devicetree/base -name compatible | xargs grep -l my-hardware
lsmod | grep mydriver
ls /sys/bus/platform/devices/
Kernel module fails to load
insmod ./mydriver.ko
dmesg | tail -20
modinfo mydriver.ko
Hung process
echo t > /proc/sysrq-trigger
echo l > /proc/sysrq-trigger
Prerequisites
- Linux host machine (Ubuntu 20.04 LTS or Debian 11 recommended).
- Cross-compilation toolchain:
arm-linux-gnueabihf-gcc or aarch64-linux-gnu-gcc.
- Target board with serial console access (USB-to-UART adapter).
ssh access to target (optional but recommended).
Step-by-Step Workflows
Step 1: Connect to the target
Use serial console (minicom/picocom at 115200 8N1) or SSH; confirm connection before starting.
Step 2: Capture kernel logs
Run dmesg | tail -50 and journalctl -k for recent kernel messages.
Step 3: Identify the root cause
Match error messages against the common patterns documented in the sections below.
Step 4: Set up remote debugging (if needed)
Start gdbserver :1234 myapp on target; attach with arm-none-eabi-gdb on host.
Step 5: Verify the fix
Reboot or reload the module; confirm the issue does not recur; update team runbook.
Troubleshooting
- No serial output — check baud rate (usually 115200 8N1) and cable; ensure
CONFIG_SERIAL_CONSOLE is enabled in the kernel config.
gdbserver connection refused — firewall or iptables may be blocking the port; try iptables -F on the target for debugging.
dmesg timestamp missing — boot with printk.time=1 kernel cmdline parameter.
- ftrace ring buffer overflows — increase buffer size:
echo 65536 > /sys/kernel/debug/tracing/buffer_size_kb.
Pre-Commit Checklist (for code that adds debugging hooks)
References