You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用BPF/eBPF追踪调用栈时用户态函数显示[unknown]的原因排查

实验环境

┌──[root@vms99.liruilongs.github.io]-[/usr/share/bcc/tools]
└─$hostnamectl
   Static hostname: vms99.liruilongs.github.io
         Icon name: computer-vm
           Chassis: vm
        Machine ID: ea70bf6266cb413c84266d4153276342
           Boot ID: 0d01838b0095494c82d1befb174a317d
    Virtualization: vmware
  Operating System: Rocky Linux 8.9 (Green Obsidian)
       CPE OS Name: cpe:/o:rocky:rocky:8:GA
            Kernel: Linux 4.18.0-513.9.1.el8_9.x86_64
      Architecture: x86-64
┌──[root@vms99.liruilongs.github.io]-[/usr/share/bcc/tools]
└─$

问题描述

使用BPF/eBPF工具追踪调用栈时,所有用户态函数均显示为[unknown]。执行profile工具的输出如下:

┌──[root@vms99.liruilongs.github.io]-[/usr/share/bcc/tools]
└─$profile
Sampling at 49 Hertz of all threads by user + kernel stack... Hit Ctrl-C to end.
^C
    _raw_spin_unlock_irqrestore
    _raw_spin_unlock_irqrestore
    prepare_to_swait_event
    rcu_gp_kthread
    kthread
    ret_from_fork
    -                rcu_sched (14)
        1

    kmem_cache_alloc_node
    kmem_cache_alloc_node
    __alloc_skb
    __ip_append_data.isra.50
    ip_append_data.part.51
    ip_send_unicast_reply
    tcp_v4_send_reset
    tcp_v4_rcv
    ip_protocol_deliver_rcu
    ip_local_deliver_finish
    ip_local_deliver
    ip_rcv
    __netif_receive_skb_core
    process_backlog
    __napi_poll
    net_rx_action
    __softirqentry_text_start
    do_softirq_own_stack
    do_softirq.part.16
    __local_bh_enable_ip
    ip_finish_output2
    ip_output
    __ip_queue_xmit
    __tcp_transmit_skb
    tcp_connect
    tcp_v4_connect
    __inet_stream_connect
    inet_stream_connect
    __sys_connect
    __x64_sys_connect
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    [unknown]
    -                haproxy (1203)
        1

    show_vma_header_prefix
    show_vma_header_prefix
    show_map_vma
    show_map
    seq_read
    vfs_read
    ksys_read
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    [unknown]
    [unknown]
    -                awk (39726)
        1
.............        
┌──[root@vms99.liruilongs.github.io]-[/usr/share/bcc/tools]
└─$

请问该现象是程序缺少调试信息导致,还是有其他原因?

另外,用Python编写的锁演示程序也出现同样问题:

┌──[root@vms99.liruilongs.github.io]-[~]
└─$cat lock_demo.py
import threading
import time

lock = threading.Lock()

def worker(id):
    print(f"Worker {id} started")
    with lock:
        print(f"Worker {id} acquired lock")
        time.sleep(2)  # 模拟长时间的计算或 I/O
    print(f"Worker {id} released lock")

threads = []
for i in range(5):
    t = threading.Thread(target=worker, args=(i,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print("All workers finished")

执行threadsnoop工具的输出:

┌──[root@vms99.liruilongs.github.io]-[~]
└─$threadsnoop
TIME(ms)   PID     COMM             FUNC
0          51671   b'python3'       b'[unknown]'
0          51671   b'python3'       b'[unknown]'
0          51671   b'python3'       b'[unknown]'
0          51671   b'python3'       b'[unknown]'
0          51671   b'python3'       b'[unknown]'

执行offcputime工具的输出:

┌──[root@vms99.liruilongs.github.io]-[~/FlameGraph]
└─$offcputime  -p `pgrep -f lock_demo.py`
Tracing off-CPU time (us) of PID 51397 by user + kernel stack... Hit Ctrl-C to end.
^C
    .......................
    finish_task_switch
    __sched_text_start
    schedule
    futex_wait_queue_me
    futex_wait
    do_futex
    __x64_sys_futex
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    [unknown]
    -                python3 (51402)
        157

    finish_task_switch
    __sched_text_start
    schedule
    futex_wait_queue_me
    futex_wait
    do_futex
    __x64_sys_futex
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    [unknown]
    -                python3 (51400)
        213

    finish_task_switch
    __sched_text_start
    schedule
    futex_wait_queue_me
    futex_wait
    do_futex
    __x64_sys_futex
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    [unknown]
    -                python3 (51397)
        267

    finish_task_switch
    __sched_text_start
    schedule
    do_nanosleep
    hrtimer_nanosleep
    common_nsleep_timens
    __x64_sys_clock_nanosleep
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    [unknown]
    [unknown]
    -                python3 (51400)
        2002609

    finish_task_switch
    __sched_text_start
    schedule
    do_nanosleep
    hrtimer_nanosleep
    common_nsleep_timens
    __x64_sys_clock_nanosleep
    do_syscall_64
    entry_SYSCALL_64_after_hwframe
    [unknown]
    [unknown]
    -                python3 (51402)
        2003178
..................
┌──[root@vms99.liruilongs.github.io]-[~/FlameGraph]
└─$

原因分析

这种用户态函数显示为[unknown]的情况,通常由以下几种原因导致:

  • 程序缺少符号表或调试信息
    大部分发行版的默认软件包(比如haproxy、python)都是经过stripped处理的,去掉了符号表和调试信息,导致eBPF工具无法将内存地址解析为对应的函数名。对于Python这类解释型语言,本身的字节码运行在解释器上,用户态的函数实际是Python代码,而eBPF追踪的是底层C实现的解释器栈,默认情况下也无法映射到Python函数名。

  • 内核配置限制
    检查内核是否开启了CONFIG_KALLSYMS_ALL和CONFIG_FRAME_POINTER,这两个配置对用户态栈追踪至关重要。如果CONFIG_FRAME_POINTER未开启,eBPF工具可能无法正确遍历用户态调用栈。
    可以通过以下命令查看配置:

    grep -E "CONFIG_KALLSYMS_ALL|CONFIG_FRAME_POINTER" /boot/config-$(uname -r)
    

    若CONFIG_FRAME_POINTER设为n,则需要重新编译内核开启该选项。

  • eBPF工具的栈追踪限制
    部分eBPF工具在处理用户态栈时,依赖于进程的/proc/[pid]/maps和符号解析能力。如果进程的内存区域没有正确的符号映射(比如动态加载的模块、JIT代码),也会导致显示[unknown]。对于Python,要追踪到Python层面的函数,需要专门的工具或者扩展,而不是通用的profile或offcputime。

  • 权限或安全机制限制
    即使使用root用户执行,内核的安全机制(比如SELinux)可能会限制eBPF工具访问进程的内存或符号信息。可以临时关闭SELinux测试:

    setenforce 0
    

解决建议

  • 对于编译型程序(如haproxy),安装带调试信息的包(通常包名后缀为-debuginfo),例如:
    dnf install haproxy-debuginfo
    
  • 对于Python程序,使用专门针对Python的eBPF工具,比如pyspy,来追踪Python层面的函数调用栈。
  • 检查并确保内核开启了CONFIG_FRAME_POINTER和CONFIG_KALLSYMS_ALL,如果未开启则重新编译内核。
  • 临时关闭SELinux验证是否是安全策略导致的限制。

内容的提问来源于stack exchange,提问作者liruilong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 00:45:58