Последняя версия systemtap поставляется с большим количеством примеров сценариев . В частности, кажется, что сервер послужит хорошей отправной точкой для решения вашей задачи:
#! /usr/bin/env stap
global thread_thislock
global thread_blocktime
global FUTEX_WAIT = 0
global lock_waits
global process_names
probe syscall.futex {
if (op != FUTEX_WAIT) next
t = tid ()
process_names[pid()] = execname()
thread_thislock[t] = $uaddr
thread_blocktime[t] = gettimeofday_us()
}
probe syscall.futex.return {
t = tid()
ts = thread_blocktime[t]
if (ts) {
elapsed = gettimeofday_us() - ts
lock_waits[pid(), thread_thislock[t]] <<< elapsed
delete thread_blocktime[t]
delete thread_thislock[t]
}
}
probe end {
foreach ([pid+, lock] in lock_waits)
printf ("%s[%d] lock %p contended %d times, %d avg us\n",
process_names[pid], pid, lock, @count(lock_waits[pid,lock]),
@avg(lock_waits[pid,lock]))
}
Я пытался диагностировать что-то похожее с процессом MySQL ранее и наблюдал вывод, похожий на следующий, используя приведенный выше скрипт:
mysqld[3991] lock 0x000000000a1589e0 contended 45 times, 3 avg us
mysqld[3991] lock 0x000000004ad289d0 contended 1 times, 3 avg us
Хотя вышеприведенный скрипт собирает информацию обо всех процессах, запущенных в системе, его было бы довольно легко изменить, чтобы он работал только с определенным процессом или исполняемым файлом. Например, мы могли бы изменить скрипт, чтобы он принимал аргумент идентификатора процесса, и изменить зонд при входе в вызов futex, чтобы он выглядел следующим образом:
probe begin {
process_id = strtol(@1, 10)
}
probe syscall.futex {
if (pid() == process_id && op == FUTEX_WAIT) {
t = tid ()
process_names[process_id] = execname()
thread_thislock[t] = $uaddr
thread_blocktime[t] = gettimeofday_us()
}
}
Очевидно, вы можете изменить сценарий множеством способов, чтобы они соответствовали тому, что вы хотите сделать. Я бы посоветовал вам взглянуть на различные примеры сценариев для SystemTap. Они, вероятно, лучшая отправная точка.