Почему отсоединение от uprobe с помощью bcc значительно медленнее, чем присоединение?

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

У меня есть программа, которая прикрепляется к 5000 функциям как на входе, так и на выходе. Прикрепление занимает некоторое время, около 30 секунд или около того. Однако, когда моя программа завершает работу, очистка здесь занимает более 10 минут.

Код на C выглядит следующим образом:

typedef struct FunctionEvent_t {
  u32 pid;
  u64 timestamp;

  u64 func_addr;
  u8 entry;
} FunctionEvent;

BPF_PERF_OUTPUT(traceevents);

static void fill_and_submit(struct pt_regs *ctx, FunctionEvent *event) {
  event->pid = bpf_get_current_pid_tgid();
  event->timestamp = bpf_ktime_get_ns();
  event->func_addr = PT_REGS_IP(ctx);
  traceevents.perf_submit(ctx, event, sizeof(FunctionEvent));
}

int do_entry_point(struct pt_regs *ctx) {
  FunctionEvent event = {.entry = 1};
  fill_and_submit(ctx, &event);
  return 0;
}

int do_exit_point(struct pt_regs *ctx) {
  FunctionEvent event = {.entry = 0};
  fill_and_submit(ctx, &event);
  return 0;
}

Код bcc выглядит примерно так:

from bcc import BPF
bpf_instance = BPF(text=bpf_program)
path = "path/to/exe"

for func_name, func_addr in BPF.get_user_functions_and_addresses(path, ".*"):
    func_name = func_name.decode("utf-8")

    if func_addr in addresses:
        continue

    addresses.add(func_addr)
    try:
        bpf_instance.attach_uprobe(name=self.args.path, sym=func_name, fn_name="do_entry_point")
        bpf_instance.attach_uretprobe(name=self.args.path, sym=func_name, fn_name="do_exit_point")
    except Exception as e:
        print(f"Не удалось прикрепиться к функции {func_name}")

Есть ли способ ускорить процесс отвязки? Можно ли как-то параллелизировать прикрепление и отвязку? Я бы не подумал, что очистка будет настолько медленной.

Также следует отметить, что я не знаю, является ли прикрепление к 5000 функциям адекватным, я просто играю с системой и пытаюсь выяснить ее пределы.

Обратите внимание, что это спекуляция, я не смог это проверить.

Есть две причины медлительности. Первая причина связана с прикреплением/отвязкой функции по одной по сравнению с пакетной отвязкой. Я нашел эту проблему, которая описывает ту же самую проблему.

У нас сейчас есть поддержка мультипроцессорных проб через BPF ссылки. Я не думаю, что BCC это поддерживает, но вы должны иметь возможность использовать это через libbpf.

Вторая причина заключается в том, почему отвязка занимает так много времени. Это, похоже, связано с оптимизацией. Когда вы прикрепляете kprobe, изначально она не оптимизирована. Фоновый оптимизатор будет оптимизировать прикрепленные kprobes, что занимает время для выполнения и отмены. Поэтому, когда вы идете и отвязываете kprobe, теперь нужно отменить оптимизированную kprobe, что занимает больше времени.

Кажется, что предпринималась попытка в прошлом сделать это более асинхронным. Однако, я не думаю, что это было объединено. Но сообщение о коммите, похоже, подтверждает мою теорию об оптимизации:

… Я обнаружил, что существует другая проблема.

Probes: 256 kprobe_events
Включить события
real   0m 0.00s
user   0m 0.00s
sys    0m 0.00s
Отключить события
real   0m 21.40s
user   0m 0.00s
sys    0m 0.02s
Удалить события
real   0m 2.24s
user   0m 0.00s
sys    0m 0.01s

Хорошо, удаление событий заняло более 2 секунд для 256 проб событий. Но отключение событий заняло 21 секунду, в 10 раз дольше, чем удаление. На самом деле, поскольку perf-events (основа BPF трейсера) отключает и удаляет одновременно, это займет больше времени.

