1. 简介
要使用ftrace进行boottime追踪,可以使用内核参数(bootargs)或者bootconfig来进行配置。
关键的函数如下:
1+-- start_kernel
2/// ... ...
3| +-- setup_arch
4| +-- setup_boot_config
5| | +-- get_boot_config_from_initrd
6| | +-- xbc_get_embedded_bootconfig
7| | +-- xbc_init /// 解析bootconfig,创建树
8| +-- setup_command_line
9/// ... ...
10| +-- parse_early_param
11| +-- parse_args /// 解析传入的ftrace等启动参数
12| +-- random_init_early
13| +-- setup_log_buf
14| +-- vfs_caches_init_early
15| +-- sort_main_extable
16| +-- trap_init
17| +-- mm_core_init
18| +-- poking_init
19| +-- ftrace_init
20| | +-- set_ftrace_early_filters /// 根据bootargs,设置function tracer和function_graph tracer要追踪的函数,event等
21| +-- early_trace_init /* trace_printk can be enabled here */
22| | +-- tracer_alloc_buffers
23| | | +-- init_function_trace /* Function tracing may start here (via kernel command line) */
24| | +-- init_events
25| +-- sched_init /// 在function tracer下,这是第一个可以被ftrace追踪的函数
26/// ... ...
27| +-- trace_init
28| | +-- trace_event_init
29| | +-- enable_instances /// if (boot_instance_index)
后续操作使用qemu模拟ARM64环境,可以参考https://gitee.com/kingdix10/eel。
2. bootargs方式
u-boot
可以通过bootargs
环境变量或者设备树向内核传递参数,qemu
则可以使用-append
参数向内核传递参数。
2.1. function tracer
在early_trace_init -> tracer_alloc_buffers -> init_function_trace
后,function tracer
就可以开始根据bootargs
中的ftrace_filter
参数,设置追踪的函数。
qemu
启动时传递给内核的启动参数,具体参考https://gitee.com/kingdix10/eel/blob/main/configs/kl_args.mk
1KL_ARGS += trace_options=sym-addr,print-parent trace_event=initcall:* trace_buf_size=1M
2# tracer: function
3KL_ARGS += ftrace=function ftrace_filter="early_trace_init,sched_init,*_rootfs*,*mount*"
启动qemu
。
1make qemu-kernel FTRACE=1
启动完成后,可以使用trace-cmd show
或者cat /sys/kernel/debug/tracing/trace
查看追踪结果。结果内容较多,可以将输出重定向到文件查看。
为了方便查看,这里为ftrace_filter
指定了sched_init
,可以看到输出结果的第一行就是sched_init
。
1# tracer: function
2#
3# entries-in-buffer/entries-written: 2032/2032 #P:2
4#
5# _-----=> irqs-off/BH-disabled
6# / _----=> need-resched
7# | / _---=> hardirq/softirq
8# || / _--=> preempt-depth
9# ||| / _-=> migrate-disable
10# |||| / delay
11# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
12# | | | ||||| | |
13 <idle>-0 [000] dp.2. 0.000000: sched_init <ffff80008145ec8c> <-start_kernel <ffff800081451270>
14 <idle>-0 [000] ...1. 0.011784: initcall_level: level=console
15 <idle>-0 [000] ...1. 0.012104: initcall_start: func=con_init+0x0/0x220
16 <idle>-0 [000] ...1. 0.014390: initcall_finish: func=con_init+0x0/0x220 ret=0
17/// ... ...
2.2. function graph tracer
function graph tracer
依赖init_graph_trace
进行初始化。
1/// kernel/trace/trace_functions_graph.c
2core_initcall(init_graph_trace)
而core_initcall
的处理是在1号进程中进行的,所以function graph tracer
可以追踪的时机要比function tracer
晚。
1+-- kernel_init
2| +-- kernel_init_freeable
3| | +-- do_basic_setup
4| | | +-- do_initcalls
类似function tracer
,具体参考https://gitee.com/kingdix10/eel/blob/main/configs/kl_args.mk
1KL_ARGS += trace_options=sym-addr,print-parent trace_event=initcall:* trace_buf_size=1M
2# tracer: function_graph
3KL_ARGS += ftrace=function_graph ftrace_graph_max_depth=5 ftrace_graph_filter="sched_init,*_rootfs*,*mount*" ftrace_graph_notrace="*_*lock"
启动qemu
。
1make qemu-kernel FTRACE=1
同样使用trace-cmd show
或者cat /sys/kernel/debug/tracing/trace
查看追踪结果。
注意initcall_finish: func=init_graph_trace
,这说明在init_graph_trace
完成后,function graph tracer
就可以开始追踪了。
1# tracer: function_graph
2#
3# CPU DURATION FUNCTION CALLS
4# | | | | | | |
5 0) | /* initcall_finish: func=init_graph_trace+0x0/0x88 ret=0 */
6 0) | /* initcall_start: func=trace_events_eprobe_init_early+0x0/0x48 */
7 0) | /* initcall_finish: func=trace_events_eprobe_init_early+0x0/0x48 ret=0 */
8 0) | /* initcall_start: func=trace_events_synth_init_early+0x0/0x48 */
9 0) | /* initcall_finish: func=trace_events_synth_init_early+0x0/0x48 ret=0 */
10/// ... ...
11 0) | /* initcall_start: func=init_elf_binfmt+0x0/0x40 */
12 0) | /* initcall_finish: func=init_elf_binfmt+0x0/0x40 ret=0 */
13 0) | /* initcall_start: func=init_compat_elf_binfmt+0x0/0x40 */
14 0) | /* initcall_finish: func=init_compat_elf_binfmt+0x0/0x40 ret=0 */
15 0) | /* initcall_start: func=configfs_init+0x0/0xd0 */
16 0) | sysfs_create_mount_point() {
17 0) | irq_enter_rcu() {
18 0) 4.640 us | preempt_count_add();
19/// ... ...
3. bootconfig方式
由于bootargs不足以控制ftrace复杂的功能,内核使用bootconfig
扩展了现有的内核命令行,用以跟踪功能编程。具体查看《参考资料》部分的文档或链接。
在do_initcalls
中,core_initcall_sync
是在core_initcall
之后执行的,bootconfig
方式可以追踪的时机要比function graph tracer
更晚。
1/// kernel/trace/trace_boot.c
2core_initcall_sync(trace_boot_init)
3.1. bootrace.bconf
以下例子来自于tools/testing/ktest/examples/bootconfigs/boottrace.bconf,将ftrace_filter
修改为sched_init,*mount*
。
1kernel {
2 trace_options = sym-addr
3 trace_event = "initcall:*"
4 trace_buf_size = 1M
5 ftrace = function
6 ftrace_filter = "sched_init,*mount*"
7}
3.2. initrd
目前bootconfig只支持追加到initrd
镜像之后或者与内核镜像编译到一起,这里使用initrd
来实现。
同样使用https://gitee.com/kingdix10/eel。
为initrd添加或修改bootconfig
,可以使用make initrd-addbconf
,输出如下:
1/data/eel/source/kernel/linux-6.6/tools/bootconfig/bootconfig -a presets/boottrace.bconf \
2 /data/eel/output/arm64/rootfs/buildroot-2024.02/mine_arm64_defconfig/images/rootfs.cpio
3Apply presets/boottrace.bconf to /data/eel/output/arm64/rootfs/buildroot-2024.02/mine_arm64_defconfig/images/rootfs.cpio
4 Number of nodes: 91
5 Size: 1045 bytes
6 Checksum: 85503
查看initrd的bootconfig
,可以使用make initrd-listbconf
,输出如下:
1/data/eel/source/kernel/linux-6.6/tools/bootconfig/bootconfig -l \
2 /data/eel/output/arm64/rootfs/buildroot-2024.02/mine_arm64_defconfig/images/rootfs.cpio
3ftrace.event.task.task_newtask.filter = "pid < 128"
4ftrace.event.task.task_newtask.enable = ""
5ftrace.event.kprobes.vfs_read.probes = "vfs_read $arg1 $arg2"
6ftrace.event.kprobes.vfs_read.filter = "common_pid < 200"
7ftrace.event.kprobes.vfs_read.enable = ""
8... ...
删除bootconfig
,可以使用make initrd-delbconf
。
1/data/eel/source/kernel/linux-6.6/tools/bootconfig/bootconfig -d \
2 /data/eel/output/arm64/rootfs/buildroot-2024.02/mine_arm64_defconfig/images/rootfs.cpio
3.3. qemu启动
为initrd增加bootconfig后,就可以启动qemu了。与bootargs
方式不同,这里使用如下命令启动qemu
。
1make qemu-kernel-initrd FTRACE=1
可以使用make qemu-kernel-initrd FTRACE=1 -n
来查看实际使用的命令,如下:
1qemu-system-aarch64 \
2 -nographic -smp 2 -m 1024 -cpu cortex-a57 \
3 -M type=virt,mte=off,virtualization=false,gic-version=3 \
4 \
5 -semihosting -semihosting-config enable=on,target=native -netdev user,id=net0 -device virtio-net-device,netdev=net0 -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0,max-bytes=1024,period=1000 -fsdev local,id=share_comm,path=/data/eel/share,security_model=none -device virtio-9p-device,fsdev=share_comm,mount_tag=mnt_comm -fsdev local,id=share_arch,path=/data/eel/output/arm64,security_model=none -device virtio-9p-device,fsdev=share_arch,mount_tag=mnt_arch -fsdev local,id=share_eel,path=/data/eel,security_model=passthrough,readonly=on -device virtio-9p-device,fsdev=share_eel,mount_tag=mnt_eel \
6 \
7 -kernel /data/eel/output/arm64/kernel/linux-6.6/arm64_debug_defconfig/arch/arm64/boot/Image \
8 -append "console=ttyAMA0 nokaslr crashkernel=512M-1G:64M,1G-:128M bootconfig trace_options=sym-addr,print-parent trace_event=initcall:* trace_buf_size=1M ftrace=function_graph ftrace_graph_max_depth=5 ftrace_graph_filter="sched_init,*_rootfs*,*mount*" ftrace_graph_notrace="*_*lock" loglevel=6 initcall_debug no_console_suspend root=/dev/vda rw init=/linuxrc" \
9 -initrd /data/eel/output/arm64/rootfs/buildroot-2024.02/mine_arm64_defconfig/images/rootfs.cpio
同样使用trace-cmd show
或者cat /sys/kernel/debug/tracing/trace
查看追踪结果。
4. 其他说明
4.1. 根文件系统
使用bootargs,根文件系统默认使用rootfs.ext4
。
使用bootconfig,根文件系统默认使用添加bootconfig
的rootfs.cpio
。
4.2. trace buffer
trace_buf_size
是一个有限的值,如果启动过程中记录的信息过多,可能导致trace buffer
回滚,丢失开始的记录。
这里在init
进程启动后,关闭了trace,防止trace buffer
回滚。见https://gitee.com/kingdix10/eel/blob/main/share/init.sh
1# disable boottime trace, avoid trace buffer overflow
2echo 0 > /sys/kernel/debug/tracing/tracing_on