代码之家  ›  专栏  ›  技术社区  ›  Peter Hosey

如何从一开始就跟踪一个程序而不将其作为根运行

  •  11
  • Peter Hosey  · 技术社区  · 15 年前

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

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

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

    有没有一种方法可以让我在用户帐户下运行程序,而不是以根用户的身份运行,但仍然可以让DTrace从一开始就跟踪它?

    7 回复  |  直到 15 年前
        1
  •  6
  •   alex strange    15 年前

    有点像 sudo dtruss -f sudo -u <original username> <command> 为我工作过,但后来我觉得很难过。

    我对它提出了雷达窃听,并把它作为5108629的复制品关闭了。

        2
  •  4
  •   geowar    12 年前

    此脚本采用要监视到DTrace的可执行文件的名称(对于一个应用程序,这是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);
    }
    
        3
  •  3
  •   kperryua    15 年前

    如果另一个答案对您不起作用,您可以在gdb中运行程序,中断main(甚至更早),获取pid,然后启动脚本吗?我以前也试过,似乎很管用。

        4
  •  3
  •   Sami    13 年前

    嗯,这有点旧了,但为什么不呢?-)。。

    我不认为有一种方法可以简单地从命令行执行此操作,但正如所建议的,一个简单的启动程序应用程序(如下面所示)将执行此操作。当然,手动附加也可以替换为对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;
    }
    
        5
  •  2
  •   bdonlan    15 年前

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

        6
  •  1
  •   Community Keith    7 年前

    DTrus有 -n 选项,您可以指定要跟踪的进程的名称,而无需启动它(可在@kenorb的答案的后面部分 https://stackoverflow.com/a/11706251/970301 ). 所以应该采取如下措施:

    sudo dtruss -n "$program"
    $program
    
        7
  •  0
  •   Birchlabs    7 年前

    my answer 论相关 question “如何让dtrace以非根权限运行跟踪命令?”[原文如此]。

    实际上,您可以启动一个(非根)后台进程,该进程等待1秒DTrace启动(抱歉,出现了race条件),然后窥探该进程的PID。

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

    链接答案中的完整解释。