12

我正在编写一个调用 DTrace 以跟踪用户指定的程序的工具。

如果我的工具使用 dtrace -c 将程序作为 DTrace 的子进程运行,我不仅不能向程序传递任何参数,而且程序以 DTrace 的所有权限运行——即以 root 身份运行(我在Mac OS X)。这使得某些应该工作的事情中断,并且显然使许多不应该工作的事情成为可能。

我知道的另一个解决方案是自己启动程序,通过发送暂停它SIGSTOP,将其 PID 传递给dtrace -p,然后通过发送继续它SIGCONT。问题是,在 DTrace 收集符号信息时,程序运行了几秒钟而没有被跟踪,或者,如果我在继续该过程之前睡了几秒钟,DTrace 会抱怨不objc<pid>:<class>:<method>:entry匹配任何探测器。

有没有办法让我可以在用户帐户下运行程序,而不是以 root 身份运行程序,但仍然让 DTrace 能够从一开始就对其进行跟踪?

4

7 回答 7

6

类似的东西sudo dtruss -f sudo -u <original username> <command>对我有用,但后来我感觉很糟糕。

我提交了一个关于它的 Radar 错误,并将其作为 #5108629 的副本关闭。

于 2009-12-05T07:43:04.830 回答
4

此脚本将您要监视的可执行文件的名称(对于应用程序,这是 info.plist 的 CFBundleExecutable)作为参数作为参数(您可以在此脚本运行后启动目标应用程序):

string gTarget;     /* the name of the target executable */

dtrace:::BEGIN
{
    gTarget = $$1;  /* get the target execname from 1st DTrace parameter */

    /*
    * Note: DTrace's execname is limited to 15 characters so if $$1 has more
    * than 15 characters the simple string comparison "($$1 == execname)"
    * will fail. We work around this by copying the parameter passed in $$1
    * to gTarget and truncating that to 15 characters.
    */

    gTarget[15] = 0;        /* truncate to 15 bytes */
    gTargetPID = -1;        /* invalidate target pid */
}

/*
* capture target launch (success)
*/
proc:::exec-success
/
    gTarget == execname
/
{
    gTargetPID = pid;
}

/*
*   detect when our target exits
*/
syscall::*exit:entry
/
    pid == gTargetPID
/
{
    gTargetPID = -1;        /* invalidate target pid */
}

/*
* capture open arguments
*/
syscall::open*:entry
/
    ((pid == gTargetPID) || progenyof(gTargetPID))
/
{
    self->arg0 = arg0;
    self->arg1 = arg1;
}

/*
* track opens
*/
syscall::open*:return
/
    ((pid == gTargetPID) || progenyof(gTargetPID))
/
{
    this->op_kind = ((self->arg1 & O_ACCMODE) == O_RDONLY) ? "READ" : "WRITE";
    this->path0 = self->arg0 ? copyinstr(self->arg0) : "<nil>";

    printf("open for %s: <%s> #%d",
        this->op_kind,
        this->path0,
        arg0);
}
于 2012-07-11T16:58:22.653 回答
4

好吧,这有点老了,但为什么不:-)..

我认为没有一种方法可以简单地从命令行执行此操作,但正如建议的那样,一个简单的启动器应用程序(如下所示)可以做到这一点。当然,手动附加也可以替换为对 libdtrace 的一些调用。

int main(int argc, char *argv[]) {
    pid_t pid = fork();
    if(pid == 0) {
        setuid(123);
        seteuid(123);
        ptrace(PT_TRACE_ME, 0, NULL, 0);
        execl("/bin/ls", "/bin/ls", NULL);
    } else if(pid > 0) {
        int status;
        wait(&status);

        printf("Process %d started. Attach now, and click enter.\n", pid);
        getchar();

        ptrace(PT_CONTINUE, pid, (caddr_t) 1, 0);
    }

    return 0;
}
于 2011-04-22T11:43:34.150 回答
3

如果其他答案对您不起作用,您可以在 gdb 中运行程序,中断 main(甚至更早),获取 pid,然后启动脚本吗?我过去曾尝试过,它似乎有效。

于 2009-07-30T07:12:09.070 回答
2

创建一个启动程序,它将等待某种信号(不一定是文字信号,只是表明它准备好了),然后 exec() 你的目标。现在 dtrace -p 启动程序,一旦 dtrace 启动,让启动程序运行。

于 2009-07-30T03:13:29.047 回答
1

dtruss 有一个-n选项,您可以在其中指定要跟踪的进程的名称,而无需启动它(归功于@kenorb 在https://stackoverflow.com/a/11706251/970301的答案的后半部分)。因此,应该执行以下操作:

sudo dtruss -n "$program"
$program
于 2013-12-26T22:42:14.763 回答
0

请参阅对相关问题的回答“如何让 dtrace 以非 root 权限运行跟踪的命令?” [原文如此]。

本质上,您可以启动一个(非根)后台进程,该进程等待 1 秒以等待 DTrace 启动(抱歉出现竞争条件),并窥探该进程的 PID。

sudo true && \
(sleep 1; cat /etc/hosts) &; \
sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $! \
&& kill $!

链接答案中的完整解释。

于 2017-06-25T18:56:29.723 回答