Я также измерил это без оптимизации прыжка kprobe (echo 0 > /proc/sys/debug/kprobe-optimization) и это изменило результаты следующим образом.

Probes: 256 kprobe_events
Включить события
real   0m 0.00s
user   0m 0.00s
sys    0m 0.00s
Отключить события
real   0m 2.07s
user   0m 0.00s
sys    0m 0.04s
Удалить события
real   0m 2.13s
user   0m 0.00s
sys    0m 0.01s

Таким образом, отключив оптимизацию прыжка, вы, возможно, сможете сделать компромисс между временем отвязки и накладными расходами на выполнение. См. документацию по kprobe ядра для необработанных и оптимизированных накладных расходов.

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

Причины медленной отвязки от uprobes с использованием BCC и возможные способы оптимизации

Когда вы работаете с механизмом проб (uprobes) и используете библиотеку BCC для анализа производительности и отладки программ, может возникнуть ситуация, когда время отвязки от функций значительно превышает время их привязки. В данной статье мы подробно рассмотрим причины такой медлительности отвязки от uprobes и возможные способы оптимизации этого процесса.

Причины медленной отвязки

  1. Однобокий процесс привязки и отвязки

    Привязка и отвязка функций происходит поочередно. При привязке к 5000 функциям обработка каждой из них осуществляется последовательно, что занимает определенное время (в данном случае около 30 секунд). Но процедура отвязки, которая включает в себя отключение и удаление проб, требует значительно большего времени (до 10 минут). Это может быть связано с тем, что каждый этап процесса отвязки требует индивидуальной проверки и выполнения инструкций, что в итоге приводит к затягиванию количества операций.

  2. Оптимизация проб в ядре Linux

    При привязке проб к функциям происходит их начальная настройка и оптимизация на уровне ядра. Бэкграундный оптимизатор производит оптимизацию для каждого привязанного проба. Это означает, что когда вы уходите от пробов (отвязка), ядру необходимо отменить эти оптимизации, что требует времени. Измерения показывают, что время, необходимое для отключения пробов, может значительно превышать время их удаления, что подтверждает гипотезу о времени на обработку оптимизаций.

  3. Недостаточная поддержка многопроцессорного рабочего процесса

    В то время как концепция многократного управления пробами (multi-kprobe) существует в современном ядре Linux, в библиотеке BCC поддержка этой функции еще не реализована. Следовательно, возможности для параллелизации этого процесса отсутствуют, и, как результат, время отвязки увеличивается.

  4. Проблемы с вычислительными ресурсами

    Также стоит учитывать, что операции, связанные с управлением probe, могут потреблять значительное количество вычислительных ресурсов, особенно если вас интересует работа с таким большим количеством проб. Наконец, система может быть перегружена другими процессами, что также может удлинить время, необходимое для выполнения операций по отвязке.

Возможные способы оптимизации

  1. Тестирование без оптимизации проб

    Один из простых способов сократить время отвязки – это временное отключение оптимизации проб на уровне ядра Linux. Вы можете попробовать отключить оптимизацию, используя команду:

    echo 0 > /proc/sys/debug/kprobe-optimization

    Это доложит значительно сократить время отвязки, хотя и приведет к увеличению накладных расходов во время работы с пробами.

  2. Параллелизация процесса

    Хотя текущая версия BCC может не поддерживать многопоточность для отвязки, можно рассмотреть возможность написания собственного кода с использованием библиотеки libbpf для параллельной обработки отвязки пробов. Это позволит использовать многопоточность и сократить общее время выполнения процесса.

  3. Использование более легковесных методов отслеживания

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

Заключение

Процессы привязки и отвязки пробов занимают значительное время, особенно при работе с большим количеством функций. Понимание причин этих задержек – критически важный шаг для оптимизации процессов и увеличения эффективности работы. Надеюсь, предлагаемые выше советы помогут вам улучшить скорость отвязки и снизить общее время работы с BCC.

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

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