Использование uprobe/uretprobe в bpftrace-скриптах вместо USDT probe

от автора

В предыдущей публикации (переводе статьи) рассматривалась возможность инструментировать код Postgres при помощи User Statically-Defined Tracing (USDT) probe. Это замечательный подход, но с некоторой особенностью.

Чтобы использовать эти пробы, бинарники должны быть скомпилированы (собраны) неким специальным образом, например, для сборки PostgreSQL вы должны использовать специальный флаг --enable-dtrace, т.е. вызвать configure --enable-dtrace перед вызовом make.

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

Сразу возникла идея сделать подобное инструментирование с использованием uprobe/uretprobe. Как известно, вызов почти любой функции может быть динамически инструментирован при помощи так называемых uprobe (динамические пробы в user space).

Давайте посмотрим, как выглядит инструментирование кода Postgres изнутри
https://github.com/postgres/postgres/blob/master/src/backend/tcop/postgres :

static void exec_simple_query(const char *query_string) { CommandDest dest = whereToSendOutput; MemoryContext oldcontext; List   *parsetree_list; ListCell   *parsetree_item; boolsave_log_statement_stats = log_statement_stats; boolwas_logged = false; booluse_implicit_block; charmsec_str[32];  /*  * Report query to various monitoring facilities.  */ debug_query_string = query_string;  pgstat_report_activity(STATE_RUNNING, query_string);  TRACE_POSTGRESQL_QUERY_START(query_string); 

Макрос TRACE_POSTGRESQL_QUERY_START является тем самым USDT-инструментированием.

Соответствующий код в bpftrace-скрипте
https://github.com/FritsHoogland/postgres-bpftrace/blob/main/query-analyzer.bt

usdt:/usr/lib/postgresql/15/bin/postgres:query__start {     $time = nsecs;     printf("[%5u]Query start:              :              : %s\n", pid, str(arg0));     @query_start[pid] = $time;     @phase_done[pid] = $time;     @query_trigger[pid] = 1; } 

Из вышеприведённого видно, что вместо USDT-пробы TRACE_POSTGRESQL_QUERY_START можно инструментировать вызов функции exec_simple_query. Соответствующий код будет выглядеть так:

uprobe:/usr/local/pgsql/bin/postgres:exec_simple_query {     $time = nsecs;     printf("[%5u]Query start:              :              : %s\n", pid, str(arg0));     @query_start[pid] = $time;     @phase_done[pid] = $time;     @query_trigger[pid] = 1; } 

Для отслеживания момента окончания вызова функции (аналог USDT-пробы TRACE_POSTGRESQL_QUERY_DONE) нужно будет использовать uretprobe-пробу.

uretprobe:/usr/local/pgsql/bin/postgres:exec_simple_query {     $time = nsecs;     $query_end = $time - @query_start[pid];     printf("[%5u]Query done : (%10u) :    %10u:\n", pid, ($time - @phase_done[pid])/1000, $query_end/1000);     @parse[pid] = (uint64)0;     @rewrite[pid] = (uint64)0;     @plan[pid] = (uint64)0;     @execute[pid] = (uint64)0;     @query_trigger[pid] = 0; } 

Целиком переработанный скрипт можно увидеть здесь:
https://hub.mos.ru/drema201/bpftrace_4pg/-/blob/main/scripts/query_uprobe_phase.bt

Для сравнения приведу исходный скрипт:
https://github.com/FritsHoogland/postgres-bpftrace/blob/main/query-analyzer.bt

Результат выполнения нового скрипта соответствует результатам Frits Hoogland:

sudo ./query_uprobe_phase.bt Attaching 17 probes... PostgreSQL statement execution analyzer. Time in microseconds (us). pid   :Phase      :time to phase :time in phase : query ------|-----------|--------------|--------------|------ [14697]Query start:              :              : SELECT 1; [14697] parse     : (       110) :           214: [14697] plan      : (       106) :           328: [14697] execute   : (        84) :            34: [14697]Query done : (        49) :           927:  

С одним незначительным отличием: uretprobe не поддерживают аргументы arg0..N, так что в Query done в вывод не включён текст запроса (впрочем, это может быть решено другими средствами).

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

PS
Я собираюсь перевести следующую статью Frits Hoogland, в которой он пробует воспроизвести функционал OWI (Oracle Wait Interface) на Postgres при помощи bpftrace. И здесь мой подход может быть не применим, по крайней мере напрямую. To be continued 🙂


ссылка на оригинал статьи https://habr.com/ru/articles/771732/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *