В предыдущей публикации (переводе статьи) рассматривалась возможность инструментировать код 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/
Добавить комментарий