Я не думаю, что вы можете сделать это с помощью одного скрипта dtrace. Вам нужно два (по крайней мере ...). И вам нужно иметь возможность запустить разрушительное действие system()
, что, скорее всего, означает root
доступ.
Предположим, вы хотите запустить этот скрипт на любом новом ls
процессе:
#!/usr/sbin/dtrace -s
pid$1:libc::entry
{
printf( "func: %s\n", probefunc );
}
Если предположить, что путь к этому сценарию равен /root/dtrace/tracelibc.d
, следующий сценарий будет запускать dtrace
в любом новом ls
процессе, который запускается. Обратите внимание, что вам нужно #pragma D option destructive
, чтобы начать новый процесс dtrace
:
#!/usr/sbin/dtrace -s
#pragma D option destructive
#pragma D option quiet
proc:::exec-success
/ "ls" == basename( execname ) /
{
printf( "tracing process %d\n", pid );
system( "/root/dtrace/tracelibc.d %d", pid );
}
Это должно сработать, но в этом случае ls
такой недолговечный процесс, что нечто подобное происходит довольно часто:
dtrace: failed to compile script /root/dtrace/tracelibc.d: line 10:
failed to grab process 12289
Процесс уходит к тому времени, когда dtrace
начинает действовать. Если вы отслеживаете долгоживущие процессы и вам не важно, что вы можете пропустить первые несколько проб, потому что dtrace
требуется некоторое время для подключения, все готово.
Но, если вы хотите отследить недолговечные процессы, вам нужно остановить процесс сразу после его запуска, а затем перезапустить после того, как dtrace
присоединит:
#!/usr/sbin/dtrace -s
#pragma D option destructive
#pragma D option quiet
proc:::exec-success
/ "ls" == basename( execname ) /
{
printf( "stopping process %d\n", pid );
system( "/root/dtrace/tracelibc.d %d", pid );
stop();
}
и запустите его обратно в tracelibc.d
:
#!/usr/sbin/dtrace -s
#pragma D option destructive
BEGIN
{
system( "prun %d", $1 );
}
pid$1:libc::entry
{
printf( "func: %s\n", probefunc );
}
Обратите внимание, что я использую Solaris prun
для перезапуска остановленного процесса. Вам нужно будет просмотреть документацию Mac dtrace
для вызова stop()
, чтобы получить Mac эквивалент Solaris prun
.
Но ... упс. Два приведенных выше сценария объединяются для получения:
stopping process 12274
dtrace: failed to compile script /root/dtrace/tracelibc.d: line 10:
probe description pid12274:libc::entry does not match any probes
Почему это говорит, что pid12274:libc::entry
не соответствует ни одному из зондов? О, да - когда exec
возвращается, общий объект libc.so
еще не загружен в память. Нам нужен зонд, который гарантированно существует в целевом процессе и который вызывается после загрузки libc.so
, но до выполнения какой-либо обработки. main
должно хватить. Таким образом, основным скриптом для запуска становится все:
#!/usr/sbin/dtrace -s
#pragma D option destructive
#pragma D option quiet
proc:::exec-success
/ "ls" == basename( execname ) /
{
printf( "stopping process %d\n", pid );
system( "/root/dtrace/tracemain.d %d", pid );
stop();
}
Запускает сценарий tracemain.d
, перезапускает процесс, загружает сценарий tracelibc.d
и снова останавливает процесс:
#!/usr/sbin/dtrace -s
#pragma D option destructive
#pragma D option quiet
BEGIN
{
system( "prun %d", $1 );
}
pid$1::main:entry
{
system( "/root/dtrace/tracelibc.d %d", $1 );
stop();
/* this instance of dtrace is now done */
exit( 0 );
}
И tracelibc.d
добавляет свой собственный system( "prun %d", $1 );
в BEGIN
пробник, и это выглядит так:
#!/usr/sbin/dtrace -s
#pragma D option destructive
BEGIN
{
system( "prun %d", $1 );
}
pid$1:libc::entry
{
printf( "func: %s\n", probefunc );
}
Эти три действительно замедляют процесс ls
, но они дают ожидаемый результат - и его, как и ожидалось, много.