Взять имя вызываемой функции в eBPF

Я хотел бы отслеживать функции конкретного PID и собирать некоторую статистику (общее количество вызовов, общее время и т. д.), и мне не совсем понятно, как создать BPF_HASH с парами funcname+my_struct.

Есть ли способ получить имена вызываемых функций в программе BPF?

Я полагаю, что мне следует прочитать регистр IP, используя «PT_REGS_IP (ctx)», но я не совсем понимаю, как преобразовать значение в удобочитаемую строку.

На данный момент программа BPF выглядит следующим образом:

#include <uapi/linux/ptrace.h>
#include <linux/sched.h>

struct data_t {
    u32 pid;
    u64 delta;
    u64 start;
} __attribute__((packed));

BPF_HASH(faddr, u64, struct data_t);
BPF_PERF_OUTPUT(events);

int do_entry(struct pt_regs *ctx) {
    struct data_t *data;
    data->start = bpf_ktime_get_ns();
    u64 ip = PT_REGS_IP(ctx);
    faddr.update(&ip, data);

    return 0;
}

int do_return(struct pt_regs *ctx) {
    struct data_t *data;
    u64 ip = PT_REGS_IP(ctx);
    data = faddr.lookup(&ip);

    if (data->start == 0)
        return 0;       // missed start

    data->delta = bpf_ktime_get_ns() - data->start;
    data->pid = bpf_get_current_pid_tgid();

    events.perf_submit(ctx, &data, sizeof(data));
    faddr.delete(&ip);

    return 0;
}

Но при запуске я получил:

error: <unknown>:0:0: in function do_entry i32 (%struct.pt_regs*): A call to built-in function 'abort' is not supported.

person lesovsky    schedule 19.11.2018    source источник
comment
Не могли бы вы предоставить команды, которые вы используете для компиляции, а затем для загрузки и присоединения вашей программы?   -  person Qeole    schedule 19.11.2018
comment
Извините за неполноту. Полные примеры кода можно найти в моем предыдущем вопросе, там пример трассируемой программы C и пример основной программы Go. Программа C должна быть скомпилирована с символами отладки (gcc -g), программа go скомпилирована с настройками по умолчанию (go build)   -  person lesovsky    schedule 20.11.2018
comment
Будет лучше, если вы сможете предоставить минимальный воспроизводимый пример программы в каждом вопросе, даже если это означает дублирование содержания между вопросами. Код пользовательского пространства необходим для запуска вашей программы BPF, и кто-то, кто хочет воспроизвести его, возможно, не читал ваш предыдущий вопрос.   -  person pchaigno    schedule 20.11.2018
comment
хорошо, сделаю в следующих вопросах.   -  person lesovsky    schedule 21.11.2018


Ответы (1)


У вас есть ошибка в вашей функции do_entry. Вы пытаетесь разыменовать нулевой указатель:

struct data_t *data;
data->start = bpf_ktime_get_ns();

Следующее должно работать лучше:

int do_entry(struct pt_regs *ctx) {
    struct data_t data = {}; // initializes data with zeros.
    data.start = bpf_ktime_get_ns();
    u64 ip = PT_REGS_IP(ctx);
    faddr.update(&ip, &data);
    return 0;
}

Я не понимаю, почему в сообщении об ошибке упоминается abort. Я поспрашиваю.


Как преобразовать адреса памяти в имена функций, зависит от используемой вами пользовательской библиотеки. Если вы используете скрытую копию, есть ksym метод, который вы можете использовать. Я не знаю, есть ли аналог в gobpf.


У вас есть как минимум еще одна ошибка в do_return:

data = faddr.lookup(&ip);
if (data->start == 0)
    return 0;       // missed start

Вам нужно будет проверить, что data не является нулевым, прежде чем разыменовывать его. В противном случае верификатор отклонит вашу программу.

data = faddr.lookup(&ip);
if (!data || data->start == 0)
    return 0;       // missed start
person pchaigno    schedule 19.11.2018
comment
Я заменил указатель на переменную и исправил ошибку в do_return(), но получил нечто странное, чего раньше не видел: $ go build timings.go $ sudo ./timings bpf: Failed to load program: Permission denied 0: (bf) r6 = r1 1: (79) r1 = *(u64 *)(r6 +128) 2: (7b) *(u64 *)(r10 -16) = r1 3: (18) r1 = 0xffff8fcad7999800 ... a lot of similar messages here ... 96: (b7) r5 = 8 97: (85) call bpf_perf_event_output#25 invalid indirect read from stack off -8+0 size 8 Failed to load do_return: error loading BPF program: permission denied - person lesovsky; 20.11.2018
comment
ошибка, затронутая ameba_events.perf_submit(ctx, &data, sizeof(data)); - person lesovsky; 20.11.2018
comment
изменение его на ameba_events.perf_submit(ctx, data, sizeof(data)); исправляет это. - person lesovsky; 20.11.2018
comment
в любом случае, чтение PT_REGS_IP(ctx) в do_entry и do_return возвращает разные значения, и я не могу использовать эти значения в качестве ключа в BPF_HASH. Может быть, можно перевести адрес в имя функции внутри программы BPF и использовать строку в качестве хэш-ключа? - person lesovsky; 20.11.2018
comment
BPF мало поддерживает манипуляции со строками, поэтому я бы не рекомендовал использовать имена функций в качестве хеш-ключа, даже если вы нашли способ перевода адресов. Однако, поскольку do_entry и do_return отслеживают одну и ту же функцию, вы можете передавать значение IP от do_entry к do_return с хэш-картой для каждого процессора, индексированной по текущему PID (это обычная практика в программах трассировки BPF). Я бы не хотел подробно об этом здесь (если вам нужны подробности), так как ответ уже довольно раздут. - person pchaigno; 20.11.2018
comment
это только мои первые шаги с eBPF. изначально я хотел бы отслеживать набор из нескольких функций одного pid, поэтому я не уверен, что текущий PID подходит (потому что его значение всегда будет одинаковым). Кроме того, я хотел бы хранить некоторую статистику между вызовами функций, поэтому в таком случае я полагаю, что карта с именами функций в качестве ключей предпочтительнее. Но если у вас есть более правильные способы, пожалуйста, дайте мне знать. - person lesovsky; 21.11.2018
comment
Я не уверен, что вижу проблему с наличием нескольких функций для трассировки. Если вы уверены, что do_return вызывается сразу после do_entry, вы можете получить значение IP из карты без ошибок. Может быть, мы можем обсудить это с примерами в чате (IRC?) или в другом вопросе SO? - person pchaigno; 21.11.2018
comment
Было бы здорово, я нашла ваши контакты, и если вы не против, я напишу вам письмо и постараюсь объяснить, что я пытаюсь выдумать ))) - person lesovsky; 21.11.2018