Когда BPF(eBPF) отслеживает стек вызовов, все функции пользовательского режима отображаются как [unknown]. Почему это так?

Вопрос или проблема

Экспериментальная среда

┌──[[email protected]]-[/usr/share/bcc/tools]
└─$hostnamectl
   Статическое имя хоста: vms99.liruilongs.github.io
         Имя иконки: computer-vm
           Шасси: vm
        Идентификатор машины: ea70bf6266cb413c84266d4153276342
           Идентификатор загрузки: 0d01838b0095494c82d1befb174a317d
    Виртуализация: vmware
  Операционная система: Rocky Linux 8.9 (Green Obsidian)
       Имя ОС CPE: cpe:/o:rocky:rocky:8:GA
            Ядро: Linux 4.18.0-513.9.1.el8_9.x86_64
      Архитектура: x86-64
┌──[[email protected]]-[/usr/share/bcc/tools]
└─$

При использовании BPF/eBPF для отслеживания стека вызовов я обнаружил, что все функции в пользовательском режиме отображаются как [неизвестно]

┌──[[email protected]]-[/usr/share/bcc/tools]
└─$profile
Выборка с частотой 49 Гц для всех потоков по стеку пользователя + ядра... Нажмите Ctrl-C для завершения.
^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
    [неизвестно]
    -                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
    [неизвестно]
    [неизвестно]
    -                awk (39726)
        1
.............        
┌──[[email protected]]-[/usr/share/bcc/tools]
└─$

В чем причина этого? Это связано с отсутствием информации для отладки в программе? Или по какой-то другой причине?

Я использовал Python для написания демонстрации lock, и это также произошло

┌──[[email protected]]-[~]
└─$cat lock_demo.py
import threading
import time

lock = threading.Lock()

def worker(id):
    print(f"Рабочий {id} запущен")
    with lock:
        print(f"Рабочий {id} захватил блокировку")
        time.sleep(2)  # Симуляция длительных вычислений или I/O
    print(f"Рабочий {id} освободил блокировку")

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("Все рабочие завершили работу")

threadsnoop

┌──[[email protected]]-[~]
└─$threadsnoop
TIME(ms)   PID     COMM             FUNC
0          51671   b'python3'       b'[неизвестно]'
0          51671   b'python3'       b'[неизвестно]'
0          51671   b'python3'       b'[неизвестно]'
0          51671   b'python3'       b'[неизвестно]'
0          51671   b'python3'       b'[неизвестно]'

offcputime

┌──[[email protected]]-[~/FlameGraph]
└─$offcputime  -p `pgrep -f lock_demo.py`
Отслеживание времени вне ЦП (мкС) PID 51397 по стеку пользователя + ядра... Нажмите Ctrl-C для завершения.
^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
    [неизвестно]
    -                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
    [неизвестно]
    -                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
    [неизвестно]
    -                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
    [неизвестно]
    [неизвестно]
    -                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
    [неизвестно]
    [неизвестно]
    -                python3 (51402)
        2003178
..................
┌──[[email protected]]-[~/FlameGraph]
└─$

Любая помощь будет очень ценна, наилучшие пожелания

Ответ или решение

Причина появления [unknown] в трассировке стека вызовов с использованием eBPF

Когда вы используете BPF или eBPF для отслеживания стека вызовов в пользовательских программах и видите, что функции в пользовательском режиме отображаются как [unknown], это может быть вызвано несколькими факторами. В этой статье мы подробно рассмотрим основные причины, по которым eBPF не может разрешить имена функций, и предложим возможные пути решения проблемы.

Причины появления [unknown]

  1. Отсутствие отладочной информации

    • Самая распространенная причина, по которой eBPF не может отобразить имена пользовательских функций, — это отсутствие отладочной информации в скомпилированных бинарных файлах. Отладочная информация генерируется компиляторами с использованием таких опций, как -g для GCC. Если ваши программы были скомпилированы без этой информации, eBPF не сможет сопоставить адреса функций с их именами.
  2. Использование stripped бинарников

    • Многие дистрибутивы Linux и дистрибутивы программного обеспечения по умолчанию поставляются с бинарниками, от которых удалена отладочная информация для уменьшения размера файла и повышения безопасности. Если вы работаете с такими бинарниками (например, в случае приложений, установленных через дистрибутивные пакеты), это также приводит к тому, что на выходе вы видите [unknown].
  3. Устаревшие версии инструментов BPF

    • Использование устаревших или несовместимых версий инструментов BPF может также оказать влияние на их возможность правильно интерпретировать стеки вызовов. Убедитесь, что у вас установлены последние версии инструментов BPF, таких как bpftrace, bcc и другие.
  4. Запуск в средах с высоким уровнем абстракции

    • В случаях запуска ваших приложений в окружениях с высоким уровнем абстракции, таких как контейнеры, виртуальные машины или в средах, использующих особые библиотеки (например, glibc), могут возникать проблемы с разрешением адресов функций.
  5. Политики безопасности и конфигурации ядра

    • Некоторые настройки безопасности, например SELinux, могут ограничить доступ к отладочной информации. Убедитесь, что ваша система правильно настроена, чтобы разрешить доступ к необходимым данным.
  6. Процессы, использующие динамическую загрузку

    • Если ваше приложение использует динамическую загрузку библиотек, то может быть сложнее отслеживать их. В этом случае вы можете также видеть [unknown], так как адреса загружаемых функций могут быть не доступны во время вашего наблюдения.

Возможные решения

  • Компиляция с отладочной информацией: При компиляции ваших программ добавьте флаг -g, чтобы включить отладочную информацию.
  • Убедитесь в наличии отладочной информации: Вы можете использовать такие утилиты, как file или readelf, чтобы проверить, есть ли отладочная информация в вашем бинарном файле:
    file your_program
    readelf --debug-dump your_program
  • Убедитесь в актуальности инструментов: Обновите ваши инструменты и библиотеки, связанные с BPF, до последних стабильных версий. Используйте команду пакетного менеджера вашей системы для этого.
  • Проверьте настройки безопасности: Убедитесь, что настройки безопасности вашего ядра не мешают работе инструментов BPF.

Заключение

Изучение узких мест в производительности вашего приложения с помощью BPF и eBPF позволяет значительно оптимизировать его работу. Однако важно обеспечить наличие необходимой информации для корректной работы этих инструментов. Уделяя внимание деталям компиляции, версиям инструментов и конфигурации системы, вы сможете не только предотвратить возникновение проблемы с [unknown], но и улучшить общее качество трассировки стека вызовов.

Оцените материал
Добавить комментарий

Капча загружается...