Можно ли сравнивать значения различных зондов безопасным для нескольких процессоров способом в DTrace? - PullRequest
2 голосов
/ 28 мая 2020

Я пытаюсь написать сценарий DTrace, который выполняет следующие действия:

  1. При каждом запуске нового потока счетчик увеличивается.
  2. Каждый раз, когда один из этих потоков завершается, уменьшите счетчик и выйдите из скрипта, если счетчик теперь равен нулю.

У меня что-то вроде этого:

BEGIN {
  threads_alive = 0;
}

proc:::lwp-start /execname == $$1/ {
  self->started = timestamp;
  threads_alive += 1;
}

proc:::lwp-exit /self->started/ {
  threads_alive -= 1;
  if (threads_alive == 0) {
    exit(0);
  }
}

Однако это не работает, потому что threads_alive - это скалярная переменная, поэтому она небезопасна для нескольких процессоров. В результате несколько потоков будут перезаписывать изменения переменной друг друга.

Я также пробовал использовать вместо этого агрегатную переменную:

@thread_count = sum(1)
//or
@threads_entered = count();
@threads_exitted = count();

К сожалению, я не обнаружил может делать что-то вроде @thread_count == 0 или @threads_started == @threads_stopped.

1 Ответ

2 голосов
/ 01 июня 2020

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

Если исполняемый файл имя уникально, вы можете использовать зонды proc:::start и proc:::exit для начала первого потока и выхода последнего потока соответственно:

proc:::start
/execname == $$1/
{
        my_pid = pid;
}

proc:::exit
/pid == my_pid/
{
        exit(0);
}

Если вы используете -c к dtrace, зонд BEGIN срабатывает очень скоро после соответствующего proc:::start. Внутренне dtrace -c запускает указанные вилки указанной команды, а затем начинает трассировку в одной из четырех точек: exec (перед первой инструкцией новой программы), preinit (после того, как ld загрузил все библиотеки), postinit (после запуска _init каждой библиотеки) или main (прямо перед первой инструкцией функции main программы, хотя это не поддерживается в macOS).

Если вы используете dtrace -x evaltime=exec -c <program> BEGIN будет срабатывать прямо перед выполнением первой инструкции программы:

# dtrace -xevaltime=exec -c /usr/bin/true -n 'BEGIN{ts = timestamp}' -n 'pid$target:::entry{printf("%dus", (timestamp - ts)/1000); exit(0); }'
dtrace: description 'BEGIN' matched 1 probe
dtrace: description 'pid$target:::entry' matched 1767 probes
dtrace: pid 1848 has exited
CPU     ID                    FUNCTION:NAME
 10  16757                _dyld_start:entry 285us

285us обусловлено временем, которое требуется dtrace для возобновления процесса через /proc или ptrace(2) на macOS. Вместо proc:::start или proc:::lwp-start вы можете использовать BEGIN, pid$target::_dyld_start:entry или pid$target::main:entry.

...