-
Notifications
You must be signed in to change notification settings - Fork 103
Debugging and troubleshooting
If you're developing or debugging Tempesta's code (thanks for that by the way!), then you need to make several steps to simplify the process or even save your VM in a cloud. Tempesta FW is a Linux kernel extension, so the below are typical steps helping in Linux kernel development and debugging.
Instructions at the below are for Ubuntu 22.
If a kernel crash occurs, then the system may hang, so it has a sense to setup automatic reboot. Normally your Linux should reboot automatically on kernel panic. You can check it by
$ cat /proc/sys/kernel/panic
1
1
means 1 second before reboot on system panic. You can access and set the
setting by sysctl kernel.panic
. Next, you can emulate the panic by
$ echo c > /proc/sysrq-trigger
and see that the system reboots in 1 second. One more important setting is
sysctl kernel.panic_on_oops
(you can find it also in
/proc/sys/kernel/panic_on_oops
). Usually it's set to 1
, i.e. reboot on any
kernel fault ("Oops") occurred. Setting this isn't necessary, but you may prefer
to use it.
Next, you need to reboot to testing kernel but make the system reboot automatically to safe kernel if the first one occasionally crashes. You can do this using
GRUB_DEFAULT=saved
GRUB_CMDLINE_LINUX_DEFAULT="panic=1"
in /etc/default/grub
. The settings allow you to set a kernel as safe, i.e.
booted by default, and add the kernel parameter to reboot automatically in 1
second after panic. The kernel parameter automatically sets kernel.panic
sysctl. To apply the changes run
# update-grub
Now let's determine the menu and submenu IDs for grub:
$ grep 'submenu' /boot/grub/grub.cfg
submenu 'Advanced options for Ubuntu' $menuentry_id_option 'gnulinux-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
$ grep 'menuentry\>' /boot/grub/grub.cfg
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_op tion 'gnulinux-simple-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.15.0-79-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-79-generic-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.15.0-79-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-79-generic-recovery-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.15.0-78-generic' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-78-generic-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.15.0-78-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.0-78-generic-recovery-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.10.35-tfw-secperf+' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.10.35-tfw-secperf+-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.10.35-tfw-secperf+ (recovery mode)' --class ubuntu--class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.10.35-tfw-secperf+-recovery-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.10.35-tfw-secperf+.old' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.10.35-tfw-secperf+.old-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
menuentry 'Ubuntu, with Linux 5.10.35-tfw-secperf+.old (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.10.35-tfw-secperf+.old-recovery-d466b9e4-cf98-4174-a7b8-ee1a9d100741' {
To boot the Ubuntu, with Linux 5.10.35-tfw-secperf+
kernel you should specify
GRUB_DEFAULT='gnulinux-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741>gnulinux-5.10.35-tfw-secperf+-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741'
in /etc/default/grub
.
Also you can choose the default Ubuntu kernel as safe and the Tempesta kernel as testing:
$ grub-set-default 'gnulinux-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741>gnulinux-5.15.0-71-generic-advanced-660909d3-a156-4a05-9647-5d3f40a6543c'
$ grub-reboot 'gnulinux-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741>gnulinux-5.10.35-tfw-secperf+-advanced-d466b9e4-cf98-4174-a7b8-ee1a9d100741'
After the system reboot it will boot into the 5.10.35-tfw-secperf+
(Tempesta) kernel,
but if it fails the system will automatically reboot to the standard Ubuntu kernel.
It might be useful to setup automatic storing kernel crash dumps. There are plenty of good documentation about kdump & crash utility, please explore the links from references.
Just some settings for quick start and to fix known issues:
-
kernel.panic=60
in/etc/sysctl.conf
- we've found that sometimes kdump isn't in time to create a memory image and this setting fixes the problem. -
it's better to use as small
tempesta_dbmem
kernel parameter as possible, e.g.tempesta_dbmem=4
. Smaller TempestaDB memory leave more space for kdump. -
adjust
/etc/default/grub.d/kdump-tools.default
asGRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT crashkernel=2048M-:512M"
- http://people.redhat.com/anderson/crash_whitepaper/
- https://www.kernel.org/doc/Documentation/kdump/kdump.txt
- http://www.dedoimedo.com/computers/crash-book.html
It's extremely important for the kernel debugging to get output of the serial
console (dmesg
), which contains call stacks and necessary system output just
before a problem occurred.
Check that CONFIG_SERIAL_8250_CONSOLE
is enabled in your kernel config
(this is default for Ubuntu).
Add printk.synchronous=1
to the kernel cmdline to get all printk()
messages.
If you see messages like
** 5029 printk messages dropped **
in dmesg, then you typically need to set the option. This is extremely important for debug builds producing massive printing.
You also might need to update your /etc/sysctl.conf
with
kernel.printk = 8 4 1 8
to make the kernel to send even debug messages to the console.
If you use a KVM virtual machine, then it's very straightforward to enable serial console output:
- add
-serial file:serial.txt
option toqemu-system-x86_64
call so KVM will write the output of the serial console to theserial.txt
file. - add
console=tty0 console=ttyS0,115200n8
toGRUB_CMDLINE_LINUX
line in/etc/default/grub
to let the kernel forward the console output to the tty device. - update the grub as in the instructions above and reboot your VM.
If you use a VMware virtual machine:
- add
serial port
in virtual machine settings: Virtual Machine -> Settings (Ctrl+D) -> Hardware tab -> Add... -> select Serial port -> Finish -> select Use output file. Or use official documentation. - add
console=tty0 console=ttyS0,115200n8
toGRUB_CMDLINE_LINUX
line in/etc/default/grub
to let the kernel forward the console output to the tty device. You may need to use `console=tty1 console=ttyS1,115200n8'. - update the grub as in the instructions above and reboot your VM.
There might be issues with serial console redirection into a file with libvirt
of version 8 (check with libvirtd --version
) and the file isn't written at all.
In this case try to use log file
eliment as described in the
official guide,
e.g.
<serial type='pty'>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
<log file="/var/log/vm-logs/tempesta-1.1.log" append="on"/>
<alias name='serial0'/>
</serial>
Restart the libvirt services after the changes:
systemctl restart libvirtd; systemctl restart virtlogd
If you run Tempesta FW on a hardware server and do not have access to IPMI, then
a netconsole
can be used to get the output of serial console. You can use the
Ubuntu guide how to set it up.
Call traces are dumped on kernel crash and on kernel warnings. Typical trace consists of entries which looks like
do_syscall_64+0x33/0x80
Here are
- a function name (do_syscall_64 in the example),
- an offset from function beginning (0x33 in the example),
- a function length (/0x80 in the example).
The addr2line utility helps to convert this information into a source file name and line number. The call to utility is:
addr2line -e /PATH/TO/MODULE -i HEX_ADDRESS
Here:
- /PATH/TO/MODULE is a path to a module where the function is located. In case of kernel built in functions, this should be a path to the kernel. Note that the kernel should not be compressed and debug info should not be stripped from it.
- HEX_ADDRESS - an address of the instruction, '0x' prefix is required.
To convert the function name and the offset into the plain hex address,
a mix from nm
, grep
, perl
& awk
could be used, so the overal command will be in
the form
addr2line -e /PATH/TO/MODULE -i $(perl -e 'printf("%x\n", 0x'$(nm /PATH/TO/MODULE | grep '\<FUNCTION_NAME$' | awk '{print $1}')'+HEX_OFFSET)')
; for example above it will be
addr2line -e vmlinux -i $(perl -e 'printf("%x\n", 0x'$(nm vmlinux | grep '\<do_syscall_64$'| awk '{print $1}')'+0x33)')
There are scripts in linux kernel tree capable of automating this task. Those can be used like this:
linux-kernel-source/scripts/decode_stacktrace.sh linux-kernel-source/vmlinux linux-kernel-source tempesta-source/fw/tempesta_fw.ko < dmesg.log
There exist several tools to help debug memory issues.
https://www.kernel.org/doc/html/v5.10/dev-tools/kmemleak.html
Kmemleak provides a way of detecting possible kernel memory leaks in a way similar to a tracing garbage collector.
In order to use kmemleak
the kernel has to be built with the following options:
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE=16000 <- can be arbitrary
Basic flow:
-
Check if
debugfs
is already mounted. Mount if necessary:# mount -t debugfs nodev /sys/kernel/debug/
-
echo clear > /sys/kernel/debug/kmemleak
-
Run the test
-
echo scan > /sys/kernel/debug/kmemleak
If there are any hypothetical leaks, one might see the following meesage in
dmesg
:[ 1185.562872] kmemleak: 39 new suspected memory leaks (see /sys/kernel/debug/kmemleak)
-
Gather the report:
cat /sys/kernel/debug/kmemleak
Example report:
unreferenced object 0xffff88813997d040 (size 704):
comm "softirq", pid 0, jiffies 4295173707 (age 614.949s)
hex dump (first 32 bytes):
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 c0 98 99 26 81 88 ff ff ...........&....
backtrace:
[<0000000035bfc8aa>] tfw_h2_add_stream+0xbe/0x410 [tempesta_fw]
[<00000000ba7e15f7>] tfw_h2_frame_recv+0x3140/0x64f0 [tempesta_fw]
[<000000004e5a3637>] ss_skb_process+0x3a3/0x5e0 [tempesta_fw]
[<00000000aedeaca5>] tfw_h2_frame_process+0x16d/0x1200 [tempesta_fw]
[<00000000e66f86b4>] tfw_connection_recv+0x100/0x1d0 [tempesta_fw]
[<000000003ca45a93>] tfw_tls_connection_recv+0x538/0xbb0 [tempesta_fw]
[<00000000b7f25f34>] ss_tcp_process_data+0x5d9/0xd90 [tempesta_fw]
[<000000004dcf244d>] ss_tcp_data_ready+0xc4/0x1c0 [tempesta_fw]
[<00000000832ba38a>] tcp_data_queue+0x1669/0x4cb0
[<000000001c743974>] tcp_rcv_established+0x6ae/0x1d10
[<00000000a6c79ebb>] tcp_v4_do_rcv+0x4fa/0x750
[<0000000035a2b47c>] tcp_v4_rcv+0x2588/0x3530
[<00000000158df7d6>] ip_protocol_deliver_rcu+0x6a/0x550
[<0000000085c0e07f>] ip_local_deliver_finish+0x1a4/0x250
[<000000006d5a9809>] ip_local_deliver+0x246/0x2a0
[<00000000d925252d>] ip_rcv+0x15a/0x180
unreferenced object 0xffff888137f47a80 (size 704):
https://www.kernel.org/doc/html/v5.10/vm/page_owner.html
kmemleak
can't track raw pages allocations from the buddy allocator
, so page_owner
comes to rescue!
This tool is useful to track who allocated each page and can be used to find memory leaks.
The kernel has to be built with CONFIG_PAGE_OWNER=y
option and page_owner=on
should be added to boot cmdline.
- Build user space helper:
cd <linux kernel>/tools/vm && make page_owner_sort
- Collect allocation statistics before the test:
cat /sys/kernel/debug/page_owner > page_owner_full_pre.txt ./page_owner_sort page_owner_full_pre.txt sorted_page_owner_pre.txt
- Run the workload.
- Collect new statistics and compare it to the original data from above:
cat /sys/kernel/debug/page_owner > page_owner_full_post.txt ./page_owner_sort page_owner_full_post.txt sorted_page_owner_post.txt
Example output:
PRE >
57889 times:
Page allocated via order 0, mask 0x100a20(GFP_ATOMIC|__GFP_HARDWALL)
prep_new_page+0x1ad/0x250
get_page_from_freelist+0x22b1/0x2e80
__alloc_pages_nodemask+0x2b4/0x630
tfw_cache_do_action+0x2598/0x2e10 [tempesta_fw]
tfw_cache_process+0x1aa/0x7b0 [tempesta_fw]
tfw_http_req_process+0x94d/0x13c0 [tempesta_fw]
tfw_http_msg_process_generic+0x42e/0x11d0 [tempesta_fw]
tfw_h2_frame_process+0x7b0/0x11d0 [tempesta_fw]
tfw_connection_recv+0x102/0x1e0 [tempesta_fw]
tfw_tls_connection_recv+0x4d9/0xa30 [tempesta_fw]
ss_tcp_process_data+0x5ea/0xd50 [tempesta_fw]
ss_tcp_data_ready+0xb9/0x1e0 [tempesta_fw]
tcp_data_queue+0x1669/0x4cb0
tcp_rcv_established+0x6e8/0x1d70
tcp_v4_do_rcv+0x4fa/0x750
tcp_v4_rcv+0x2588/0x3530
POST >
120203 times:
Page allocated via order 0, mask 0x100a20(GFP_ATOMIC|__GFP_HARDWALL)
prep_new_page+0x1ad/0x250
get_page_from_freelist+0x22b1/0x2e80
__alloc_pages_nodemask+0x2b4/0x630
tfw_cache_do_action+0x2598/0x2e10 [tempesta_fw]
tfw_cache_process+0x1aa/0x7b0 [tempesta_fw]
tfw_http_req_process+0x94d/0x13c0 [tempesta_fw]
tfw_http_msg_process_generic+0x42e/0x11d0 [tempesta_fw]
tfw_h2_frame_process+0x7b0/0x11d0 [tempesta_fw]
tfw_connection_recv+0x102/0x1e0 [tempesta_fw]
tfw_tls_connection_recv+0x4d9/0xa30 [tempesta_fw]
ss_tcp_process_data+0x5ea/0xd50 [tempesta_fw]
ss_tcp_data_ready+0xb9/0x1e0 [tempesta_fw]
tcp_data_queue+0x1669/0x4cb0
tcp_rcv_established+0x6e8/0x1d70
tcp_v4_do_rcv+0x4fa/0x750
tcp_v4_rcv+0x2588/0x3530
https://www.kernel.org/doc/html/v5.10/dev-tools/kasan.html
KernelAddressSANitizer (KASAN) is a dynamic memory error detector designed to find out-of-bound and use-after-free bugs.
The following build options are needed to use it:
CONFIG_KASAN=y
CONFIG_KASAN_GENERIC=y
# CONFIG_KASAN_OUTLINE is not set
CONFIG_KASAN_INLINE=y <- works faster than CONFIG_KASAN_OUTLINE
CONFIG_KASAN_STACK=1
CONFIG_KASAN_VMALLOC=y <- enable this option, so KASAN would also cover vmalloc space
Usage:
- Run the desired test
- Observe reports in
dmesg
Example report:
BUG: KASAN: slab-out-of-bounds in sg_next+0x72/0x80
Read of size 8 at addr ffff8881292b50e8 by task ksoftirqd/3/29
CPU: 3 PID: 29 Comm: ksoftirqd/3 Tainted: G OE 5.10.35+ #8
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015
Call Trace:
dump_stack+0x91/0xbc
print_address_description.constprop.0+0x1c/0x210
? _raw_spin_lock_irqsave+0x7d/0xc0
? _raw_write_unlock_bh+0x60/0x60
? sg_next+0x72/0x80
kasan_report.cold+0x37/0x7c
? kasan_poison_shadow+0x10/0x30
? sg_next+0x72/0x80
sg_next+0x72/0x80
ttls_decrypt+0x12cb/0x20b0 [tempesta_tls]
? receive_buf+0xb68/0x57f0 [virtio_net]
? virtnet_poll+0x4b0/0xc81 [virtio_net]
? ttls_send_alert+0x1d0/0x1d0 [tempesta_tls]
? smpboot_thread_fn+0x2eb/0x6a0
? kthread+0x31c/0x3e0
? ret_from_fork+0x1f/0x30
? console_trylock+0xb0/0xb0
ttls_recv+0x23e/0x1e10 [tempesta_tls]
? memset+0x10/0x40
ss_skb_process+0x3a3/0x5e0 [tempesta_fw]
? ttls_handle_alert+0x1a0/0x1a0 [tempesta_tls]
? __kasan_slab_free+0x124/0x150
? tfw_sock_srv_exit+0x30/0x30 [tempesta_fw]
? _raw_spin_lock+0x6a/0xb0
? _raw_write_lock_irqsave+0xc0/0xc0
tfw_tls_connection_recv+0x17d/0xc00 [tempesta_fw]
? ss_skb_unroll+0x9b0/0xf40 [tempesta_fw]
? tfw_tls_connection_lost+0x30/0x30 [tempesta_fw]
? dynamic_emit_prefix+0x350/0x350
ss_tcp_process_data+0x5d9/0xe30 [tempesta_fw]
? ss_linkerror+0x120/0x120 [tempesta_fw]
ss_tcp_data_ready+0x13b/0x270 [tempesta_fw]
tcp_rcv_established+0x1155/0x1d10
? tcp_data_queue+0x4cb0/0x4cb0
tcp_v4_do_rcv+0x4fa/0x750
tcp_v4_rcv+0x2588/0x3530
? nf_ct_deliver_cached_events+0xe4/0x410 [nf_conntrack]
? __inet_lookup_established+0x27e/0x4d0
? tcp_v4_early_demux+0x790/0x790
ip_protocol_deliver_rcu+0x6a/0x550
? nf_confirm+0x340/0x340 [nf_conntrack]
ip_local_deliver_finish+0x1a4/0x250
ip_local_deliver+0x246/0x2a0
? ip_local_deliver_finish+0x250/0x250
? ip_rcv_finish_core.constprop.0+0x2e9/0x18a0
? ip_protocol_deliver_rcu+0x550/0x550
ip_rcv+0x15a/0x180
? ip_local_deliver+0x2a0/0x2a0
? ip_rcv_finish_core.constprop.0+0x18a0/0x18a0
? kasan_unpoison_shadow+0x34/0x40
__netif_receive_skb_core.constprop.0+0xdd9/0x3050
? get_page_from_freelist+0x1b39/0x4da0
? generic_xdp_tx+0x410/0x410
? ip_finish_output2+0x60c/0x1a60
? __zone_watermark_ok+0x420/0x420
__netif_receive_skb_list_core+0x2cf/0x8b0
? __netif_receive_skb_core.constprop.0+0x3050/0x3050
? ret_from_fork+0x1f/0x30
? ktime_get_with_offset+0xa9/0x150
netif_receive_skb_list_internal+0x5d7/0xc10
? arch_stack_walk+0x6c/0xb0
? __netif_receive_skb_list_core+0x8b0/0x8b0
? dev_gro_receive+0x229/0x2330
? memcpy+0x39/0x60
gro_normal_one+0x137/0x220
napi_gro_receive+0x23c/0x2a0
receive_buf+0xb68/0x57f0 [virtio_net]
? page_to_skb+0x9f0/0x9f0 [virtio_net]
? tcp_schedule_loss_probe.part.0+0x383/0x510
? virtqueue_get_buf_ctx_split+0x273/0x630
? _raw_spin_unlock_bh+0x20/0x20
virtnet_poll+0x4b0/0xc81 [virtio_net]
? receive_buf+0x57f0/0x57f0 [virtio_net]
net_rx_action+0x37c/0xbc0
? napi_complete_done+0x5d0/0x5d0
? sched_clock_cpu+0x18/0x160
__do_softirq+0x1a6/0x5ab
? tasklet_hi_action+0x20/0x20
run_ksoftirqd+0x26/0x40
smpboot_thread_fn+0x2eb/0x6a0
? smpboot_register_percpu_thread+0x320/0x320
? __kthread_parkme+0x85/0x100
? smpboot_register_percpu_thread+0x320/0x320
kthread+0x31c/0x3e0
? __kthread_bind_mask+0x90/0x90
ret_from_fork+0x1f/0x30
Allocated by task 29:
kasan_save_stack+0x1b/0x40
__kasan_kmalloc.constprop.0+0xc2/0xd0
ttls_decrypt+0xf6f/0x20b0 [tempesta_tls]
ttls_recv+0x23e/0x1e10 [tempesta_tls]
ss_skb_process+0x3a3/0x5e0 [tempesta_fw]
tfw_tls_connection_recv+0x17d/0xc00 [tempesta_fw]
ss_tcp_process_data+0x5d9/0xe30 [tempesta_fw]
ss_tcp_data_ready+0x13b/0x270 [tempesta_fw]
tcp_rcv_established+0x1155/0x1d10
tcp_v4_do_rcv+0x4fa/0x750
tcp_v4_rcv+0x2588/0x3530
ip_protocol_deliver_rcu+0x6a/0x550
ip_local_deliver_finish+0x1a4/0x250
ip_local_deliver+0x246/0x2a0
ip_rcv+0x15a/0x180
__netif_receive_skb_core.constprop.0+0xdd9/0x3050
__netif_receive_skb_list_core+0x2cf/0x8b0
netif_receive_skb_list_internal+0x5d7/0xc10
gro_normal_one+0x137/0x220
napi_gro_receive+0x23c/0x2a0
receive_buf+0xb68/0x57f0 [virtio_net]
virtnet_poll+0x4b0/0xc81 [virtio_net]
net_rx_action+0x37c/0xbc0
__do_softirq+0x1a6/0x5ab
The buggy address belongs to the object at ffff8881292b5000
which belongs to the cache kmalloc-256 of size 256
The buggy address is located 232 bytes inside of
256-byte region [ffff8881292b5000, ffff8881292b5100)
The buggy address belongs to the page:
page:00000000decafbbf refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1292b4
head:00000000decafbbf order:1 compound_mapcount:0
flags: 0x17ffffc0010200(slab|head)
raw: 0017ffffc0010200 0000000000000000 0000000500000001 ffff888100043400
raw: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8881292b4f80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff8881292b5000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff8881292b5080: 00 00 00 00 00 00 00 00 00 00 00 00 00 fc fc fc
^
ffff8881292b5100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff8881292b5180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
Disabling lock debugging due to kernel taint
Source code: https://sourceware.org/git/?p=systemtap.git;a=summary
SystemTap is a tracing and probing tool that allows users to study and monitor the activities of the operating system (particularly, the kernel) in fine detail
This tool is quite useful for non-intrusive debugging/tracing. One can trace nearly any part of the running kernel and/or module without any explicit modifications to the source code. Systemtap can do a lot of things ranging from simple printk()
insertion at arbitrary place in code to on-the-fly data structres modification, thus allowing to perform some sort of fault injection
to test the code.
Scripting language reference: https://sourceware.org/systemtap/langref/
Beginners guide: https://sourceware.org/systemtap/SystemTap_Beginners_Guide/
Tutorial: https://sourceware.org/systemtap/tutorial/
It is usually available as a package in your distro of choice.
- Debian/Ubuntu:
apt-get install -y systemtap
- Fedora:
dnf install systemtap
In order to perform instrumentation, systemtap needs some information about the running kernel:
- For packaged kernel, one might need to install
kernel-debuginfo kernel-debuginfo-common kernel-devel
additional packages. - If the kernel was build from source, we would need an access to the folder containing the built kernel:
f35tfw :: ~ » l /lib/modules/5.10.35perf+ total 2.6M drwxr-xr-x 4 root root 4.0K Feb 1 04:42 . drwxr-xr-x. 6 root root 4.0K Feb 1 04:20 .. lrwxrwxrwx 1 root root 27 Feb 1 04:20 build -> /root/host/linux-stable-tfw drwxr-xr-x 2 root root 4.0K Feb 1 04:42 extra drwxr-xr-x 11 root root 4.0K Feb 1 04:21 kernel -rw-r--r-- 1 root root 681K Feb 1 04:26 modules.alias -rw-r--r-- 1 root root 658K Feb 1 04:26 modules.alias.bin -rw-r--r-- 1 root root 11K Feb 1 04:20 modules.builtin -rw-r--r-- 1 root root 29K Feb 1 04:26 modules.builtin.alias.bin -rw-r--r-- 1 root root 13K Feb 1 04:26 modules.builtin.bin -rw-r--r-- 1 root root 82K Feb 1 04:20 modules.builtin.modinfo -rw-r--r-- 1 root root 161K Feb 1 04:26 modules.dep -rw-r--r-- 1 root root 254K Feb 1 04:26 modules.dep.bin -rw-r--r-- 1 root root 280 Feb 1 04:26 modules.devname -rw-r--r-- 1 root root 82K Feb 1 04:20 modules.order -rw-r--r-- 1 root root 572 Feb 1 04:26 modules.softdep -rw-r--r-- 1 root root 248K Feb 1 04:26 modules.symbols -rw-r--r-- 1 root root 300K Feb 1 04:26 modules.symbols.bin lrwxrwxrwx 1 root root 27 Feb 1 04:20 source -> /root/host/linux-stable-tfw build/source symlinks point to the sources
- In order to be able to probe the code in out-of-tree modules like
tempesta_fw
, one should create symlinks to the these modules insideextra
folder in/lib/modules/<kernel>/
. E.g:f35tfw :: ~ » l /lib/modules/5.10.35perf+/extra/ total 8.0K drwxr-xr-x 2 root root 4.0K Feb 1 04:42 . drwxr-xr-x 4 root root 4.0K Feb 1 04:42 .. lrwxrwxrwx 1 root root 42 Feb 1 04:42 tempesta_db.ko -> /root/host/tempesta/db/core/tempesta_db.ko lrwxrwxrwx 1 root root 37 Feb 1 04:42 tempesta_fw.ko -> /root/host/tempesta/fw/tempesta_fw.ko lrwxrwxrwx 1 root root 39 Feb 1 04:42 tempesta_lib.ko -> /root/host/tempesta/lib/tempesta_lib.ko lrwxrwxrwx 1 root root 39 Feb 1 04:42 tempesta_tls.ko -> /root/host/tempesta/tls/tempesta_tls.ko
Run the script: stap -g -v ~/my_script.stp
Sample output:
f35tfw :: ~ »
Pass 1: parsed user script and 500 library scripts using 227748virt/202296res/14656shr/187828data kb, in 250usr/100sys/340real ms.
Pass 2: analyzed script: 10 probes, 17 functions, 1 embed, 1 global using 692748virt/636244res/15892shr/652828data kb, in 2720usr/180sys/3164real ms.
Pass 3: translated to C into "/tmp/stapYCj74o/stap_066522e114f6510efc1fcfdfd1c92da5_8418_src.c" using 692748virt/636432res/16080shr/652828data kb, in 550usr/20sys/572real ms.
Pass 4: compiled C into "stap_066522e114f6510efc1fcfdfd1c92da5_8418.ko" in 1280usr/370sys/1958real ms.
Pass 5: starting run. <--- success!
Here are some simple examples of what can be done inside the script.
- Print
softIRQ
NET_TX_ACTION / NET_RX_ACTION
invocations count every 1s on VM with 4 vCPUs.
f35tfw :: ~ » cat tfw_softirq_rx2_tx3.stp
global t_si_arr_rx[4]
global t_si_arr_tx[4]
probe timer.ms(1000) {
printf("CPU: [0] [1] [2] [3] \n")
printf("==========================================================================\n")
printf("RX => %d |%d |%d |%d\n",
t_si_arr_rx[0], t_si_arr_rx[1], t_si_arr_rx[2], t_si_arr_rx[3])
printf("--------------------------------------------------------------------------\n")
printf("TX => %d |%d |%d |%d\n",
t_si_arr_tx[0], t_si_arr_tx[1], t_si_arr_tx[2], t_si_arr_tx[3])
printf("\n")
}
probe kernel.statement("__do_softirq@kernel/softirq.c:303") {
if ($vec_nr == 3) {
t_si_arr_tx[cpu()] ++
}
if ($vec_nr == 2) {
t_si_arr_rx[cpu()] ++
}
}
- A little bit more complicated example, which uses embedded C code to dump
sk_buff
.skb_dump()
function has been 'enhanced' and several probes are attached in the datapath.
%{
#include <include/net/sock.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/smp.h>
%}
function tfw_tls_skb_type:long (sk_buff:long)
{
dev = @cast(sk_buff, "sk_buff")->dev
if (!(dev & 1))
return 0
return dev >> 1
}
function should_check_skb:long (sk:long, sk_buff:long)
{
return (@cast(sk, "sock")->sk_write_xmit != NULL) && tfw_tls_skb_type(sk_buff)
}
function skb_dump_msg(sk:long, sk_buff:long, msg:string, full:long)
%{
pr_warn("[%d] sk %px skb %px: %s\n", smp_processor_id(),
(struct sock *)STAP_ARG_sk, (struct sk_buff *)STAP_ARG_sk_buff,
(const char *)STAP_ARG_msg);
skb_dump(KERN_WARNING, (struct sk_buff *)STAP_ARG_sk_buff,
(long)STAP_ARG_full ? true : false);
%}
function __tcp_dump(sk:long, sk_buff:long, lim:long, msg:string)
%{
pr_warn("[%d] %s sk %px skb %px limit %lu\n", smp_processor_id(), (const char*)STAP_ARG_msg,
(struct sock *)STAP_ARG_sk, (struct sk_buff *)STAP_ARG_sk_buff,
(long)STAP_ARG_lim);
%}
function __tcp_tskb_info(sk:long, sk_buff:long, thdr_size:long, topt_size:long)
%{
pr_warn("[%d] sk %px skb %px thdr_size %lu topt_size %lu\n", smp_processor_id(),
(struct sock *)STAP_ARG_sk, (struct sk_buff *)STAP_ARG_sk_buff,
(long)STAP_ARG_thdr_size, (long)STAP_ARG_topt_size);
%}
function __skb_dump_ext(sk_buff:long, msg:string, full:long)
%{
bool full = (bool)STAP_ARG_full;
struct sk_buff *skb = (struct sk_buff *)STAP_ARG_sk_buff;
struct skb_shared_info *sh = skb_shinfo(skb);
struct net_device *dev = skb->dev;
struct sock *sk = skb->sk;
struct sk_buff *list_skb;
bool has_mac, has_trans;
int headroom, tailroom;
int i, len, seg_len;
if (full)
len = skb->len;
else
len = min_t(int, skb->len, MAX_HEADER + 128);
headroom = skb_headroom(skb);
tailroom = skb_tailroom(skb);
has_mac = skb_mac_header_was_set(skb);
has_trans = skb_transport_header_was_set(skb);
pr_warn("[%d] %s skb %px\n", smp_processor_id(),
(const char *)STAP_ARG_msg, (struct sk_buff *)STAP_ARG_sk_buff);
printk("%sskb len=%u headroom=%u headlen=%u tailroom=%u\n"
"mac=(%d,%d) net=(%d,%d) trans=%d\n"
"shinfo(txflags=%u nr_frags=%u gso(size=%hu type=%u segs=%hu))\n"
"csum(0x%x ip_summed=%u complete_sw=%u valid=%u level=%u)\n"
"hash(0x%x sw=%u l4=%u) proto=0x%04x pkttype=%u iif=%d\n",
KERN_WARNING, skb->len, headroom, skb_headlen(skb), tailroom,
has_mac ? skb->mac_header : -1,
has_mac ? skb_mac_header_len(skb) : -1,
skb->network_header,
has_trans ? skb_network_header_len(skb) : -1,
has_trans ? skb->transport_header : -1,
sh->tx_flags, sh->nr_frags,
sh->gso_size, sh->gso_type, sh->gso_segs,
skb->csum, skb->ip_summed, skb->csum_complete_sw,
skb->csum_valid, skb->csum_level,
skb->hash, skb->sw_hash, skb->l4_hash,
ntohs(skb->protocol), skb->pkt_type, skb->skb_iif);
if (dev)
printk("%sdev name=%s feat=0x%pNF\n",
KERN_WARNING, dev->name, &dev->features);
if (sk)
printk("%ssk family=%hu type=%u proto=%u\n",
KERN_WARNING, sk->sk_family, sk->sk_type, sk->sk_protocol);
if (full && headroom)
print_hex_dump(KERN_WARNING, "skb headroom: ", DUMP_PREFIX_OFFSET,
16, 1, skb->head, headroom, true);
seg_len = min_t(int, skb_headlen(skb), len);
if (seg_len)
print_hex_dump(KERN_WARNING, "skb linear: ", DUMP_PREFIX_OFFSET,
16, 1, skb->data, seg_len, true);
len -= seg_len;
if (full && tailroom)
print_hex_dump(KERN_WARNING, "skb tailroom: ", DUMP_PREFIX_OFFSET,
16, 1, skb_tail_pointer(skb), tailroom, true);
for (i = 0; len && i < skb_shinfo(skb)->nr_frags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
u32 p_off, p_len, copied;
struct page *p;
u8 *vaddr;
skb_frag_foreach_page(frag, skb_frag_off(frag),
skb_frag_size(frag), p, p_off, p_len,
copied) {
seg_len = min_t(int, p_len, len);
vaddr = kmap_atomic(p);
print_hex_dump(KERN_WARNING, "skb frag: ",
DUMP_PREFIX_OFFSET,
16, 1, vaddr + p_off, seg_len, true);
kunmap_atomic(vaddr);
len -= seg_len;
if (!len)
break;
}
}
if (full && skb_has_frag_list(skb)) {
printk("skb fraglist:\n");
skb_walk_frags(skb, list_skb)
skb_dump(KERN_WARNING, list_skb, true);
}
%}
probe kernel.statement("tcp_write_xmit@net/ipv4/tcp_output.c:2663") {
if (should_check_skb($sk, $skb)) {
__skb_dump_ext($skb, "RIGHT AFTER WHILE", 1)
}
}
probe kernel.statement("tcp_write_xmit@net/ipv4/tcp_output.c:2696") {
if (should_check_skb($sk, $skb))
__tcp_dump($sk, $skb, $limit, "FIRST LIMIT")
}
probe kernel.statement("tcp_write_xmit@net/ipv4/tcp_output.c:2703") {
if (should_check_skb($sk, $skb))
__tcp_dump($sk, $skb, $limit, "SECOND LIMIT")
}
probe kernel.statement("tcp_write_xmit@net/ipv4/tcp_output.c:2716") {
if (should_check_skb($sk, $skb)) {
__tcp_dump($sk, $skb, $limit, "AFTER ADJUST")
__skb_dump_ext($skb, "BEFORE FRAGMENT", 1)
}
}
probe kernel.statement("tcp_write_xmit@net/ipv4/tcp_output.c:2744") {
__skb_dump_ext($skb, "BEFORE ENCRYPT", 1)
}
probe kernel.statement("tcp_write_xmit@net/ipv4/tcp_output.c:2745") {
skb_dump_msg($sk, $skb, "AFTER ENCRYPT", 1)
/* __skb_dump_ext($skb, "AFTER ENCRYPT", 1) */
}
probe kernel.statement("__tcp_transmit_skb@net/ipv4/tcp_output.c:1259") {
skb_dump_msg($sk, $skb, "OSKB PRE CLONE", 1)
/* __skb_dump_ext($skb, "OSKB PRE CLONE", 1) */
}
probe kernel.statement("__tcp_transmit_skb@net/ipv4/tcp_output.c:1312") {
__tcp_tskb_info($sk, $skb, $tcp_header_size, $tcp_options_size)
skb_dump_msg($sk, $skb, "SKB AFTER CLONE", 1)
/* __skb_dump_ext($skb, "SKB AFTER CLONE", 1) */
}
probe kernel.function("skb_panic") {
__skb_dump_ext($skb, "PANIC SKB", 0)
}
function __skb_alloc_trace(sk_buff:long)
%{
pr_warn("[%d] got new skb %px\n", smp_processor_id(), (struct sk_buff *)STAP_ARG_sk_buff);
dump_stack();
%}
probe module("tempesta_fw").statement("tfw_h2_resp_adjust_fwd@fw/http.c:5367") {
skb_dump_msg(NULL, $sk_head, "BEFORE MSG TRANS", 1)
}
Tempesta FW uses fault injection infrastructure provided by linux kernel (https://docs.kernel.org/fault-injection/fault-injection.html). We use error injection to make tests for some difficult places in source code. How to use:
- Linux kernel should be build with CONFIG_BPF, CONFIG_BPF_KPROBE_OVERRIDE, CONFIG_FUNCTION_ERROR_INJECTION, CONFIG_FAULT_INJECTION, CONFIG_FAULT_INJECTION_DEBUG_FS and CONFIG_FAIL_FUNCTION.
- Add macro ALLOW_ERROR_INJECTION(function_name, error_code) after function definition that should return an error.
- Implement test based on one of the tests from t_fault_injection/test_fault_injection_base.py
- Home
- Requirements
- Installation
-
Configuration
- Migration from Nginx
- On-the-fly reconfiguration
- Handling clients
- Backend servers
- Load Balancing
- Caching Responses
- Non-Idempotent Requests
- Modify HTTP Messages
- Virtual hosts and locations
- HTTP Session Management
- HTTP Tables
- HTTP(S) Security
- Header Via
- Health monitor
- TLS
- Virtual host confusion
- Traffic Filtering by Fingerprints
- Run & Stop
- Application Performance Monitoring
- Use cases
- Performance
- Contributing