您能否在 DTrace 中以多 CPU 安全方式比较跨探测器的值?

Can you compare values across probes in a multi-CPU safe way in DTrace?

我正在尝试编写执行以下操作的 DTrace 脚本:

  1. 每当启动一个新线程时,增加一个计数。
  2. 每当这些线程之一退出时,递减计数,如果计数现在为零则退出脚本。

我有这样的东西:

BEGIN {
  threads_alive = 0;
}

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

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

但是,这不起作用,因为 threads_alive 是一个标量变量,因此它不是 multi-cpu 安全的。结果,多个线程将覆盖彼此对变量的更改。

我也尝试过使用聚合变量:

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

不幸的是,我还没有找到能够执行 @thread_count == 0@threads_started == @threads_stopped.

之类的语法

DTrace 没有执行您提议的那种线程安全数据共享的工具,但您有几个选项,具体取决于您尝试执行的操作。

如果可执行文件名称是唯一的,您可以分别使用proc:::startproc:::exit探测第一个线程的启动和最后一个线程的退出:

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

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

如果您使用 dtrace-c 选项,BEGIN 探测器会在相应的 proc:::start 之后不久触发。在内部,dtrace -c 启动指定的 fork 指定的命令,然后在四个点之一开始跟踪: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 是由于在 macOS 上通过 /procptrace(2) 恢复进程需要 dtrace 的时间。您可以使用 BEGINpid$target::_dyld_start:entrypid$target::main:entry.

而不是 proc:::startproc:::lwp-start