Debugging Slides
Debugging Slides
Corrections, suggestions, contributions and translations are welcome! embedded Linux and kernel engineering
Send them to feedback@[Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 1/346
Linux debugging, profiling and tracing training
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 2/346
About Bootlin
About Bootlin
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 3/346
Bootlin introduction
▶ Engineering company
• In business since 2004
• Before 2018: Free Electrons
▶ Team based in France and Italy
▶ Serving customers worldwide
▶ Highly focused and recognized expertise
• Embedded Linux
• Linux kernel
• Embedded Linux build systems
▶ Strong open-source contributor
▶ Activities
• Engineering services
• Training courses
▶ [Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 4/346
Bootlin engineering services
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 5/346
Bootlin training courses
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 6/346
Bootlin, an open-source contributor
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 7/346
Bootlin on-line resources
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 8/346
Generic course information
Generic course
information
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 9/346
STM32MP157 shopping list
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 10/346
Beagleplay shopping list
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 11/346
Training quiz and certificate
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 12/346
Participate!
During the lectures...
▶ Don’t hesitate to ask questions. Other people in the audience may have similar
questions too.
▶ Don’t hesitate to share your experience too, for example to compare Linux with
other operating systems you know.
▶ Your point of view is most valuable, because it can be similar to your colleagues’
and different from the trainer’s.
▶ In on-line sessions
• Please always keep your camera on!
• Also make sure your name is properly filled.
• You can also use the ”Raise your hand” button when you wish to ask a question but
don’t want to interrupt.
▶ All this helps the trainer to engage with participants, see when something needs
clarifying and make the session more interactive, enjoyable and useful for everyone.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 13/346
Collaborate!
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 14/346
Practical lab - Training Setup
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 15/346
Debugging, Tracing, Profiling
Debugging, Tracing,
Profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 16/346
Debugging, Tracing, Profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 17/346
Debugging
”Everyone knows that debugging is twice as hard as writing a program in the first place. So if
you’re as clever as you can be when you write it, how will you ever debug it?”
- Brian Kernighan
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 18/346
Tracing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 19/346
Profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 20/346
Event sources
▶ Some activities like tracing or profiling involve some preliminary data collection
▶ Those data are gathered from different event sources, which can be of various
types.
• Some low-level events are gathered directly by the hardware (eg: CPU cycles, MMU
exceptions...)
• Some events are generated by some code explicitely added to generate traces, either
in an application or in the kernel: those are static tracepoints
• Some are generated by instrumentation added at runtime (ie without having to
modify/rebuild the application and/or the kernel): those are dynamic probes
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 21/346
Linux Application Stack
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 22/346
Linux Application Stack
User/Kernel mode
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 23/346
User/Kernel mode
▶ User mode vs Kernel mode are often used to refer to the privilege level of
execution.
▶ This mode actually refers to the processor execution mode which is a hardware
mode.
• Might be named differently between architectures but the goal is the same
▶ Allows the kernel to control the full processor state (handle exceptions, MMU,
etc) whereas the userspace can only do basic control and execute under the kernel
supervision.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 24/346
Linux Application Stack
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 25/346
Processes and Threads (1/2)
▶ A process is a group of resources that are allocated by the kernel to allow the
execution of a program.
• Memory regions, threads, file descriptors, etc.
▶ A process is identified by a PID (Process ID) and all the information that are
specific to this process are exposed in /proc/<pid>.
• A special file named /proc/self accessible by the process points to the proc folder
associated to it.
▶ When starting a process, it initially has one execution thread that is represented
by a struct task_struct and that can be scheduled.
• A process is represented in the kernel by a thread associated to multiple resources.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 26/346
Processes and Threads (2/2)
▶ Threads are independent execution units that are sharing common resources inside
a process.
• Same address space, file descriptors, etc.
▶ A new process is created using the fork() system call (man 2 fork) and a new
thread is created using pthread_create() (man 3 pthread_create).
• Internally, both will call clone() with different flags
▶ At any moment, only one task is executing on a CPU core and is accessible using
get_current() function (defined by architecture and often stored in a register).
▶ Each CPU core will execute a different task.
▶ A task can only be executing on one core at a time.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 27/346
Linux Application Stack
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 28/346
The MMU
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 29/346
MMU and memory management
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 30/346
Userspace/Kernel memory layout
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 31/346
Userspace/Kernel memory layout
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 32/346
Kernel memory map
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 33/346
Userspace memory segments
▶ When starting a process, the kernel sets up several Virtual Memory Areas (VMA),
backed by struct vm_area_struct, with different execution attributes.
▶ VMA are actually memory zones that are mapped with specific attributes
(R/W/X).
▶ A segmentation fault happens when a program tries to access an unmapped area
or a mapped area with an access mode that is not allowed.
• Writing data in a read-only segment
• Executing data from a non-executable segment
▶ New memory zones can be created using mmap() (man 2 mmap)
▶ Per application mappings are visible in /proc/<pid>/maps
7f1855b2a000-7f1855b2c000 rw-p 00030000 103:01 3408650 [Link]
7ffc01625000-7ffc01646000 rw-p 00000000 00:00 0 [stack]
7ffc016e5000-7ffc016e9000 r--p 00000000 00:00 0 [vvar]
7ffc016e9000-7ffc016eb000 r-xp 00000000 00:00 0 [vdso]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 34/346
Virtual memory VS physical memory
▶ Memory segments can be shared among different processes
▶ Non-contiguous physical memory can be virtually contiguous
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 35/346
Userspace memory types
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 36/346
On-demand memory mapping (Lazy allocation)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 37/346
On-demand memory mapping: page faults
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 38/346
Terms for memory in Linux tools
▶ When using Linux tools, four terms are used to describe memory:
• VSS/VSZ: Virtual Set Size (Virtual memory size, shared libraries included).
• RSS: Resident Set Size (Total physical memory usage, shared libraries included).
• PSS: Proportional Set Size (Actual physical memory used, divided by the number of
times it has been mapped).
• USS: Unique Set Size (Physical memory occupied by the process, shared mappings
memory excluded).
▶ VSS >= RSS >= PSS >= USS.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 39/346
Linux Application Stack
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 40/346
Process context
▶ The process context can be seen as the content of the CPU registers associated to
a process: execution register, stack register...
▶ This context also designates an execution state and allows to sleep inside kernel
mode.
▶ A process that is executing in process context can be preempted.
▶ While executing in such context, the current process struct task_struct can be
accessed using get_current().
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 41/346
Linux Application Stack
Scheduling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 42/346
Scheduling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 43/346
The Linux Kernel Scheduler
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 44/346
Non-Realtime Scheduling Classes
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 45/346
Realtime Scheduling Classes
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 46/346
Changing the Scheduling Class
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 47/346
Linux Application Stack
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 48/346
Execution mode switching
▶ Execution mode switching is the action of changing the execution mode of the
processor (Kernel ↔ User).
• Explicitly by executing system calls instructions (synchronous request to the kernel
from user mode).
• Implicitly when receiving exceptions (MMU fault, interrupts, breakpoints, etc).
▶ This state change will end up in a kernel entrypoint (often call vectors) that will
execute necessary code to setup a correct state for kernel mode execution.
▶ The kernel takes care of saving registers, switching to the kernel stack and
potentially other things depending on the architecture.
• Does not use the user stack but a specific kernel fixed size stack for security
purposes.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 49/346
Exceptions
▶ Exceptions designate the kind of events that will trigger a CPU execution mode
change to handle the exception.
▶ Two main types of exceptions exist: synchronous and asynchronous.
• Asynchronous exceptions when a fault happens while executing (MMU, bus abort,
etc) or when an interrupt is received (either software or hardware).
• Synchronous when executing some specific instructions (breakpoint, syscall, etc)
▶ When such exception is triggered, the processor will jump to the exception vector
and execute the code that was setup for this exception.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 50/346
Interrupts
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 51/346
Interrupt context
▶ While handling the interrupts, the kernel is executing in a specific context named
interrupt context.
▶ This context does not have access to userspace and should not use
get_current().
▶ Depending on the architecture, might use an IRQ stack.
▶ Interrupts are disabled (no nested interrupt support)!
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 52/346
System Calls (1/2)
▶ A system call allows the user space to request services from the kernel by executing
a special instruction that will switch to the kernel mode (man 2 syscall)
• When executing functions provided by the libc (read(), write(), etc), they often
end up executing a system call.
▶ System calls are identified by a numeric identifier that is passed via the registers.
• The kernel exports some defines (in unistd.h) that are named __NR_<sycall> and
defines the syscall identifiers.
#define __NR_read 63
#define __NR_write 64
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 53/346
System Calls (2/2)
▶ The kernel holds a table of function pointers which matches these identifiers and
will invoke the correct handler after checking the validity of the syscall.
▶ System call parameters are passed via registers (up to 6).
▶ When executing this instruction the CPU will change its execution state and
switch to the kernel mode.
▶ Each architecture uses a specific hardware mechanism (man 2 syscall)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 54/346
Linux Application Stack
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 55/346
Kernel execution contexts
▶ The kernel runs code in various contexts depending on the event it is handling.
▶ Might have interrupts disabled, specific stack, etc.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 56/346
Kernel threads
▶ Kernel threads (kthreads) are a special kind of struct task_struct that do not
have any user resources associated (mm == NULL).
▶ These processes are cloned from the kthreadd process and can be created using
kthread_create().
▶ Kernel threads are scheduled and are allowed to sleep much like a process
executing in process context.
▶ Kernel threads are visible and their names are displayed between brackets under ps:
$ ps --ppid 2 -p 2 -o uname,pid,ppid,cmd,cls
USER PID PPID CMD CLS
root 2 0 [kthreadd] TS
root 3 2 [rcu_gp] TS
root 4 2 [rcu_par_gp] TS
root 5 2 [netns] TS
root 7 2 [kworker/0:0H-events_highpr TS
root 10 2 [mm_percpu_wq] TS
root 11 2 [rcu_tasks_kthread] TS
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 57/346
Workqueues
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 58/346
softirq
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 59/346
Interrupts & Softirqs
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 60/346
Threaded interrupts
▶ Threaded interrupts are a mecanism that allows to handle the interrupt using a
hard IRQ handler and a threaded IRQ handler.
• Created calling request_threaded_irq() instead of request_irq()
▶ A threaded IRQ handler will allow to execute work that can potentially sleep in a
kthread.
▶ One kthread is created for each interrupt line that was requested as a threaded
IRQ.
• kthread is named irq/<irq>-<name> and can be seen using ps.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 61/346
Allocations and context
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 62/346
Practical lab - Preparing the system
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 63/346
Linux Common Analysis & Observability Tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 64/346
Linux Common Analysis & Observability Tools
Pseudo Filesystems
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 65/346
Pseudo Filesystems
▶ Some virtual filesystems are exposed by the kernel and provide a lot of information
on the system.
▶ procfs contains information about processes and system information.
• Mounted on /proc
• Often parsed by tools to display raw data in a more user-friendly way.
▶ sysfs provides information about hardware/logical devices, association between
devices and drivers.
• Mounted on /sys
▶ debugfs exposes information related to debug.
• Typically mounted on /sys/kernel/debug/
• mount -t debugfs none /sys/kernel/debug
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 66/346
procfs
▶ procfs exposes information about processes and system (man 5 proc).
• /proc/cpuinfo CPU information.
• /proc/meminfo memory information (used, free, total, etc).
• /proc/sys/ contains system parameters that can be tuned. The list of parameters
that can be modified is available at admin-guide/sysctl/index
• /proc/interrupts: interrupt count per CPU for each interrupt in use
We also have one entry per interrupt in /proc/irq for specific configuration/status
for each interrupt line
• /proc/<pid>/ process related information
/proc/<pid>/status process basic information
/proc/<pid>/maps process memory mappings
/proc/<pid>/fd file descriptors of the process
/proc/<pid>/task descriptors of threads belonging to the process
• /proc/self/ will refer to the process used to access the file
▶ A list of all available procfs file and their content is described at
filesystems/proc and man 5 proc
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 67/346
sysfs
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 68/346
debugfs
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 69/346
Linux Common Analysis & Observability Tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 70/346
ELF files
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 71/346
binutils for ELF analysis
▶ The binutils are used to deal with binary files, either object files or executables.
• Includes ld, as and other useful tools.
▶ readelf displays information about ELF files (header, section, segments, etc).
▶ objdump allows to display information and disassemble ELF files.
▶ objcopy can convert ELF files or extract/translate some parts of it.
▶ nm displays the list of symbols embedded in ELF files.
▶ addr2line finds the source code line/file pair from an address using an ELF file
with debug information
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 72/346
binutils example (1/2)
▶ Using addr2line to match a kernel OOPS address or a symbol name with source
code:
$ addr2line -s -f -e vmlinux ffffffff8145a8b0
queue_wc_show
blk-sysfs.c:516
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 73/346
binutils example (2/2)
$ readelf -h binary
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
...
▶ Convert an ELF file to a flat binary file using objcopy:
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 74/346
ldd
▶ In order to display the shared libraries used by an ELF binary, one can use ldd
(Generally packaged with C library. See man 1 ldd).
▶ ldd will list all the libraries that were used at link time.
• Libraries that are loaded at runtime using dlopen() are not displayed.
$ ldd /usr/bin/bash
[Link].1 (0x00007ffdf3fc6000)
[Link].8 => /usr/lib/[Link].8 (0x00007fa2d2aef000)
[Link].6 => /usr/lib/[Link].6 (0x00007fa2d2905000)
[Link].6 => /usr/lib/[Link].6 (0x00007fa2d288e000)
/lib64/[Link].2 => /usr/lib64/[Link].2 (0x00007fa2d2c88000)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 75/346
Linux Common Analysis & Observability Tools
Monitoring tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 76/346
Monitoring Tools
▶ Lots of monitoring tools on Linux to allow monitoring various part of the system.
▶ Most of the time, these are CLI interactive programs.
• Processes with ps, top, htop, etc
• Memory with free, vmstat
• Networking
▶ Almost all these tools rely on the sysfs or procfs filesystem to obtain the
processes, memory and system information but will display them in a more
human-readable way.
• Networking tools use a netlink interface with the networking subsystem of the kernel.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 77/346
Linux Common Analysis & Observability Tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 78/346
Processes with ps
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 79/346
Processes with ps
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 168864 12800 ? Ss 09:08 0:00 /sbin/init
root 2 0.0 0.0 0 0 ? S 09:08 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 09:08 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 09:08 0:00 [rcu_par_gp]
root 5 0.0 0.0 0 0 ? I< 09:08 0:00 [netns]
[...]
root 914 0.0 0.0 396216 16220 ? Ssl 09:08 0:04 /usr/libexec/udisks2/udisksd
avahi 929 0.0 0.0 8728 412 ? S 09:08 0:00 avahi-daemon: chroot helper
root 956 0.0 0.1 260304 19024 ? Ssl 09:08 0:02 /usr/sbin/NetworkManager [...]
root 960 0.0 0.0 17040 5704 ? Ss 09:08 0:00 /sbin/wpa_supplicant -u [...]
root 962 0.0 0.0 317644 11896 ? Ssl 09:08 0:00 /usr/sbin/ModemManager
vnstat 987 0.0 0.0 5516 3696 ? Ss 09:08 0:00 /usr/sbin/vnstatd -n
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 80/346
Processes with top
▶ top command output information similar to ps but dynamic and interactive
(man 1 top).
• Also almost always present on embedded platforms (provided by Busybox)
$ top
top - 18:38:11 up 9:29, 1 user, load average: 2.84, 2.74, 2.02
Tasks: 371 total, 1 running, 370 sleeping, 0 stopped, 0 zombie
%Cpu(s): 5.8 us, 2.1 sy, 0.0 ni, 77.4 id, 14.7 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 15947.6 total, 1476.9 free, 7685.7 used, 6784.9 buff/cache
MiB Swap: 15259.0 total, 15238.7 free, 20.2 used. 7742.3 avail Mem
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 81/346
mpstat
$ mpstat -P ALL
Linux 6.0.0-1-amd64 (fixe) 19/10/2022 _x86_64_ (4 CPU)
17:02:50 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
17:02:50 all 6,77 0,00 2,09 11,67 0,00 0,06 0,00 0,00 0,00 79,40
17:02:50 0 6,88 0,00 1,93 8,22 0,00 0,13 0,00 0,00 0,00 82,84
17:02:50 1 4,91 0,00 1,50 8,91 0,00 0,03 0,00 0,00 0,00 84,64
17:02:50 2 6,96 0,00 1,74 7,23 0,00 0,01 0,00 0,00 0,00 84,06
17:02:50 3 9,32 0,00 2,80 54,67 0,00 0,00 0,00 0,00 0,00 33,20
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 82/346
Linux Common Analysis & Observability Tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 83/346
free
▶ free is a simple program that displays the amount of free and used memory in the
system (man 1 free).
• Useful to check if the system suffers from memory exhaustion
• Uses /proc/meminfo to obtain memory information.
$ free -h
total used free shared buff/cache available
Mem: 15Gi 7.5Gi 1.4Gi 192Mi 6.6Gi 7.5Gi
Swap: 14Gi 20Mi 14Gi
▶ A small free value does not mean that your system suffers from memory
depletion! Linux considers any unused memory as ”wasted” so it uses it for buffers
and caches to optimize performance. See also drop_caches from man 5 proc to
observe buffers/cache impact on free/available memory
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 84/346
vmstat
$ vmstat 1 6
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
3 0 253440 1237236 194936 9286980 3 6 186 540 134 157 3 5 82 10 0
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 85/346
pmap
# pmap 2002
2002: /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
...
00007f3f958bb000 56K r---- [Link].3.32.1
00007f3f958c9000 192K r-x-- [Link].3.32.1
00007f3f958f9000 84K r---- [Link].3.32.1
00007f3f9590e000 8K r---- [Link].3.32.1
00007f3f95910000 4K rw--- [Link].3.32.1
00007f3f95937000 8K rw--- [ anon ]
00007f3f95939000 8K r---- [Link].2
00007f3f9593b000 152K r-x-- [Link].2
00007f3f95961000 44K r---- [Link].2
00007f3f9596c000 8K r---- [Link].2
00007f3f9596e000 8K rw--- [Link].2
00007ffe13857000 132K rw--- [ stack ]
00007ffe13934000 16K r---- [ anon ]
00007ffe13938000 8K r-x-- [ anon ]
total 11088K
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 86/346
Linux Common Analysis & Observability Tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 87/346
iostat
$ iostat
Linux 5.19.0-2-amd64 (fixe) 11/10/2022 _x86_64_ (12 CPU)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 88/346
iotop
▶ iotop displays information about IOs much like top for each process.
▶ Useful to find applications generating too much I/O traffic.
• Needs CONFIG_TASKSTATS=y, CONFIG_TASK_DELAY_ACCT=y and
CONFIG_TASK_IO_ACCOUNTING=y to be enabled in the kernel.
• Also needs to be enabled at runtime: sysctl -w kernel.task_delayacct=1
# iotop
Total DISK READ: 20.61 K/s | Total DISK WRITE: 51.52 K/s
Current DISK READ: 20.61 K/s | Current DISK WRITE: 24.04 K/s
TID PRIO USER DISK READ DISK WRITE> COMMAND
2629 be/4 cleger 20.61 K/s 44.65 K/s firefox-esr [Cache2 I/O]
322 be/3 root 0.00 B/s 3.43 K/s [jbd2/nvme0n1p1-8]
39055 be/4 cleger 0.00 B/s 3.43 K/s firefox-esr [DOMCacheThread]
1 be/4 root 0.00 B/s 0.00 B/s init
2 be/4 root 0.00 B/s 0.00 B/s [kthreadd]
3 be/0 root 0.00 B/s 0.00 B/s [rcu_gp]
4 be/0 root 0.00 B/s 0.00 B/s [rcu_par_gp]
...
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 89/346
Linux Common Analysis & Observability Tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 90/346
ss
▶ ss shows the status of network sockets
• IPv4 and IPv6, UDP, TCP, ICMP and UNIX domain sockets
▶ Replaces netstat, now obsolete
▶ Gets info from /proc/net
▶ Usage:
ss by default shows connected sockets
ss -l shows listening sockets
ss -a shows both listening and connected sockets
ss -4/-6/-x shows only IPv4, IPv6, or UNIX sockets
ss -t/-u shows only TCP or UDP sockets
ss -p shows process using each socket
ss -n shows numeric addresses
ss -s shows a summary of existing sockets
▶ See the ss manpage for all the options
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 91/346
ss example output
# ss
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
u_dgr ESTAB 0 0 * 304840 * 26673
u_str ESTAB 0 0 /run/dbus/system_bus_socket 42871 * 26100
icmp6 UNCONN 0 0 *:ipv6-icmp *:*
udp ESTAB 0 0 [Link]%wlp0s20f3:bootpc [Link]:bootps
tcp ESTAB 0 136 [Link]:41376 [Link]:ssh
tcp ESTAB 0 273 [Link]:55494 [Link]:https
tcp ESTAB 0 0 [2a02:...:dbdc]:38466 [2001:...:9]:imap2
...
#
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 92/346
iftop
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 93/346
tcpdump
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 94/346
tcpdump example output
# tcpdump -i eth0
18:41:22.913058 IP [Link].40764 > [Link]: 14324+ AAAA? [Link]. (29)
18:41:22.913797 IP [Link] > [Link].40764: 14324 0/1/0 (89)
18:41:22.914268 IP [Link] > [Link]: ICMP echo request, id 3, seq 1, length 64
18:41:23.933063 IP [Link] > [Link]: ICMP echo request, id 3, seq 2, length 64
18:41:24.957027 IP [Link] > [Link]: ICMP echo request, id 3, seq 3, length 64
18:41:24.996415 IP [Link] > [Link]: ICMP echo reply, id 3, seq 3, length 64
^C
# tcpdump -i eth0 tcp and not port 22
... IP [Link] > A.38910: Flags [.], ack 469, win 501, options [...], length 0
... IP [Link] > A.38910: Flags [P.], seq 2602:2857, ack 469, win 501, options [...], length 255
... IP A.38910 > [Link]: Flags [.], ack 2857, win 501, options [...], length 0
... IP A.38910 > [Link]: Flags [P.], seq 469:621, ack 2857, win 501, options [...], length 152
... IP [Link] > A.38910: Flags [.], ack 621, win 501, options [...], length 0
... IP [Link] > A.38910: Flags [P.], seq 2857:3825, ack 621, win 501, options [...], length 968
... IP A.38910 > [Link]: Flags [P.], seq 621:779, ack 3825, win 501, options [...], length 158
^C
#
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 95/346
Wireshark
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 96/346
Wireshark
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 97/346
Practical lab - System Status
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 98/346
Application Debugging
Application Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 99/346
Application Debugging
Good practices
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 100/346
Good practices
▶ Some good practices can allow you to save time before even needing to use a
debugger
▶ Compiler are now smart enough to detect a wide range of errors at compile-time
using warnings
• Using -Werror -Wall -Wextra is recommended if possible to catch errors as early
as possible
▶ Compilers now offer static analysis capabilities
• GCC allows to do so using the -fanalyzer flag
• LLVM provides dedicated tools that can be used in build process
▶ You can also enable component-specific helpers/hardening
• If you are using the GNU C library, you can for example enable
_FORTIFY_SOURCE macro to add runtime checks on inputs (e.g: buffers)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 101/346
Application Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 102/346
Debugging with ELF files
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 103/346
Debugging with compiler optimizations
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 104/346
Application Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 105/346
Instrumenting code crashes
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 106/346
Custom code crash report (1/2)
[...]
void callee(void *ptr) {
int *myptr = (int *)ptr;
printf("Executing suspicious operation\n");
myptr[2] = 0;
} int main() {
const struct sigaction act = {
void caller(void) { .sa_handler = segfault_handler,
void *ptr = NULL; .sa_mask = 0,
callee(ptr); .sa_flags = 0,
} };
if (sigaction(SIGSEGV, &act, NULL))
void segfault_handler(int sig) { exit(EXIT_FAILURE);
void *array[20]; printf("Calling a faulty function\n");
size_t size; caller();
char msg[]= "Segmentation fault!\n"; return 0;
}
write(STDERR_FILENO, msg, sizeof(msg));
size = backtrace(array, 20);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 107/346
Custom code crash report (2/2)
[root@arch-bootlin-alexis custom_backtrace]# ./main
Calling a faulty function
Executing suspicious operation
Segmentation fault!
./main(segfault_handler+0x60)[0x55c6e4c1723c]
/usr/lib/[Link].6(+0x38f50)[0x7fecb0a95f50]
./main(callee+0x2b)[0x55c6e4c171b4]
./main(caller+0x1c)[0x55c6e4c171d9]
./main(main+0x2c)[0x55c6e4c1729a]
/usr/lib/[Link].6(+0x23790)[0x7fecb0a80790]
/usr/lib/[Link].6(__libc_start_main+0x8a)[0x7fecb0a8084a]
./main(_start+0x25)[0x55c6e4c170b5]
▶ Writing robust signal handlers is not easy
• When your application has received a SIGSEGV signal, its execution can not really be
trusted anymore
• We are for example supposed to use only reentrant functions in signal handlers
• Not following this rule may lead to undefined behavior
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 108/346
Application Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 109/346
ptrace
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 110/346
Application Debugging
GDB
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 111/346
GDB: GNU Project Debugger
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 113/346
GDB crash course (2/3)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 114/346
GDB crash course (3/3)
▶ continue (c)
Continue the execution after a breakpoint
▶ next (n)
Continue to the next line, stepping over function calls
▶ step (s)
Continue to the next line, entering into subfunctions
▶ stepi (si)
Continue to the next instruction
▶ finish
Execute up to function return
▶ backtrace (bt)
Display the program stack
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 115/346
GDB advanced commands (1/3)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 116/346
GDB advanced commands (2/3)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 117/346
GDB advanced commands (3/3)
▶ list <expr>
Display the source code associated to the current program counter location.
▶ disassemble <location,start_offset,end_offset> (disas)
Display the assembly code that is currently executed.
▶ print variable = value (p variable = value)
Modify the content of the specified variable with a new value
▶ p function(arguments)
Execute a function using GDB. NOTE: be careful of any side effects that may happen
when executing the function
▶ p $newvar = value
Declare a new gdb variable that can be used locally or in command sequence
▶ define <command_name>
Define a new command sequence. GDB will prompt for the sequence of commands.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 118/346
Remote debugging
▶ In a non-embedded environment, debugging takes place using gdb or one of its
front-ends.
▶ gdb has direct access to the binary and libraries compiled with debugging symbols,
which is often false for embedded systems (binaries are stripped, without
debug_info) to save storage space.
▶ For the same reason, embedding the gdb program on embedded targets is rarely
desirable (2.4 MB on x86).
▶ Remote debugging is preferred
• ARCH-linux-gdb is used on the development workstation, offering all its features.
• gdbserver is used on the target system (only 400 KB on arm).
ARCH-linux-gdb
gdbserver
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 119/346
Remote debugging: architecture
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 120/346
Remote debugging: target setup
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 121/346
Remote debugging: host setup
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 122/346
Coredumps for post mortem analysis
▶ It is sometime not possible to have a debugger attached when a crash occurs
▶ Fortunately, Linux can generate a core file (a snapshot of the whole process
memory at the moment of the crash), in the ELF format. gdb can use this core
file to let us analyze the state of the crashed application
▶ On the target
• Use ulimit -c unlimited in the shell starting the application, to enable the
generation of a core file when a crash occurs
• The output name and path for the coredump file can be modified using
/proc/sys/kernel/core_pattern (see man 5 core)
Example: echo /tmp/mycore > /proc/sys/kernel/core_pattern
• Depending on the system configuration, the core_pattern file may be rewritten
automatically by some software to handle core files or even disable core generation
(eg: systemd)
▶ On the host
• After the crash, transfer the core file from the target to the host, and run
ARCH-linux-gdb application-binary core-file
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 123/346
minicoredumper
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 124/346
GDB: going further
▶ Tutorial: Debugging Embedded Devices using GDB - Chris Simmonds, 2020
• Slides: [Link]
[Link]
• Video: [Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 125/346
GDB Python Extension
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 126/346
GDB Python Extension (1/2)
class PrintOpenFD([Link]):
def __init__(self, file):
[Link] = file
super(PrintOpenFD, self).__init__()
class PrintOpen([Link]):
def stop(self):
PrintOpenFD(gdb.parse_and_eval("file").string())
return False
TraceFDs()
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 127/346
GDB Python Extension (2/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 128/346
Common debugging issues
▶ You will likely encounter some issues while debugging, like poor address->symbols
conversion, ”optimized out” values or functions, empty backtraces...
▶ A quick checklist before starting debugging can spare you some troubles:
• Make sure your host binary has debug symbols: with gcc, ensure -g is provided, and
use non-stripped version with host gdb
• Disable optimizations on final binary (-O0) if possible, or at least use a less intrusive
level (-Og)
Static functions can for example be folded into caller depending on the optimization
level, so they would be missing from backtraces
• Prevent code optimization from reusing frame pointer register: with GCC, make sure
-fno-omit-frame-pointer option is set
Not only true for debugging: any profiling/tracing tool relying on backtraces will
benefit from it
▶ Your application is probably composed of multiple libraries: you will need to apply
those configurations on all used components!
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 129/346
Practical lab - Solving an application crash
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 130/346
Application Tracing
Application Tracing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 131/346
Application Tracing
strace
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 132/346
strace
System call tracer - [Link]
▶ Available on all GNU/Linux systems
Can be built by your cross-compiling toolchain generator or by
your build system.
▶ Allows to see what any of your processes is doing: accessing files,
allocating memory... Often sufficient to find simple bugs.
▶ Usage:
strace <command> (starting a new process)
strace -f <command> (follow child processes too)
strace -p <pid> (tracing an existing process)
strace -c <command> (time statistics per system call)
strace -e <expr> <command> (use expression for advanced
filtering)
Image credits: [Link]
See the strace manual for details
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 133/346
strace example output
> strace cat Makefile
[...]
fstat64(3, {st_mode=S_IFREG|0644, st_size=111585, ...}) = 0
mmap2(NULL, 111585, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f69000
close(3) = 0
access("/etc/[Link]", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/tls/i686/cmov/[Link].6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320h\1\0004\0\0\0\344"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1442180, ...}) = 0
mmap2(NULL, 1451632, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e06000
mprotect(0xb7f62000, 4096, PROT_NONE) = 0
mmap2(0xb7f66000, 9840, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f66000
close(3) = 0
[...]
openat(AT_FDCWD, "Makefile", O_RDONLY) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=173, ...}, AT_EMPTY_PATH) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7290d28000
read(3, "ifneq ($(KERNELRELEASE),)\nobj-m "..., 131072) = 173
write(1, "ifneq ($(KERNELRELEASE),)\nobj-m "..., 173ifneq ($(KERNELRELEASE),)
Hint: follow the open file descriptors returned by open(). This tells you what files are
handled by further system calls.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 134/346
strace filtering
▶ Display only a specific set of system calls:
▶ Trace how a file is accessed and used among different system calls
ltrace
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 136/346
ltrace
A tool to trace shared library calls used by a program and all the signals it receives
▶ Very useful complement to strace, which shows only system calls.
▶ Of course, works even if you don’t have the sources
▶ Allows to filter library calls with regular expressions, or just by a list of function
names.
▶ With the -S option it shows system calls too!
▶ Also offers a summary with its -c option.
▶ Manual page: [Link]
▶ Works better with glibc. ltrace used to be broken with uClibc (now fixed), and is
not supported with Musl (Buildroot 2022.11 status).
See [Link] for details
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 137/346
ltrace example output
# ltrace ffmpeg -f video4linux2 -video_size 544x288 -input_format mjpeg -i /dev
/video0 -pix_fmt rgb565le -f fbdev /dev/fb0
__libc_start_main([ "ffmpeg", "-f", "video4linux2", "-video_size"... ] <unfinished ...>
setvbuf(0xb6a0ec80, nil, 2, 0) = 0
av_log_set_flags(1, 0, 1, 0) = 1
strchr("f", ':') = nil
strlen("f") = 1
strncmp("f", "L", 1) = 26
strncmp("f", "h", 1) = -2
strncmp("f", "?", 1) = 39
strncmp("f", "help", 1) = -2
strncmp("f", "-help", 1) = 57
strncmp("f", "version", 1) = -16
strncmp("f", "buildconf", 1) = 4
strncmp("f", "formats", 1) = 0
strlen("formats") = 7
strncmp("f", "muxers", 1) = -7
strncmp("f", "demuxers", 1) = 2
strncmp("f", "devices", 1) = 2
strncmp("f", "codecs", 1) = 3
...
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 138/346
ltrace summary
Example summary at the end of the ltrace output (-c option)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 139/346
Application Tracing
LD_PRELOAD
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 140/346
Shared libraries
▶ Shared libraries are provided as .so files that are actually ELF files
• Loaded at startup by [Link] (the dynamic loader)
• Or at runtime using dlopen() from your code
▶ When starting a program (an ELF file actually), the kernel will parse it and load
the interpreter that needs to be invoked.
• Most of the time PT_INTERP program header of the ELF file is set to [Link].
▶ At loading time, the dynamic loader [Link] will resolve all the symbols that are
present in dynamic libraries.
▶ Shared libraries are loaded only once by the OS and then mappings are created for
each application that uses the library.
• This allows to reduce the memory used by libraries.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 141/346
Hooking Library Calls
▶ In order to do some more complex library call hooks, one can use the
LD_PRELOAD environment variable.
▶ LD_PRELOAD is used to specify a shared library that will be loaded before any
other library by the dynamic loader.
▶ Allows to intercept all library calls by preloading another library.
• Overrides libraries symbols that have the same name.
• Allows to redefine only a few specific symbols.
• ”Real” symbol can still be loaded and used with dlsym (man 3 dlsym)
▶ Used by some debugging/tracing libraries (libsegfault, libefence)
▶ Works for C and C++.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 142/346
LD_PRELOAD example 1/2
▶ Library snippet that we want to preload using LD_PRELOAD:
#include <string.h>
#include <unistd.h>
$ LD_PRELOAD=./my_lib.so ./exe
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 143/346
LD_PRELOAD example 2/2
▶ Chaining a call to the real symbol to avoid altering the application behavior:
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 144/346
Application Tracing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 145/346
Probes in linux
▶ The linux kernel is able to dynamically add some instrumentation (or ”probes”) to
almost any code running on a platform, either in userspace, kernel space, or both.
▶ This mechanism works by ”patching” the code at runtime to insert the probe.
When the patched code is executed, the probe records the execution. It can also
collect additional data.
▶ There are different kinds of probes exposed by the kernel:
• uprobes: hook on almost any userspace instruction and capture local data
• uretprobes: hook on userspace function exit and capture return value
• entry fprobe: hook on kernel function entry
• exit fprobe: hook on kernel function exit
• kprobes: hook on almost any kernel instruction and capture local data
• kretprobe: hook on kernel function exit and capture return value
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 146/346
uprobes
▶ Uprobes are wrapped by some common tools (e.g: perf, bcc) for easier usage
▶ trace/uprobetracer
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 147/346
The perf tool
▶ perf tool was started as a tool to profile application under Linux using
performance counters (man 1 perf).
▶ It became much more than that and now allows to manage tracepoints, kprobes
and uprobes.
▶ perf can profile both user-space and kernel-space execution.
▶ perf is based on the perf_event interface that is exposed by the kernel.
• Needs CONFIG_PERF_EVENTS=y at kernel build time
▶ Provides a set of operations, each having specific arguments (see perf help).
• stat, record, report, top, annotate, ftrace, list, probe, etc
▶ Some of those commands operate on an intermediate [Link], containing data
from a recording session.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 148/346
Probing userspace functions
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 149/346
Practical lab - Application tracing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 150/346
Memory Issues
Memory Issues
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 151/346
Usual Memory Issues
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 152/346
Segmentation Faults
▶ Segmentation Faults are generated by the kernel when a program tries to access a
memory area that it is not allowed to or to access it in an incorrect way
• Might be generated by a write on a read only memory zone
• Can also be triggered when trying to execute memory that is not executable
$ ./program
Segmentation fault
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 153/346
Buffer Overflows
▶ Buffer Overflows are easily triggered when accessing an array outside of its
boundaries (most often past the end)
▶ Such access might generate a crash or not depending on the access
• Writing past the end of a malloc()’ed array will most often overwrite the malloc
data structure leading to corruption
• Writing past the end of an array allocated on the stack can corrupt data on the stack
• Reading past the end of an array might generate a segfault but not always, this
depends on the area of memory that is accessed
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 154/346
Memory Leaks
▶ Memory leaks are another class of memory errors that will not directly trigger a
crash but will exhaust the system memory (sooner or later)
▶ This happens when allocating memory in your program and not releasing it after
using it
▶ Can trigger in production when the program runs for a very long time
• Better to debug that kind of problem early in the development process
void func1(void) {
uint32_t *array = malloc(10 * sizeof(*array));
do_something_with_array(array);
}
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 155/346
Memory Issues
Valgrind memcheck
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 156/346
Valgrind (1/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 157/346
Valgrind (2/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 158/346
Valgrind Memcheck usage and report
$ valgrind ./mem_leak
==202104== Memcheck, a memory error detector
==202104== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==202104== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==202104== Command: ./mem_leak
==202104==
==202104== Conditional jump or move depends on uninitialised value(s)
==202104== at 0x109161: do_actual_jump (in /home/user/mem_leak)
==202104== by 0x109187: compute_address (in /home/user/mem_leak)
==202104== by 0x1091A2: do_jump (in /home/user/mem_leak)
==202104== by 0x1091D7: main (in /home/user/mem_leak)
==202104==
==202104== HEAP SUMMARY:
==202104== in use at exit: 120 bytes in 1 blocks
==202104== total heap usage: 1 allocs, 0 frees, 120 bytes allocated
==202104==
==202104== LEAK SUMMARY:
==202104== definitely lost: 120 bytes in 1 blocks
==202104== indirectly lost: 0 bytes in 0 blocks
==202104== possibly lost: 0 bytes in 0 blocks
==202104== still reachable: 0 bytes in 0 blocks
==202104== suppressed: 0 bytes in 0 blocks
==202104== Rerun with --leak-check=full to see details of leaked memory
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 159/346
Valgrind and VGDB
▶ Valgrind can also act as a GDB server which can receive and process commands.
One can interact with valgrind gdb server either with a gdb client, or directly with
vgdb program (provided with valgrind). vgdb can be used in different ways:
• As a standalone CLI program to send ”monitor” commands to valgrind
• As a relay between a gdb client and an existing valgrind session
• As a server to drive multiple valgrind sessions from a remote gdb client
▶ See man 1 vgdb for available modes, commands and options
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 160/346
Using GDB with Memcheck
▶ valgrind allows to attach with GDB to the process that is currently analyzed.
$ gdb ./mem_leak
(gdb) target remote | vgdb
▶ If valgrind detects an error, it will stop the execution and break into GDB.
(gdb) continue
Continuing.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 161/346
Memory Issues
Electric Fence
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 162/346
libefence (1/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 163/346
libefence (2/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 164/346
Practical lab - Debugging Memory Issues
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 165/346
Application Profiling
Application Profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 166/346
Profiling
▶ Profiling is the act of gathering data from a program execution in order to analyze
them and then optimize or fix performance issues.
▶ Profiling is achieved by using programs that insert instrumentation in the code or
leverage kernel/userspace mechanisms.
• Profiling function calls and count of calls allow to optimize performance.
• Profiling processor usage allows to optimize performance and reduce power usage.
• Profiling memory usage allows to optimize memory consumption.
▶ After profiling, the data set must be analyzed to identify potential improvements
(and not the reverse!).
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 167/346
Performance issues
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 168/346
Profiling metrics
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 169/346
Application Profiling
Memory profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 170/346
Memory profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 171/346
Massif usage
▶ Massif is a tool provided by valgrind which allows to profile heap usage during the
program execution (user-space only).
▶ Works by making snapshots of allocations.
$ ms_print [Link].275099
▶ #: Peak allocation
▶ @: Detailed snapshot (count can be adjusted thanks to --detailed-freq)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 172/346
Massif report
KB
547.0^ # :: : :@ : :: :
| @:#:::::::@::::::::@
| ::@:#:::::::@::::::::@::
| :::::@:#:::::::@::::::::@:::::
| :::::::@:#:::::::@::::::::@:::::::
| :::::::@:#:::::::@::::::::@:::::::
| :::::::@:#:::::::@::::::::@:::::::
| :::::::@:#:::::::@::::::::@:::::::
| @@@@@@@@:::::::@:#:::::::@::::::::@:::::::
| @ :::::::@:#:::::::@::::::::@:::::::
| @ :::::::@:#:::::::@::::::::@:::::::
| :::::::@ :::::::@:#:::::::@::::::::@:::::::
| : @ :::::::@:#:::::::@::::::::@:::::::
| ::::::: @ :::::::@:#:::::::@::::::::@:::::::
| : : @ :::::::@:#:::::::@::::::::@:::::::
| :::::: : @ :::::::@:#:::::::@::::::::@:::::::
| : : : @ :::::::@:#:::::::@::::::::@:::::::
| ::::: : : @ :::::::@:#:::::::@::::::::@:::::::
| :::: : : : @ :::::::@:#:::::::@::::::::@:::::::
| :::: : : : : @ :::::::@:#:::::::@::::::::@:::::::
0 +----------------------------------------------------------------------->KB
0 830.5
Number of snapshots: 52
Detailed snapshots: [9, 19, 22 (peak), 32, 42]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 173/346
massif-visualizer - Visualizing massif profiling data
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 174/346
heaptrack usage
$ heaptrack program
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 175/346
heaptrack_gui - Visualizing heaptrack profiling data
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 176/346
heaptrack_gui - Flamegraph view
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 177/346
memusage
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 178/346
memusage usage
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 179/346
Application Profiling
Execution profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 180/346
Execution profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 181/346
Using perf stat
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 182/346
perf stat example (1/2)
▶ NOTE: the percentage displayed at the end denotes the time during which the
kernel measured the event due to multiplexing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 183/346
perf stat example (2/2)
$ perf list
List of pre-defined events (to be used in -e):
23 418 L1-dcache-load-misses
7 192 branch-load-misses
...
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 184/346
Cachegrind
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 185/346
Kcachegrind - Visualizing Cachegrind profiling data
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 186/346
Callgrind
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 187/346
Kcachegrind - Visualizing Callgrind profiling data
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 188/346
Practical lab - Profiling applications
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 189/346
System-wide Profiling & Tracing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 190/346
System-wide Profiling & Tracing
▶ Sometimes, the problems are not tied to an application but rather due to the
usage of multiple layers (drivers, application, kernel).
▶ In that case, it might be useful to analyze the whole stack.
▶ The kernel already includes a large number of tracepoints that can be recorded
using specific tools.
▶ New tracepoints can also be created statically or dynamically using various
mechanisms (kprobes for instance).
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 191/346
System-wide Profiling & Tracing
kprobes
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 192/346
Kprobes
▶ Allows to insert breaks at almost any kernel address dynamically and to extract
debugging and performance information
▶ Uses code patching to modify text code to insert calls to specific handlers
• kprobes allows to execute specific handlers when the hooked instruction is executed
• kretprobes will trigger when returning from a function allowing to extract the return
value of functions but also display the parameters that were used for the function call
▶ Needs some basic kernel configuration:
• CONFIG_KPROBES=y to enable general kprobe support
• CONFIG_KALLSYMS_ALL=y to allow hooking probes using <symbol_name> instead of
raw function address
• CONFIG_KPROBE_EVENTS=y to enable kprobes usage as tracing events in tracefs
▶ At the lowest level, k(ret)probes are manipulated with dedicated kernel APIs,
allowing to write our own kprobe tools (eg as kernel modules)
▶ Can also be used from userspace with /sys/kernel/tracing/kprobe_events
▶ See trace/kprobes for more information
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 193/346
Basic kprobe tracing (1/2)
▶ Add a kprobe in the same function but at a specific offset, and capture some
arguments
▶ Insert a kretprobe
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 194/346
Basic kprobe tracing (2/2)
$ cat /sys/kernel/tracing/kprobe_events
$ cat /sys/kernel/tracing/trace
▶ Delete a kprobe
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 195/346
System-wide Profiling & Tracing
perf
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 196/346
perf
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 197/346
perf example
▶ List all events that matches syscalls:*
$ perf report
Samples: 591 of event 'cycles', Event count (approx.): 393877062
Overhead Command Shared Object Symbol
22,88% firefox-esr [nvidia] [k] _nv031568rm
3,21% firefox-esr [Link].2 [.] __minimal_realloc
2,00% firefox-esr [Link].6 [.] __stpncpy_ssse3
1,86% firefox-esr [Link].0.7400.0 [.] g_hash_table_lookup
1,62% firefox-esr [Link].2 [.] _dl_strtoul
1,56% firefox-esr [[Link]] [k] clear_page_rep
1,52% firefox-esr [Link].6 [.] __strncpy_sse2_unaligned
1,37% firefox-esr [Link].2 [.] strncmp
1,30% firefox-esr firefox-esr [.] malloc
1,27% firefox-esr [Link].6 [.] __GI___strcasecmp_l_ssse3
1,23% firefox-esr [nvidia] [k] _nv013165rm
1,09% firefox-esr [nvidia] [k] _nv007298rm
1,03% firefox-esr [[Link]] [k] unmap_page_range
0,91% firefox-esr [Link].2 [.] __minimal_free
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 199/346
perf probe
▶ perf allows to create dynamic tracepoints on both kernel functions and user-space
functions.
▶ In order to be able to insert probes, CONFIG_KPROBES must be enabled in the
kernel.
• Note: libelf is required to compile perf with probe command support.
▶ New dynamic probes can be created and then used using perf record.
▶ Often on embedded platforms, vmlinux is not present on the target and thus only
symbols and registers can be used.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 200/346
perf probe examples (1/3)
▶ List all the kernel symbols that can be probed (no debug info needed):
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 201/346
perf probe examples (2/3)
$ perf script
tail 164 [000] 3552.956573: probe:do_sys_openat2: (c02c3750) filename_string="/etc/[Link]"
tail 164 [000] 3552.956642: probe:do_sys_openat2: (c02c3750) filename_string="/lib/tls/v7l/neon/vfp/[Link].2"
...
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 202/346
perf probe examples (3/3)
$ perf probe -l
probe:ksys_read__return (on ksys_read%return with ret)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 203/346
perf record example
$ perf record -a
^C
$ perf script
...
klogd 85 [000] 208.609712: 116584 cycles: b6dd551c
memset+0x2c (/lib/[Link].6)
klogd 85 [000] 208.609898: 121267 cycles: c0a44c84
_raw_spin_unlock_irq+0x34 (vmlinux)
klogd 85 [000] 208.610094: 127434 cycles: c02f3ef4
kmem_cache_alloc+0xd0 (vmlinux)
perf 130 [000] 208.610311: 132915 cycles: c0a44c84
_raw_spin_unlock_irq+0x34 (vmlinux)
perf 130 [000] 208.619831: 143834 cycles: c0a44cf4
_raw_spin_unlock_irqrestore+0x3c (vmlinux)
klogd 85 [000] 208.620048: 143834 cycles: c01a07f8
syslog_print+0x170 (vmlinux)
klogd 85 [000] 208.620241: 126328 cycles: c0100184
vector_swi+0x44 (vmlinux)
klogd 85 [000] 208.620434: 128451 cycles: c096f228
unix_dgram_sendmsg+0x46c (vmlinux)
kworker/0:2-mm_ 44 [000] 208.620653: 133104 cycles: c0a44c84 _raw_spin_unlock_irq+0x34 (vmlinux)
perf 130 [000] 208.620859: 138065 cycles: c0198460 lock_acquire+0x184 (vmlinux)
...
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 204/346
Using perf trace
▶ perf trace captures and displays all tracepoints/events that have been triggered
when executing a command
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 205/346
Using perf top
$ perf top
Samples: 19K of event 'cycles', 4000 Hz, Event count (approx.): 4571734204 lost: 0/0 drop: 0/0
Overhead Shared Object Symbol
2,01% [nvidia] [k] _nv023368rm
0,94% [kernel] [k] __static_call_text_end
0,89% [vdso] [.] 0x0000000000000655
0,81% [nvidia] [k] _nv027733rm
0,79% [kernel] [k] clear_page_rep
0,76% [kernel] [k] psi_group_change
0,70% [kernel] [k] check_preemption_disabled
0,69% code [.] 0x000000000623108f
0,60% code [.] 0x0000000006231083
0,59% [kernel] [k] preempt_count_add
0,54% [kernel] [k] module_get_kallsym
0,53% [kernel] [k] copy_user_generic_string
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 206/346
Using a GUI to display perf data
▶ perf report is the default way to display perf data, directly in the console
▶ There are also graphical tools to display perf data:
• Flamegraphs
Visualization based on hierarchical stacks
Allows to quickly find bottlenecks and explore the call stack
Popularized by Brendan Gregg tools which allows to generate flamegraphs from perf
results.
• Hotspot software
Developed and maintained by KDAB
A larger tool able to generate various types of visualizations from a [Link] file
Can also perform the actual perf recording
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 207/346
Visualizing data with flamegraphs
▶ Get the flamegraph scripts:
▶ The plates on top represent the functions sampled by perf during the recording
▶ The plates width represents how often a function has been sampled by perf
▶ The plates below represent the call stacks for the sampled functions
▶ Flamegraphs are interactive: clicking on a plate will zoom on the corresponding
callstack
▶ Colors can be tuned at flamegraph generation (eg: to get a clear split between
kernel and userspace)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 209/346
Visualizing data with hotspot (1/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 210/346
Visualizing data with hotspot (2/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 211/346
System-wide Profiling & Tracing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 212/346
ftrace
▶ ftrace is a tracing framework within the kernel which stands for ”Function Tracer”.
▶ It offers a wide range of tracing capabilities allowing to observe the system
behavior.
• Trace static tracepoints already inserted at various locations in the kernel (scheduler,
interrupts, etc).
• Relies on GCC mcount() capability and kernel code patching mechanism to call
ftrace tracing handlers.
▶ All traces are recorded in a ring buffer that is optimized for tracing.
▶ Uses tracefs filesystem to control and display tracing events.
• # mount -t tracefs nodev /sys/kernel/tracing.
▶ ftrace support must be enabled in the kernel using CONFIG_FTRACE=y.
▶ CONFIG_DYNAMIC_FTRACE allows to have a zero overhead tracing support.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 213/346
ftrace files
▶ ftrace controls are exposed through some specific files located under
/sys/kernel/tracing.
• current_tracer: Current tracer that is used.
• available_tracers: List of available tracers that are compiled in the kernel.
• tracing_on: Enable/disable tracing.
• trace: Acquired trace in human readable format. Format will differ depending on
the tracer used.
• trace_pipe: same as trace, but each read consumes the trace as it is read.
• trace_marker{_raw}: Emit comments from userspace in the trace buffer.
• set_ftrace_filter: Filter some specific functions.
• set_graph_function: Graph only the specified functions child.
▶ Many other files are exposed, see trace/ftrace.
▶ trace-cmd CLI and Kernelshark GUI tools allow to record and visualize tracing
data more easily.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 214/346
ftrace tracers
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 215/346
function_graph tracer report example
▶ The function_graph traces all the function that executed and their associated
callgraphs
▶ Will display the process, CPU, timestamp and function graph:
$ trace-cmd report
...
dd-113 [000] 304.526590: funcgraph_entry: | sys_write() {
dd-113 [000] 304.526597: funcgraph_entry: | ksys_write() {
dd-113 [000] 304.526603: funcgraph_entry: | __fdget_pos() {
dd-113 [000] 304.526609: funcgraph_entry: 6.541 us | __fget_light();
dd-113 [000] 304.526621: funcgraph_exit: + 18.500 us | }
dd-113 [000] 304.526627: funcgraph_entry: | vfs_write() {
dd-113 [000] 304.526634: funcgraph_entry: 6.334 us | rw_verify_area();
dd-113 [000] 304.526646: funcgraph_entry: 6.208 us | write_null();
dd-113 [000] 304.526658: funcgraph_entry: 6.292 us | __fsnotify_parent();
dd-113 [000] 304.526669: funcgraph_exit: + 43.042 us | }
dd-113 [000] 304.526675: funcgraph_exit: + 78.833 us | }
dd-113 [000] 304.526680: funcgraph_exit: + 91.291 us | }
dd-113 [000] 304.526689: funcgraph_entry: | sys_read() {
dd-113 [000] 304.526695: funcgraph_entry: | ksys_read() {
dd-113 [000] 304.526702: funcgraph_entry: | __fdget_pos() {
dd-113 [000] 304.526708: funcgraph_entry: 6.167 us | __fget_light();
dd-113 [000] 304.526719: funcgraph_exit: + 18.083 us | }
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 216/346
irqsoff tracer
▶ ftrace irqsoff tracer allows to trace the irqs latency due to interrupts being
disabled for too long.
▶ Helpful to find why interrupts have high latencies on a system.
▶ This tracer will record the longest trace with interrupts being disabled.
▶ This tracer needs to be enabled with CONFIG_IRQSOFF_TRACER=y.
• preemptoff, premptirqsoff tracers also exist to trace section of code were
preemption is disabled.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 217/346
irqsoff: report example
# latency: 276 us, #104/104, CPU#0 | (M:preempt VP:0, KP:0, SP:0 HP:0 #P:2)
# -----------------
# | task: stress-ng-114 (uid:0 nice:0 policy:0 rt_prio:0)
# -----------------
# => started at: __irq_usr
# => ended at: irq_exit
#
#
# _------=> CPU#
# / _-----=> irqs-off
# | / _----=> need-resched
# || / _---=> hardirq/softirq
# ||| / _--=> preempt-depth
# |||| / delay
# cmd pid ||||| time | caller
# \ / ||||| \ | /
stress-n-114 0d... 2us : __irq_usr
stress-n-114 0d... 7us : gic_handle_irq <-__irq_usr
stress-n-114 0d... 10us : __handle_domain_irq <-gic_handle_irq
...
stress-n-114 0d... 270us : __local_bh_disable_ip <-__do_softirq
stress-n-114 0d.s. 275us : __do_softirq <-irq_exit
stress-n-114 0d.s. 279us+: tracer_hardirqs_on <-irq_exit
stress-n-114 0d.s. 290us : <stack trace>
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 218/346
Hardware latency detector
▶ ftrace hwlat tracer will help to find if the hardware generates latency.
• Sytem Management interrupts for instance are non maskable and directly trigger
some firmware support feature, suspending CPU execution.
• Interrupts handled by secure monitor can also cause this kind of latency.
▶ If some latency is found with this tracer, the system is probably not suitable for
real time usage.
▶ Uses a single core looping while interrupts are disabled and measuring the time
elapsed between two consecutive time reads.
▶ Needs to be builtin the kernel with CONFIG_HWLAT_TRACER=y.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 219/346
trace-cmd
▶ trace-cmd is a tool written by Steven Rostedt which allows interacting with ftrace
(man 1 trace-cmd).
▶ The tracers supported by trace-cmd are those exposed by ftrace.
▶ trace-cmd offers multiple commands:
• list: List available plugins/events that can be recorded.
• record: Record a trace into the file [Link].
• report: Display [Link] acquisition results.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 220/346
trace-cmd examples (1/3)
▶ List available tracers
$ trace-cmd list -t
blk mmiotrace function_graph function nop
▶ List available functions for filtering with function and function_graph tracers
$ trace-cmd list -f
...
wait_for_initramfs
__ftrace_invalid_address___64
calibration_delay_done
calibrate_delay
...
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 221/346
trace-cmd examples (2/3)
▶ Start the function tracer and record data globally on the system
▶ Use the function graph tracer but filter only spi_* functions
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 222/346
trace-cmd examples (3/3)
$ trace-cmd report
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 223/346
Remote tracing with trace-cmd
▶ trace-cmd output can be quite big and thus difficult to store on an embedded
platform with limited storage.
▶ For that purpose, a listen command is available and allows sending the
acquisitions over the network:
• Run trace-cmd listen -p 6578 on the remote system that will be collecting the
traces
• On the target system, use trace-cmd record -N <target_ip>:6578 to specify the
remote system that will collect the traces
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 224/346
trace_printk()
▶ Will display the following in the trace buffer for function_graph tracer
1) | read_hw() {
1) | /* Condition is true! */
1) 2.657 us | }
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 225/346
Adding ftrace tracepoints (1/2)
▶ For some custom needs, it might be needed to add custom tracepoints
▶ First, one needs to declare the tracepoint definition in a .h file
#undef TRACE_SYSTEM
#define TRACE_SYSTEM subsys
#include <linux/tracepoint.h>
DECLARE_TRACE(subsys_eventname,
TP_PROTO(int firstarg, struct task_struct *p),
TP_ARGS(firstarg, p));
#endif /* _TRACE_SUBSYS_H */
#include <trace/events/subsys.h>
#define CREATE_TRACE_POINTS
DEFINE_TRACE(subsys_eventname);
void any_func(void)
{
...
trace_subsys_eventname(arg, task);
...
}
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 227/346
Kernelshark
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 228/346
kernelshark
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 229/346
Practical lab - System wide profiling
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 230/346
System-wide Profiling & Tracing
LTTng
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 231/346
LTTng
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 232/346
Tracepoints with LTTng
▶ LTTng works with a session daemon that receive all events from kernel and
userspace LTTng tracing components.
▶ LTTng can use and trace the following instrumentation points:
• User space LTTng tracepoints
• Linux user space probes
• Linux kernel system calls
• LTTng kernel tracepoints
• kprobes and kretprobes
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 233/346
Creating userspace tracepoints with LTTng
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 234/346
Defining a LTTng tracepoint (1/2)
// Tracepoint/event name
my_first_tracepoint,
▶ lttng-gen-tp will take this template file and generate/build all needed files (.h,
.c and .o files)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 235/346
Defining a LTTng tracepoint (2/2)
$ lttng-gen-tp hello_world-[Link]
#include <stdio.h>
#include "hello-tp.h"
▶ Compilation:
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 236/346
Using LTTng
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 237/346
Remote tracing with LTTng
$ lttng-relayd --output=${PWD}/traces
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 238/346
System-wide Profiling & Tracing
eBPF
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 239/346
The ancestor: Berkeley Packet filter
▶ BPF stands for Berkeley Packet Filter and was initially used for network packet
filtering
▶ BPF is implemented and used in Linux to perform Linux Socket Filtering (see
networking/filter)
▶ tcpdump and Wireshark heavily rely on BPF (through libpcap) for packet capture
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 240/346
BPF in libpcap: setup
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 241/346
BPF in libpcap: capture
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 242/346
eBPF (1/2)
▶ eBPF is a new framework allowing to run small user programs directly in the
kernel, in a safe and efficient way. It has been added in kernel 3.18 but it is still
evolving and receiving updates frequently.
▶ eBPF programs can capture and expose kernel data to userspace, and also alter
kernel behavior based on some user-defined rules.
▶ eBPF is event-driven: an eBPF program is triggered and executed on a specific
kernel event
▶ A major benefit from eBPF is the possibility to reprogram the kernel behavior,
without performing kernel development:
• no risk of crashing the kernel because of bugs
• faster development cycles to get a new feature ready
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 244/346
eBPF program lifecycle
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 245/346
Kernel configuration for eBPF
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 246/346
eBPF ISA
▶ eBPF is a ”virtual” ISA, defining its own set of instructions: load and store
instructions, arithmetic instructions, jump instructions,etc
▶ It also defines a set of 10 64-bits wide registers as well as a calling convention:
• R0: return value from functions and BPF program
• R1, R2, R3, R4, R5: function arguments
• R6, R7, R8, R9: callee-saved registers
• R10: stack pointer
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 247/346
The eBPF verifier
▶ When loaded into the kernel, a program must first be validated by the eBPF
verifier.
▶ The verifier is a complex piece of software which checks eBPF programs against a
set of rules to ensure that running those may not compromise the whole kernel.
For example:
• a program must always return and so not contain paths which could make them
”infinite” (e.g: no infinite loop)
• a program must make sure that a pointer is valid before dereferencing it
• a program cannot access arbitrary memory addresses, it must use passed context and
available helpers
▶ If a program violates one of the verifier rules, it will be rejected.
▶ Despite the presence of the verifier, you still need to be careful when writing
programs! eBPF programs run with preemption enabled (but CPU migration
disabled), so they can still suffer from concurrency issues
• There are mechanisms and helpers to avoid those issues, like per-CPU maps types.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 248/346
Program types and attach points
▶ There are different categories of hooks to which a program can be attached:
• an arbitrary kprobe
• a kernel-defined static tracepoint
• a specific perf event
• throughout the network stack
• an arbitrary uprobe
• and a lot more, see bpf_attach_type
▶ A specific attach-point type can only be hooked with a set of specific program
types, see bpf_prog_type and bpf/libbpf/program_types.
▶ The program type then defines the data passed to an eBPF program as input
when it is invoked. For example:
• A BPF_PROG_TYPE_TRACEPOINT program will receive a structure containing all data
returned to userspace by the targeted tracepoint.
• A BPF_PROG_TYPE_SCHED_CLS program (used to implement packet classifiers) will
receive a struct __sk_buff, the kernel representation of a socket buffer.
• You can learn about the context passed to any program type by checking
include/linux/bpf_types.h
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 249/346
eBPF maps
▶ eBPF programs exchange data with userspace or other programs through maps of
different natures:
• BPF_MAP_TYPE_ARRAY: generic array storage. Can be differentiated per CPU
• BPF_MAP_TYPE_HASH: a storage composed of key-value pairs. Keys can be of
different types: __u32, a device type, an IP address...
• BPF_MAP_TYPE_QUEUE: a FIFO-type queue
• BPF_MAP_TYPE_CGROUP_STORAGE: a specific hash map keyed by a cgroup id. There
are other types of maps specific to other object types (inodes, tasks, sockets, etc)
• etc...
▶ For basic data, it is easier and more efficient to directly use eBPF global variables
(no syscalls involved, contrary to maps)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 250/346
The bpf() syscall
▶ The kernel exposes a bpf() syscall to allow interacting with the eBPF subsystem
▶ The syscall takes a set of subcommands, and depending on the subcommand,
some specific data:
• BPF_PROG_LOAD to load a bpf program
• BPF_MAP_CREATE to allocate maps to be used by a program
• BPF_MAP_LOOKUP_ELEM to search for an entry in a map
• BPF_MAP_UPDATE_ELEM to update an entry in a map
• etc
▶ The syscall works with file descriptors pointing to eBPF resources. Those
resources (program, maps, links, etc) remain valid while there is at least one
program holding a valid file descriptor to it. Those are automatically cleaned once
there are no user left.
▶ For more details, see man 2 bpf
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 251/346
Writing eBPF programs
▶ eBPF programs can either be written directly in raw eBPF assembly or in higher
level languages (e.g: C or rust), and are compiled using the clang compiler.
▶ The kernel provides some helpers that can be called from an eBPF program:
• bpf_trace_printk Emits a log to the trace buffer
• bpf_map_{lookup,update,delete}_elem Manipulates maps
• bpf_probe_{read,write}[_user] Safely read/write data from/to kernel or
userspace
• bpf_get_current_pid_tgid Returns current Process ID and Thread group ID
• bpf_get_current_uid_gid Returns current User ID and Group ID
• bpf_get_current_comm Returns the name of the executable running in the current
task
• bpf_get_current_task Returns the current struct task_struct
• Many other helpers are available, see man 7 bpf-helpers
▶ Kernel also exposes kfuncs (see bpf/kfuncs), but contrary to bpf-helpers, those
do not belong to the kernel stable interface.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 252/346
Manipulating eBPF program
▶ There are different ways to build, load and manipulate eBPF programs:
• One way is to write an eBPF program, build it with clang, and then load it, attach it
and read data from it with bare bpf() calls in a custom userspace program
• One can also use bpftool on the built ebpf program to manipulate it (load, attach,
read maps, etc), without writing any userspace tool
• Or we can write our own eBPF tool thanks to some intermediate libraries which
handle most of the hard work, like libbpf
• We can also use specialized frameworks like BCC or bpftrace to really get all
operations (bpf program build included) handled
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 253/346
BCC
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 254/346
BCC tools
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 255/346
BCC Tools example
$ tcpconnect
PID COMM IP SADDR DADDR DPORT
220321 ssh 6 ::1 ::1 22
220321 ssh 4 [Link] [Link] 22
17676 Chrome_Child 6 2a01:cb15:81e4:8100:37cf:d45b:d87d:d97d 2606:50c0:8003::154 443
[...]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 256/346
Using BCC with python
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 257/346
Using BCC with python
▶ Hook with a kprobe on the clone() system call and display "Hello, World!"
each time it is called
#!/usr/bin/env python3
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 259/346
libbpf
▶ Instead of using a high level framework like BCC, one can use libbpf to build
custom tools with finer control over every aspect of the program.
▶ libbpf is a C-based library that aims to ease eBPF programming thanks to the
following features:
• userspace APIs to handle open/load/attach/teardown of bpf programs
• userspace APIs to interact with attached programs
• eBPF APIs to ease eBPF program writing
▶ Packaged in many distributions and build systems (e.g.: Buildroot)
▶ Learn more at [Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 260/346
eBPF programming with libbpf (1/2)
my_prog.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define TASK_COMM_LEN 16
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, __u32);
__type(value, __u64);
__uint(max_entries, 1);
} counter_map SEC(".maps");
struct sched_switch_args {
unsigned long long pad;
char prev_comm[TASK_COMM_LEN];
int prev_pid;
int prev_prio;
long long prev_state;
char next_comm[TASK_COMM_LEN];
int next_pid;
int next_prio;
};
▶ The fields to define in the *_args structure are obtained from the event
description in /sys/kernel/tracing/events (see this example)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 261/346
eBPF programming with libbpf (2/2)
my_prog.bpf.c
SEC("tracepoint/sched/sched_switch")
int sched_tracer(struct sched_switch_args *ctx)
{
__u32 key = 0;
__u64 *counter;
char *file;
return 0;
}
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 262/346
Building eBPF programs
▶ An eBPF program written in C can be built into a loadable object thanks to clang:
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 263/346
bpftool
▶ bpftool is a command line tool allowing to interact with bpf object files and the
kernel to manipulate bpf programs:
• Load programs into the kernel
• List loaded programs
• Dump program instructions, either as BPF code or JIT code
• List loaded maps
• Dump map content
• Attach programs to hooks (so they can run)
• etc
▶ You may need to mount the bpf filesystem to be able to pin a program (needed to
keep a program loaded after bpftool has finished running):
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 264/346
bpftool
$ bpftool prog
348: tracepoint name sched_tracer tag 3051de4551f07909 gpl
loaded_at 2024-08-06T15:43:11+0200 uid 0
xlated 376B jited 215B memlock 4096B map_ids 146,148
btf_id 545
$ mkdir /sys/fs/bpf/myprog
$ bpftool prog loadall trace_execve.bpf.o /sys/fs/bpf/myprog autoattach
▶ Unload a program
$ rm -rf /sys/fs/bpf/myprog
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 265/346
bpftool
▶ Dump a loaded program
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 266/346
bpftool
▶ List created maps
$ bpftool map
80: array name counter_map flags 0x0
key 4B value 8B max_entries 1 memlock 256B
btf_id 421
82: array name .rodata.str1.1 flags 0x80
key 4B value 33B max_entries 1 memlock 288B
frozen
96: array name libbpf_global flags 0x0
key 4B value 32B max_entries 1 memlock 280B
[...]
▶ We can then write our userspace program and benefit from high level APIs to
manipulate our eBPF program:
• instantiation of a global context object which will have references to all of our
programs, maps, links, etc
• loading/attaching/unloading of our programs
• eBPF program directly embedded in the generated header as a byte array
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 268/346
Userspace code with libbpf
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "trace_sched_switch.skel.h"
skel = trace_sched_switch__open_and_load();
if(!skel)
exit(EXIT_FAILURE);
if (trace_sched_switch__attach(skel)) {
trace_sched_switch__destroy(skel);
exit(EXIT_FAILURE);
}
while(true) {
bpf_map__lookup_elem(skel->maps.counter_map, &key, sizeof(key), &counter, sizeof(counter), 0);
fprintf(stderr, "Scheduling switch count: %d\n", counter);
sleep(1);
}
return 0;
}
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 269/346
eBPF programs portability (1/2)
▶ Kernel internals, contrary to userspace APIs, do not expose stable APIs. This
means that an eBPF program manipulating some kernel data may not work with
another kernel version
▶ The CO-RE (Compile Once - Run Everywhere) approach aims to solve this issue
and make programs portable between kernel versions. It relies on the following
features:
• your kernel must be built with CONFIG_DEBUG_INFO_BTF=y to have BTF data
embedded. BTF is a format similar to dwarf which encodes data layout and function
signatures in an efficient way.
• your eBPF compiler must be able to emit BTF relocations (both clang and GCC are
capable of this on recent versions, with the -g argument)
• you need a BPF loader capable of processing BPF programs based on BTF data and
adjust accordingly data access: libbpf is the de-facto standard bpf loader
• you then need eBPF APIs to read/write to CO-RE relocatable variables. libbpf
provides such helpers, like bpf_core_read
▶ To learn more, take a look at Andrii Nakryiko’s CO-RE guide
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 270/346
eBPF programs portability (2/2)
▶ Despite CO-RE, you may still face different constraints on different kernel
versions, because of major features introduction or change, since the eBPF
subsystem keeps receiving frequent updates:
• eBPF tail calls (which allow a program to call a function) have been added in
version 4.2, and allow to call another program only since version 5.10
• eBPF spin locks have been added in version 5.1 to prevent concurrent access to
maps shared between CPUs.
• Different attach types keep being added, but possibly on different kernel versions
when it depends on the architecture: fentry/fexit attach points have been added in
kernel 5.5 for x86 but in 6.0 for arm32.
• Any kind of loop (even bounded) was forbidden until version 5.3
• CAP_BPF capability, allowing a process to perform eBPF tasks, has been added in
version 5.8
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 271/346
eBPF for tracing/profiling
▶ eBPF is a very powerful framework to spy on kernel internals: thanks to the wide
variety of attach point, you can expose almost any kernel code path and data.
▶ In the meantime, eBPF programs remain isolated from kernel code, which makes
it safe (compared to kernel development) and easy to use.
▶ Thanks to the in-kernel interpreter and optimizations like JIT compilation, eBPF
is very well suited for tracing or profiling with low overhead, even in production
environments, while being very flexible.
▶ This is why eBPF adoption level keeps growing for debugging, tracing and
profiling in the Linux ecosystem. As a few examples, we find eBPF usage in:
• tracing frameworks like BCC and bpftrace
• network infrastructure components, like Cilium or Calico
• network packet tracers, like pwru or dropwatch
• And many more, check [Link] for more examples
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 272/346
eBPF: resources
▶ libbpf-bootstrap: [Link]
▶ A Beginner’s Guide to eBPF Programming - Liz Rice, 2020
• Video: [Link]
• Resources: [Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 273/346
Practical lab - Advanced eBPF development
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 274/346
System-wide Profiling & Tracing
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 275/346
Choosing the right tool
▶ Before starting to profile or trace, one should know which type of tool to use.
▶ This choice is guided by the level of profiling
▶ Often start by analyzing/optimizing the application level using application
tracing/profiling tools (valgrind, perf, etc).
▶ Then analyze user space + kernel performance
▶ Finally, trace or profile the whole system if the performance problems happens
only when running under a loaded system.
• For ”constant” load problems, snapshot tools works fine.
• For sporadic problems, record traces and analyze them.
▶ If you happen to have a complex setup that you often have to bring up, it is likely
a sign that you want to ease this setup with some custom tooling: scripting,
custom traces, eBPF, etc
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 276/346
Kernel Debugging
Kernel Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 277/346
Kernel Debugging
Preventing bugs
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 278/346
Static code analysis
▶ Static analysis can be run with the sparse tool
▶ sparse works with annotation and can detect various errors at compile time
• Locking issues (unbalanced locking)
• Address space issues, such as accessing user space pointer directly
▶ Analysis can be run using make C=2 to run only on files that are recompiled
▶ Or with make C=1 to run on all files
▶ Example of an unbalanced locking scheme:
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 279/346
Good practices in kernel development (1/2)
▶ When writing driver code, never expect the user to provide correct values. Always
check these values.
▶ Use the WARN_ON() macro if you want to display a stacktrace when a specific
condition did happen.
• dump_stack() can also be used during debugging to show the current call stack.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 280/346
Good practices in kernel development (2/2)
BUILD_BUG_ON(sizeof(ctx->__reserved) != sizeof(reserved));
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 281/346
Kernel Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 282/346
Linux Kernel Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 283/346
Kernel Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 284/346
Debugging/tracing using logs 1/4
[ 1.878382] in probe
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 285/346
Debugging/tracing using logs 2/4
▶ The pr_*() family of functions
• They include the log level in the name:
pr_emerg(), pr_alert(), pr_crit(), pr_err(), pr_warn(), pr_notice(),
pr_info(), pr_cont() and the special pr_debug() (see next pages)
• They allow setting a manual prefix (eg. eases grepping):
#define pr_fmt(fmt) "foo: " fmt
▶ Also defined in include/linux/printk.h
Example:
pr_info("in probe\n");
Here’s what you get in the kernel log:
[ 1.878382] in probe
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 287/346
Debugging/tracing using logs 4/4
▶ The kernel defines many more format specifiers than the standard printf()
existing ones.
• %p: Display the hashed value of pointer by default.
• %px: Always display the address of a pointer (use carefully on non-sensitive
addresses).
• %pK: Display hashed pointer value, zeros or the pointer address depending on
kptr_restrict sysctl value.
• %pOF: Device-tree node format specifier.
• %pr: Resource structure format specifier.
• %pa: Physical address display (work on all architectures 32/64 bits)
• %pe: Error pointer (displays the string corresponding to the error number)
▶ See core-api/printk-formats for an exhaustive list of format specifiers
▶ Also features a helper to dump entire buffers with a hexdump like display:
print_hex_dump()
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 288/346
pr_debug() and dev_dbg()
▶ When the driver is compiled with DEBUG defined, all these messages are compiled
and printed at the debug level. DEBUG can be defined by #define DEBUG at the
beginning of the driver, or using ccflags-$(CONFIG_DRIVER) += -DDEBUG in the
Makefile
▶ When the kernel is compiled with CONFIG_DYNAMIC_DEBUG, then these messages
can dynamically be enabled on a per-file, per-module or per-message basis, by
writing commands to /proc/dynamic_debug/control. Note that messages are
not enabled by default.
• Details in admin-guide/dynamic-debug-howto
• Very powerful feature to only get the debug messages you’re interested in.
▶ When neither DEBUG nor CONFIG_DYNAMIC_DEBUG are used, these messages are not
compiled in.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 289/346
pr_debug() and dev_dbg() usage
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 290/346
Debug logs troubleshooting
▶ When using dynamic debug, make sure that your debug call is enabled: it must be
visible in control file in debugfs and be activated (=p)
▶ Is your log output only in the kernel log buffer?
• You can see it thanks to dmesg
• You can lower the loglevel to output it to the console directly
• You can also set ignore_loglevel in the kernel command line to force all kernel
logs to console
▶ If you are working on an out-of-tree module, you may prefer to define DEBUG in
your module source or Makefile instead of using dynamic debug
▶ If configuration is done through the kernel command line, is it properly
interpreted?
• Starting from 5.14, kernel will let you know about faulty command line:
Unknown kernel command line parameters foo, will be passed to user
space.
• You may need to take care of special characters escaping (e.g: quotes)
▶ Be aware that a few subsystems bring their own logging infrastructure, with
specific configuration/controls, eg: [Link]=0x1ff
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 291/346
Kernel early debug
▶ When booting, the kernel sometimes crashes even before displaying the system
messages
▶ On ARM, if your kernel doesn’t boot or hangs without any message, you can
activate early debugging options
• CONFIG_DEBUG_LL=y to enable ARM early serial output capabilities
• CONFIG_EARLY_PRINTK=y will allow printk to output the prints earlier
▶ earlyprintk command line parameter should be given to enable early printk
output
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 292/346
Kernel Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 293/346
Kernel crashes
▶ The kernel is not immune to crash, many errors can be done and lead to crashes
• Memory access error (NULL pointer, out of bounds access, etc)
• Voluntarily panicking on error detection (using panic())
• Kernel incorrect execution mode (sleeping in atomic context)
• Deadlocks detected by the kernel (Soft lockup/locking problem)
▶ On error, the kernel will display a message on the console that is called a ”Kernel
oops”
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 294/346
Kernel oops (1/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 295/346
Kernel oops (2/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 296/346
Oops example (1/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 297/346
Oops example (2/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 298/346
Kernel oops debugging: addr2line
▶ In order to convert addresses/symbol name from this display to source code lines,
one can use addr2line
• addr2line -e vmlinux <address>
▶ GNU binutils >= 2.39 takes the symbol+offset notation too:
• addr2line -e vmlinux <symbol_name>+<off>
▶ The symbol+offset notation can be used with older binutils versions via the
faddr2line script in the kernel sources:
• scripts/faddr2line vmlinux <symbol_name>+<off>
▶ The kernel must have been compiled with CONFIG_DEBUG_INFO=y to embed the
debugging information into the vmlinux file.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 299/346
Kernel oops debugging: decode_stacktrace.sh
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 300/346
Panic and oops behavior configuration
▶ Sometimes, crash might be so bad that the kernel will panic and halt its execution
entirely by stopping scheduling application and staying in a busy loop.
▶ Automatic reboot on panic can be enabled via CONFIG_PANIC_TIMEOUT
• 0: never reboots
• Negative value: reboot immediately
• Positive value: seconds to wait before rebooting
▶ OOPS can be configured to always panic:
• at boot time, adding oops=panic to the command line
• at build time, setting CONFIG_PANIC_ON_OOPS=y
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 301/346
Kernel Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 302/346
Kernel memory issue debugging
▶ The same kind of memory issues that can happen in user space can be triggered
while writing kernel code
• Out of bounds accesses
• Use-after-free errors (dereferencing a pointer after kfree())
• Out of memory due to missing kfree()
▶ Various tools are present in the kernel to catch these issues
• KASAN to find use-after-free and out-of-bound memory accesses
• KFENCE to find use-after-free and out-of-bound in production systems
• Kmemleak to find memory leak due to missing free of memory
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 303/346
KASAN
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 304/346
Kmemleak
▶ Kmemleak allows to find memory leaks for dynamically allocated objects with
kmalloc()
• Works by scanning the memory to detect if allocated address are not referenced
anymore anywhere (large overhead).
▶ Once enabled with CONFIG_DEBUG_KMEMLEAK, kmemleak control files will be visible
in debugfs
▶ Memory leaks is scanned every 10 minutes
• can be disabled via CONFIG_DEBUG_KMEMLEAK_AUTO_SCAN
▶ An immediate scan can be triggered using
• # echo scan > /sys/kernel/debug/kmemleak
▶ Results are displayed in debugfs
• # cat /sys/kernel/debug/kmemleak
▶ See dev-tools/kmemleak for more information
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 305/346
Kmemleak report
# cat /sys/kernel/debug/kmemleak
unreferenced object 0x82d43100 (size 64):
comm "insmod", pid 140, jiffies 4294943424 (age 270.420s)
hex dump (first 32 bytes):
b4 bb e1 8f c8 a4 e1 8f 8c ce e1 8f 88 c6 e1 8f ................
10 a5 e1 8f 18 e2 e1 8f ac c6 e1 8f 0c c1 e1 8f ................
backtrace:
[<c31f5b59>] slab_post_alloc_hook+0xa8/0x1b8
[<c8200adb>] kmem_cache_alloc_trace+0xb8/0x104
[<1836406b>] 0x7f005038
[<89fff56d>] do_one_initcall+0x80/0x1a8
[<31d908e3>] do_init_module+0x50/0x210
[<2658dd55>] load_module+0x208c/0x211c
[<e1d48f15>] sys_finit_module+0xe4/0xf4
[<1de12529>] ret_fast_syscall+0x0/0x54
[<7ee81f34>] 0x7eca8c80
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 306/346
UBSAN
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 307/346
UBSAN: report example
▶ Report for an undefined behavior due to a shift with a value > 32.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 308/346
Debugging locking
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 309/346
Concurrency issues
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 310/346
Practical lab - Kernel debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 311/346
Kernel Debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 312/346
The Magic SysRq
Functionality provided by serial drivers
▶ Allows to run multiple debug/rescue commands even when the kernel seems to be
in deep trouble
• On embedded: in the console, send a break character
(Picocom: press [Ctrl] + a followed by [Ctrl] + \ ), then press <character>
• By echoing <character> in /proc/sysrq-trigger
▶ Example commands:
• h: show available commands
• s: sync all mounted filesystems
• b: reboot the system
• w: shows the kernel stack of all sleeping processes
• t: shows the kernel stack of all running processes
• g: enter kgdb mode
• z: flush trace buffer
• c: triggers a crash (kernel panic)
• You can even register your own!
▶ Detailed in admin-guide/sysrq
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 313/346
Kernel Debugging
KGDB
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 314/346
kgdb - A kernel debugger
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 315/346
kgdb kernel config
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 316/346
kgdb pitfalls
▶ KASLR should be disabled to avoid confusing gdb with randomized kernel
addresses
• Disable kaslr mode using nokaslr command line parameter if enabled in your kernel.
▶ Disable the platform watchdog to avoid rebooting while debugging.
• When interrupted by KGDB, all interrupts are disabled thus, the watchdog is not
serviced.
• Sometimes, watchdog is enabled by upper boot levels. Make sure to disable the
watchdog there too.
▶ Can not interrupt kernel execution from gdb using interrupt command or
Ctrl + C.
▶ Not possible to break everywhere (see CONFIG_KGDB_HONOUR_BLOCKLIST).
▶ Need a console driver with polling support.
▶ Some architecture lacks functionalities (No watchpoints on arm32 for instance)
and some instabilities might happen!
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 317/346
Using kgdb (1/2)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 318/346
Using kgdb (2/2)
▶ Then also pass kgdbwait to the kernel: it makes kgdb wait for a debugger
connection.
▶ Boot your kernel, and when the console is initialized, interrupt the kernel with a
break character and then g in the serial console (see our Magic SysRq
explanations).
▶ On your workstation, start gdb as follows:
• arm-linux-gdb ./vmlinux
• (gdb) set serial baud 115200
• (gdb) target remote /dev/ttyS0
▶ Once connected, you can debug a kernel the way you would debug an application
program.
▶ On GDB side, the first threads represent the CPU context (ShadowCPU<x>),
then all the other threads represents a task.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 319/346
Kernel GDB scripts
▶ CONFIG_GDB_SCRIPTS allows to build a set of python script which ease the kernel
debugging by adding new commands and functions.
▶ When using gdb vmlinux, the scripts present in [Link] file at the root of
build dir will be loaded automatically.
• lx-symbols: (Re)load symbols for vmlinux and modules
• lx-dmesg: display kernel dmesg
• lx-lsmod: display loaded modules
• lx-device-{bus|class|tree}: display device bus, classes and tree
• lx-ps: ps like view of tasks
• $lx_current() contains the current task_struct
• $lx_per_cpu(var, cpu) returns a per-cpu variable
• apropos lx To display all available functions.
▶ dev-tools/gdb-kernel-debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 320/346
KDB
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 321/346
KDB commands
▶ KDB does not consume gdb commands but a set of dedicated KDB commands:
• go: Continue execution
• bt: Display backtrace
• env: Show environment variables
• ps: List all tasks
• pid: Switch to another task
• md/mm: Read/write memory
• lsmod: List loaded modules
▶ To check all available commands, you can refer to the help command output, or
check maintab in kernel source code
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 322/346
kdmx
▶ When the system has only a single serial port, it is not possible to use both KGDB
and the serial line as an output terminal since only one program can access that
port.
▶ Fortunately, the kdmx tool allows to use both KGDB and serial output by splitting
GDB messages and standard console from a single port to 2 slave pty
(/dev/pts/x)
▶ [Link]
• Located in the subdirectory kdmx
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 323/346
Going further with KGDB
▶ Good presentation from Doug Anderson with a lot of demos and explanations
• Video: [Link]
• Slides: [Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 324/346
Kernel Debugging
crash
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 325/346
crash
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 326/346
crash example
crash> mach
MACHINE TYPE: armv7l
MEMORY SIZE: 512 MB
CPUS: 1
PROCESSOR SPEED: (unknown)
HZ: 100
PAGE SIZE: 4096
KERNEL VIRTUAL BASE: c0000000
KERNEL MODULES BASE: bf000000
KERNEL VMALLOC BASE: e0000000
KERNEL STACK SIZE: 8192
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 327/346
Practical lab - Kernel debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 328/346
Kernel Debugging
Post-mortem analysis
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 329/346
Kernel crash post-mortem analysis
▶ Sometimes, accessing the crashed system is not possible or the system can’t stay
offline while waiting to be debugged
▶ Kernel can generate crash dumps (a vmcore file) to a remote location, allowing to
quickly restart the system while still be able to perform post-mortem analysis with
GDB.
▶ This feature relies on kexec and kdump which will boot another kernel as soon as
the crash occurs right after dumping the vmcore file.
• The vmcore file can be saved on local storage, via SSH, FTP etc.
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 330/346
kexec & kdump (1/2)
▶ On panic, the kernel kexec support will execute a ”dump-capture kernel” directly
from the kernel that crashed
• Most of the time, a specific dump-capture kernel is compiled for that task (minimal
config with specific initramfs/initrd)
▶ kexec system works by saving some RAM for the kdump kernel execution at
startup
• crashkernel parameter should be set to specify the crash kernel dedicated physical
memory region
▶ kexec-tools are then used to load dump-capture kernel into this memory zone
using the kexec command
• Internally uses the kexec_load system call man 2 kexec_load
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 331/346
kexec & kdump (2/2)
▶ Finally, on panic, the kernel will reboot into the ”dump-capture” kernel allowing
the user to dump the kernel coredump (/proc/vmcore) onto whatever media
▶ Additional command line options depends on the architecture
▶ See admin-guide/kdump/kdump for more comprehensive explanations on how to
setup the kdump kernel with kexec.
▶ Additional user-space services and tools allow to automatically collect and dump
the vmcore file to a remote location.
• See kdump systemd service and the makedumpfile tool which can also compress the
vmcore file into a smaller file (Only for x86, PPC, IA64, S390).
• [Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 332/346
kdump
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 333/346
kexec config and setup
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 334/346
Going further with kexec & kdump
▶ Presentation from Steven Rostedt about using kexec, kdump and ftrace with lot
of tips and tricks about using kexec/kdump
• Video: [Link]
• Slides: [Link]
%20Kexec%2C%20Kdump%20and%[Link]
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 335/346
pstore (1/3)
▶ Linux provides a filesystem interface for Persistent Storage (pstore) to save data
across system resets: kernel logs, oopses, ftrace records, user messages...
▶ The platform needs to provide a persistent area to pstore (a block device, reserved
RAM which is not reset on reboot, etc). Then you can enable a pstore frontend.
▶ ramoops is a common frontend for pstore: it will log any panic/oops to a
pstore-managed ram buffer, which will be accessible on next boot
▶ Saved logs can be retrieved on next boot thanks to the pstore filesystem
▶ Some earlier software components in the boot chain (eg: U-Boot), if properly
configured, may be able to access pstore data as well
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 336/346
pstore (2/3)
▶ Kernel configuration:
• CONFIG_PSTORE=y
• CONFIG_PSTORE_RAM=y
▶ Platform configuration: reserve some memory for pstore and configure it
• Either through kernel command line:
mem=<usable_memory_size> ramoops.mem_address=0x8000000 [Link]=1
• Or through device tree:
reserved-memory {
[...]
ramoops@8f000000 {
compatible = "ramoops";
reg = <0 0x8f000000 0 0x100000>;
record-size = <0x4000>;
console-size = <0x4000>;
};
};
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 337/346
pstore (3/3)
▶ After a crash, the collected logs/traces will be available in the pstore filesystem:
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 338/346
Practical lab - Kernel debugging
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 339/346
Going further
Going further
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 340/346
Debugging resources
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 341/346
Going further (Tracing & Profiling)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 342/346
Going further (BPF)
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 343/346
Last slides
Last slides
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 344/346
Last slide
Thank you!
And may the Source be with you
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 345/346
Rights to copy
- Kernel, drivers and embedded Linux - Development, consulting, training and support - [Link] 346/346