Linux Kernel Debugging Going Beyond Printk Messages
Linux Kernel Debugging Going Beyond Printk Messages
Linux Kernel Debugging Going Beyond Printk Messages
Embedded Labworks
$ WHOAMI
✗ Embedded software developer for more than 20 years.
✗ Principal Engineer of Embedded Labworks, a company specialized in
the development of software projects and BSPs for embedded
systems.
https://e-labworks.com/en/
✗ Active in the embedded systems community in Brazil, creator of the
website Embarcados and blogger.
https://sergioprado.org
https://embeddedbits.org
✗ Contributor of several open source projects, including Buildroot,
Yocto Project and the Linux kernel.
Embedded Labworks
✗ This is also not a tutorial! We will talk about a lot of tools and
techniches and have fun with some demos!
Embedded Labworks
DEBUGGING STEP-BY-STEP
TYPES OF PROBLEMS
✗ We can consider as the top 5 types of problems in software:
✗ Crash.
✗ Lockup.
✗ Logic/implementation error.
✗ Resource leak.
✗ Performance.
Embedded Labworks
PROBLEMS vs TECHNIQUES
Crash Lockup Logic Leak Performance
printk()
Embedded Labworks
PROBLEMS vs TECHNIQUES
Crash Lockup Logic Leak Performance
Knowledge
Logs/dumps
Tracing/profiling
Interactive
debugging
Debugging
frameworks
Embedded Labworks
KERNEL OOPS
✗ Kernel oops is a way for the Linux kernel to communicate the user that
a certain error has occurred.
KERNEL PANIC
✗ After a system has experienced an oops, some internal resources
may no longer be operational.
KERNEL OOPS
# cat /sys/class/gpio/gpio504/value
[ 23.688107] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 23.696431] pgd = (ptrval)
[ 23.699167] [00000000] *pgd=28bd4831, *pte=00000000, *ppte=00000000
[ 23.705596] Internal error: Oops: 17 [#1] SMP ARM
[ 23.710316] Modules linked in:
[ 23.713394] CPU: 1 PID: 177 Comm: cat Not tainted 4.19.17 #8
[ 23.719060] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[ 23.725606] PC is at mcp23sxx_spi_read+0x34/0x84
[ 23.730241] LR is at _regmap_raw_read+0xfc/0x384
[ 23.734866] pc : [<c0539c44>] lr : [<c067d894>] psr: 60040013
[ 23.741142] sp : d8c6da48 ip : 00000009 fp : d8c6da6c
[ 23.746375] r10: 00000040 r9 : d8a94000 r8 : d8c6db30
[ 23.751608] r7 : c12ed9d4 r6 : 00000001 r5 : c0539c10 r4 : c1208988
[ 23.758145] r3 : d8789f41 r2 : 2afb07c1 r1 : d8789f40 r0 : 00000000
[...]
[ 24.164250] Backtrace:
[ 24.166720] [<c0539c10>] (mcp23sxx_spi_read) from [<c067d894>] (_regmap_raw_read+0xfc/0x384)
[ 24.177714] [<c067d798>] (_regmap_raw_read) from [<c067db64>] (_regmap_bus_read+0x48/0x70)
[ 24.196372] [<c067db1c>] (_regmap_bus_read) from [<c067c1a4>] (_regmap_read+0x74/0x200)
[ 24.210056] [<c067c130>] (_regmap_read) from [<c067c37c>] (regmap_read+0x4c/0x6c)
[ 24.227931] [<c067c330>] (regmap_read) from [<c053a24c>] (mcp23s08_get+0x58/0xa4)
[ 24.241096] [<c053a1f4>] (mcp23s08_get) from [<c053e764>]
[ 24.255650] [<c053e724>] (gpiod_get_raw_value_commit) from [<c05401f0>] (gpiod_get_value_canslee
[ 24.276913] [<c05401c0>] (gpiod_get_value_cansleep) from [<c0544a68>] (value_show+0x34/0x5c)
[ 24.288949] [<c0544a34>] (value_show) from [<c06580d0>] (dev_attr_show+0x2c/0x5c)
[ 24.302118] [<c06580a4>] (dev_attr_show) from [<c0343a78>] (sysfs_kf_read+0x58/0xd8)
[...]
Embedded Labworks
ADDR2LINE
✗ The addr2line tool is capable of converting a memory address
into a line of source code:
$ arm-linux-addr2line -f -e vmlinux 0xc0539c44
mcp23sxx_spi_read
/home/sprado/elce/linux/drivers/pinctrl/pinctrl-mcp23s08.c:357
Embedded Labworks
FADDR2LINE
✗ The faddr2line kernel script will translate a stack dump function
offset into a source code line:
$ ./scripts/faddr2line vmlinux mcp23sxx_spi_read+0x34
mcp23sxx_spi_read+0x34/0x80:
mcp23sxx_spi_read at drivers/pinctrl/pinctrl-mcp23s08.c:357
Embedded Labworks
GDB LIST
$ arm-linux-gdb vmlinux
GDB DISASSEMBLE
$ arm-linux-gdb vmlinux
[...]
[...]
Embedded Labworks
PSTORE
✗ Pstore is a generic kernel framework for persistent data storage
and can be enabled with the CONFIG_PSTORE option.
✗ With pstore you can save the oops and panic logs through the
CONFIG_PSTORE_RAM option, allowing you to retrieve log messages
even after a soft reboot.
CONFIGURING PSTORE
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
ramoops: ramoops@0b000000 {
compatible = "ramoops";
reg = <0x20000000 0x200000>; /* 2MB */
record-size = <0x4000>; /* 16kB */
console-size = <0x4000>; /* 16kB */
};
};
Embedded Labworks
USING PSTORE
✗ To access the logs you should mount the pstore file system:
# mount -t pstore pstore /sys/fs/pstore/
KDUMP
✗ Kdump uses kexec to quickly boot to a dump-capture kernel
whenever a dump of the system kernel's memory needs to be taken
(for example, when the system panics).
KDUMP
✗ On a kernel panic, the new kernel will boot and you can access the
memory image of the crashed kernel through /proc/vmcore.
✗ This exports the dump as an ELF-format file that can be copied and
analysed with tools such as GDB and crash.
Interactive debugging
Embedded Labworks
✗ Problem 2: source code and development tools are on the host and
the kernel image is running on target.
Host Target
arm-linux-gdb KGDB
serial or
ethernet
connection
KGDB
✗ KGDB is a GDB server implementation integrated in the Linux kernel.
https://www.kernel.org/doc/html/latest/dev-tools/kgdb.html
✗ Available in the mainline Linux kernel since version 2.6.26 (x86 and
sparc) and 2.6.27 (arm, mips and ppc).
1. ENABLING KGDB
✗ To use KGDB, you must recompile the Linux kernel with the
following options:
✗ CONFIG_KGDB: enables support for KGDB.
✗ CONFIG_KGDB_SERIAL_CONSOLE: Enables KGDB communication I/O
driver over the serial port.
✗ CONFIG_MAGIC_SYSRQ: Enables magic sysrq key functionality to put
the kernel in debug mode.
✗ CONFIG_DEBUG_INFO: Compiles the kernel with debug symbols.
✗ CONFIG_FRAME_POINTER: Helps to produce more reliable stack
traces.
Embedded Labworks
✗ To configure KGDB at boot time, use the boot parameters kgdboc and
kgdbwait as shown below:
kgdboc=ttymxc0,115200 kgdbwait
✗ At run time, we can use the commands below to put the kernel in
debug mode:
# echo ttymxc0 > /sys/module/kgdboc/parameters/kgdboc
# echo g > /proc/sysrq-trigger
Embedded Labworks
✗ At the GDB command line, configure the serial port and connect to
the target:
(gdb) set serial baud 115200
(gdb) target remote /dev/ttyUSB0
Embedded Labworks
AGENT PROXY
✗ If you are using the serial port for both console and KGDB
debugging, you will need to use a proxy to manage the serial
communication.
$ cd agent-proxy/
$ make
Embedded Labworks
✗ Open a terminal and run the telnet command connect to the target
console:
$ telnet localhost 5550
AGENT PROXY
Host Target
console
KGDB
5550
Agent Proxy
5551
Serial port
arm-linux-gdb
Kernel Linux
(zImage)
Kernel image
with debug symbols
(vmlinux)
Embedded Labworks
GDB SCRIPTS
✗ The kernel provides a collection of helper scripts that can simplify
the kernel debugging process.
Tracing
Embedded Labworks
TRACING
✗ There are two main types of tracing: static tracing and dynamic tracing.
GCC -PG
(gdb) disassemble gpiod_direction_input
Dump of assembler code for function gpiod_direction_input:
0xc04faeb8 <+0>: mov r12, sp
0xc04faebc <+4>: push {r4, r5, r6, r7, r11, r12, lr, pc}
0xc04faec0 <+8>: sub r11, r12, #4
0xc04faec4 <+12>: push {lr} ; (str lr, [sp, #-4]!)
0xc04faec8 <+16>: bl 0xc01132e8 <__gnu_mcount_nc>
0xc04faecc <+20>: ldr r1, [pc, #280] ; 0xc04fafec <gpiod_directio...
0xc04faed0 <+24>: mov r5, r0
0xc04faed4 <+28>: bl 0xc04fa924 <validate_desc>
0xc04faed8 <+32>: subs r4, r0, #0
0xc04faedc <+36>: ble 0xc04faf28 <gpiod_direction_input+112>
0xc04faee0 <+40>: ldr r3, [r5]
0xc04faee4 <+44>: ldr r0, [r3, #492] ; 0x1ec
0xc04faee8 <+48>: ldr r1, [r3, #496] ; 0x1f0
0xc04faeec <+52>: ldr r2, [r0, #36] ; 0x24
0xc04faef0 <+56>: sub r1, r5, r1
0xc04faef4 <+60>: cmp r2, #0
0xc04faef8 <+64>: asr r1, r1, #4
0xc04faefc <+68>: beq 0xc04fafc0 <gpiod_direction_input+264>
[...]
Embedded Labworks
TRACE EVENTS
int gpiod_direction_input(struct gpio_desc *desc)
{
struct gpio_chip *chip;
int status = -EINVAL;
VALIDATE_DESC(desc);
chip = desc->gdev->chip;
if (!chip->get || !chip->direction_input) {
gpiod_warn(desc,
"%s: missing get() or direction_input() operations\n",
__func__);
return -EIO;
}
trace_gpio_direction(desc_to_gpio(desc), 1, status);
return status;
}
Embedded Labworks
KPROBE
void input_set_abs_params(struct input_dev *dev, unsigned int axis,
int min, int max, int fuzz, int flat)
{
struct input_absinfo *absinfo;
input_alloc_absinfo(dev);
if (!dev->absinfo) Save context
return;
Software INT Probe function
absinfo = &dev->absinfo[axis];
absinfo->minimum = min;
absinfo->maximum = max; Restore context
absinfo->fuzz = fuzz;
absinfo->flat = flat;
dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);
}
Embedded Labworks
FTRACE
✗ Ftrace is the official tracer of the Linux kernel and can be used for
debugging and performance/latency analysis.
ENABLING FTRACE
Embedded Labworks
USING FTRACE
# mount -t tracefs none /sys/kernel/tracing
# cd /sys/kernel/tracing/
# cat available_tracers
hwlat blk function_graph wakeup_dl wakeup_rt
wakeup irqsoff function nop
Embedded Labworks
FUNCTION TRACER
# echo function > current_tracer
# cat trace
# tracer: function
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
<idle>-0 [001] d... 23.695208: _raw_spin_lock_irqsave <-hrtimer_next_event_wi...
<idle>-0 [001] d... 23.695209: __hrtimer_next_event_base <-hrtimer_next_event...
<idle>-0 [001] d... 23.695210: __next_base <-__hrtimer_next_event_base
<idle>-0 [001] d... 23.695211: __hrtimer_next_event_base <-hrtimer_next_event...
<idle>-0 [001] d... 23.695212: __next_base <-__hrtimer_next_event_base
<idle>-0 [001] d... 23.695213: __next_base <-__hrtimer_next_event_base
<idle>-0 [001] d... 23.695214: _raw_spin_unlock_irqrestore <-hrtimer_next_eve...
<idle>-0 [001] d... 23.695215: get_iowait_load <-menu_select
<idle>-0 [001] d... 23.695217: tick_nohz_tick_stopped <-menu_select
<idle>-0 [001] d... 23.695218: tick_nohz_idle_stop_tick <-do_idle
<idle>-0 [001] d... 23.695219: rcu_idle_enter <-do_idle
<idle>-0 [001] d... 23.695220: call_cpuidle <-do_idle
<idle>-0 [001] d... 23.695221: cpuidle_enter <-call_cpuidle
[...]
Embedded Labworks
✗ It can configure ftrace, read the buffer and save the data to a file
(trace.dat) for further analysis.
TRACE-CMD
# trace-cmd record -p function -F ls /
plugin 'function'
CPU0 data recorded at offset=0x30d000
737280 bytes in size
CPU1 data recorded at offset=0x3c1000
0 bytes in size
# ls trace.dat
trace.dat
# trace-cmd report
CPU 1 is empty
cpus=2
ls-175 [000] 43.359618: function: mutex_unlock <-- rb_simple_write
ls-175 [000] 43.359624: function: __fsnotify_parent <-- vfs_write
ls-175 [000] 43.359625: function: fsnotify <-- vfs_write
ls-175 [000] 43.359627: function: __sb_end_write <-- vfs_write
ls-175 [000] 43.359628: function: __f_unlock_pos <-- ksys_write
ls-175 [000] 43.359629: function: mutex_unlock <-- __f_unlock_pos
ls-175 [000] 43.359647: function: do_PrefetchAbort <-- ret_fr
ls-175 [000] 43.359649: function: do_page_fault <-- do_PrefetchAbo
ls-175 [000] 43.359651: function: down_read_trylock <-- do_page_fa
ls-175 [000] 43.359652: function: _cond_resched <-- do_page_fault
ls-175 [000] 43.359654: function: rcu_all_qs <-- _cond_resched
ls-175 [000] 43.359655: function: find_vma <-- do_page_fault
ls-175 [000] 43.359656: function: vmacache_find <-- find_vma
[...]
Embedded Labworks
KERNELSHARK
$ kernelshark trace.dat
Embedded Labworks
DEBUGGING LOCKUPS
# echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# task is hanging in kernel space!
# ls
trace.dat.cpu0 trace.dat.cpu1
# ls
trace.dat trace.dat.cpu0 trace.dat.cpu1
Embedded Labworks
DEBUGGING LOCKUPS
Embedded Labworks
Debugging frameworks
Embedded Labworks
KERNEL HACKING
Embedded Labworks
LOCKUPS
✗ The kernel has some options for identifying kernel space lockups in
the "Kernel Hacking" configuration menu, showing a kernel oops
message when a task hangs in kernel space.
LOCKUPS
✗ The CONFIG_SOFTLOCKUP_DETECTOR option will monitor lockups
for more than 20 seconds without letting other tasks run.
✗ The CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC option will cause a
soft lockup to panic.
DEBUGGING LOCKUPS
# hwclock -w -f /dev/rtc1
[ 48.041337] watchdog: BUG: soft lockup - CPU#1 stuck for 22s! [hwclock:180]
[ 48.048322] Modules linked in:
[ 48.051396] CPU: 1 PID: 180 Comm: hwclock Not tainted 4.18.9 #51
[ 48.057412] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[ 48.063964] PC is at snvs_rtc_set_time+0x60/0xc8
[ 48.068599] LR is at _raw_spin_unlock_irqrestore+0x40/0x54
[ 48.074093] pc : [<c0516eec>] lr : [<c0723aa8>] psr: 60060013
[ 48.080367] sp : d949fdf8 ip : d949fd78 fp : d949fe2c
[ 48.085599] r10: c0786554 r9 : bef2bc94 r8 : 00000000
[ 48.090832] r7 : d8e71450 r6 : c0bc74a0 r5 : d840b410 r4 : d949fe58
[ 48.097368] r3 : 1e6a8abe r2 : 1e6a8abe r1 : 00000000 r0 : 00000000
[ 48.103904] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
[ 48.111047] Control: 10c5387d Table: 2980804a DAC: 00000051
[ 48.116805] CPU: 1 PID: 180 Comm: hwclock Not tainted 4.18.9 #51
[ 48.122818] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[...]
[ 48.253808] [<c0009a30>] (__irq_svc) from [<c0516eec>] (snvs_rtc_set_time+0x60/0xc8)
[ 48.261571] [<c0516eec>] (snvs_rtc_set_time) from [<c050c358>] (rtc_set_time+0x94/0x1f0)
[ 48.269676] [<c050c358>] (rtc_set_time) from [<c050dee8>] (rtc_dev_ioctl+0x3a8/0x654)
[ 48.277529] [<c050dee8>] (rtc_dev_ioctl) from [<c019e310>] (do_vfs_ioctl+0xac/0x944)
[ 48.285291] [<c019e310>] (do_vfs_ioctl) from [<c019ebec>] (ksys_ioctl+0x44/0x68)
[ 48.292701] [<c019ebec>] (ksys_ioctl) from [<c019ec28>] (sys_ioctl+0x18/0x1c)
[ 48.299851] [<c019ec28>] (sys_ioctl) from [<c0009000>] (ret_fast_syscall+0x0/0x28)
Embedded Labworks
DEBUGGING LOCKUPS
$ arm-linux-addr2line -f -e vmlinux 0xc0516eec
snvs_rtc_set_time
/opt/labs/ex/linux/drivers/rtc/rtc-snvs.c:140
$ arm-linux-gdb vmlinux
(gdb) list *(snvs_rtc_set_time+0x60)
0xc0516eec is in snvs_rtc_set_time (drivers/rtc/rtc-snvs.c:140).
135
136 dev_dbg(dev, "After convertion: %ld", time);
137
138 /* Disable RTC first */
139 ret = snvs_rtc_enable(data, false);
140 if (ret)
141 return ret;
142
143 while(1);
144
Embedded Labworks
MEMORY LEAK
✗ Excessive system memory consumption may be associated with a
kernel space memory leak problem.
KMEMLEAK
✗ With kmemleak enabled, a kernel thread will monitor the memory
every 10 minutes and log potential allocated and unfreed memory
regions.
# ps | grep kmemleak
root 151 2 0 0 800df728 00000000 S kmemleak
KMEMLEAK
✗ We can force a memory check and create a list of possible memory
leaks by writing scan to this file:
# echo scan > /sys/kernel/debug/kmemleak
USING KMEMLEAK
# cat /sys/kernel/debug/kmemleak
unreferenced object 0xd9868000 (size 30720):
comm "sh", pid 179, jiffies 4294943731 (age 19.720s)
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0a 00 07 41 00 00 00 00 00 00 00 00 28 6e bf d8 ...A........(n..
backtrace:
[<c015c9e8>] kmalloc_order+0x54/0x5c
[<c015ca1c>] kmalloc_order_trace+0x2c/0x10c
[<c03c39ec>] gpiod_set_value_cansleep+0x3c/0x54
[<c03c827c>] value_store+0x98/0xd8
[<c042e31c>] dev_attr_store+0x28/0x34
[<c02112a0>] sysfs_kf_write+0x48/0x54
[<c021099c>] kernfs_fop_write+0xfc/0x1e0
[<c0190fa8>] __vfs_write+0x44/0x160
[<c0191254>] vfs_write+0xb0/0x178
[<c0191490>] ksys_write+0x58/0xbc
[<c019150c>] sys_write+0x18/0x1c
[<c0009000>] ret_fast_syscall+0x0/0x28
[<be829888>] 0xbe829888
Embedded Labworks
USING KMEMLEAK
$ arm-linux-addr2line -f -e vmlinux 0xc03c39ec
gpiod_set_value_cansleep
/opt/labs/ex/linux/drivers/gpio/gpiolib.c:3465
$ arm-linux-gdb vmlinux
(gdb) list *(gpiod_set_value_cansleep+0x3c)
0xc03c39ec is in gpiod_set_value_cansleep (drivers/gpio/gpiolib.c:3465).
3460 void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
3461 {
3462 might_sleep_if(extra_checks);
3463 VALIDATE_DESC_VOID(desc);
3464 kmalloc(1024*30, GFP_KERNEL);
3465 gpiod_set_value_nocheck(desc, value);
3466 }
3467 EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
Embedded Labworks
CONCLUSION
✗ Know the tools!
✗ There are many more tools: SystemTap, Perf, eBPF, LTTnG, etc.
✗ Debugging is fun!
QUESTIONS?
E-mail sergio.prado@e-labworks.com
Website https://e-labworks.com/en
Blog https://embeddedbits.org
Twitter @sergioprado
Embedded Labworks