Pull to refresh
АО «ГНИВЦ»
Драйвер цифровой трансформации

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

Level of difficultyHard
Reading time3 min
Views386

В предыдущей публикации (переводе статьи) рассматривалась возможность инструментировать код 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;
	bool		save_log_statement_stats = log_statement_stats;
	bool		was_logged = false;
	bool		use_implicit_block;
	char		msec_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 :)

Tags:
Hubs:
Total votes 3: ↑3 and ↓0+3
Comments0

Articles

Information

Website
www.gnivc.ru
Registered
Founded
1977
Employees
1,001–5,000 employees
Location
Россия