【问题标题】:Tracing syscalls of a process and all forked processes跟踪进程和所有分叉进程的系统调用
【发布时间】:2012-11-11 23:58:27
【问题描述】:

我正在使用ptrace 来跟踪进程的系统调用。分叉进程后,我使用PTRACE_TRACEME 开始跟踪进程。代码如下所示:

while (true) {
    int status;
    int gotPid;
    gotPid = waitpid(pid, &status, 0);

    if (WIFEXITED(status) || WIFSIGNALED(status)) {
        break;
    }

    if (WIFSTOPPED(status)) {
        handleTrace();
    }
}

然后是handleTrace函数,看起来像这样。

long syscall;
syscall = ptrace(PTRACE_PEEKUSER,
     pid, 8 * ORIG_RAX, NULL);

// do something with the syscall

// continue program
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);

这一切都很好,但是如果程序分叉(或创建一个新线程),我还想跟踪被跟踪进程创建的子进程(以及该进程创建的线程)。我知道它可以使用PTRACE_O_TRACEFORKPTRACE_O_TRACEVFORKPTRACE_O_TRACECLONE 来完成,但是从man 文档中很难弄清楚它是如何完成的。我需要一些例子。

编辑:

我在这里发现了一个类似的问题:How to ptrace a multi-threaded application? 我用下面的代码试了一下。此代码跟踪启动进程的系统调用,它也应该跟踪分叉进程。它在父进程中的fork() 之后运行(子进程调用PTRACE_TRACEMEexec())。

编辑2:

我对代码做了更多的修改,有了更多的进步。

long orig_eax;
int status;
int numPrograms = 1;

while(1) {
    int pid;
    CHECK_ERROR_VALUE(pid = waitpid(-1, &status, __WALL));
    std::cout << pid << ": Got event." << std::endl;
    if(WIFEXITED(status) || WIFSIGNALED(status)) {
        std::cout << pid << ": Program exited." << std::endl;
        if (--numPrograms == 0) {
            break;
        }
        continue;
    }

    if (status >> 16 == PTRACE_EVENT_FORK || status >> 16 == PTRACE_EVENT_VFORK ||
            status >> 16 == PTRACE_EVENT_CLONE) {
        int newpid;
        CHECK_ERROR_VALUE(ptrace(PTRACE_GETEVENTMSG, child, NULL, (long) &newpid));
        std::cout << pid << ": Attached to offspring " << newpid << std::endl;
        boost::this_thread::sleep(boost::posix_time::millisec(100));
        CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
                newpid, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
        CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL, newpid, NULL, NULL));
        ++numPrograms;
    } else {
        CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
                pid, NULL, PTRACE_O_TRACEFORK  | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
        CHECK_ERROR_VALUE(orig_eax = ptrace(PTRACE_PEEKUSER,
                pid, 8 * ORIG_RAX, NULL));
        std::cout << pid << ": Syscall called: " <<
                SyscallMap::instance().get().right.at(orig_eax) <<
                std::endl;
        CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL,
                pid, NULL, NULL));
    }

}

CHECK_ERROR_VALUE 只是一个宏,它检查结果代码并抛出异常,其中包含errno 的描述。

显然,当我收到 fork/clone 事件时,新进程尚不存在,如果我尝试对其进行 ptrace,则会收到“进程不存在”错误消息。如果我在尝试 ptrace 新进程之前进入睡眠状态,我不会收到错误消息。现在当程序到达 fork/clone 点时,它开始跟踪新进程,但它永远不会到达父进程中clone() 系统调用的返回点,这意味着子进程成功完成,但是parent 挂在它的 fork 点。

【问题讨论】:

    标签: c++ c linux fork ptrace


    【解决方案1】:

    Strace 执行此操作,其 README-linux-ptrace 文件包含有关该主题的一些信息:

    https://github.com/strace/strace/blob/master/README-linux-ptrace

    它似乎确实在抱怨这个平台的内核错误,所以 YMMV。

    代码解释了如何获取孩子的 pid。但是,由于在它调用的二进制文件上设置了 setuid 或 setgid 位,孩子可能会获得另一个用户 ID。所以答案是你在子 PID 上调用 ptrace 看看你是否可以访问。

    以下是相关部分:

    PTRACE_EVENT 停止被跟踪器观察为 waitpid 返回 WIFSTOPPED(status) == trueWSTOPSIG(status) == SIGTRAP。附加位 在状态字的高字节中设置:值((status &gt;&gt; 8) &amp; 0xffff) 将是(SIGTRAP | PTRACE_EVENT_foo &lt;&lt; 8)。存在以下事件:

    • PTRACE_EVENT_VFORK - 在从 vfork/clone+CLONE_VFORK 返回之前停止。 在此之后继续跟踪时,它将等待孩子 在继续执行之前退出/执行(IOW:通常的行为 vfork)。
    • PTRACE_EVENT_FORK - 在从 fork/clone+SIGCHLD 返回之前停止
    • PTRACE_EVENT_CLONE - 在从克隆返回之前停止
    • PTRACE_EVENT_VFORK_DONE - 在返回之前停止 vfork/clone+CLONE_VFORK,但是在 vfork child 通过 退出或执行。

    对于上述所有四个停止:停止发生在父节点中,而不是在新节点中 创建线程。 PTRACE_GETEVENTMSG 可用于检索新线程的 时间。

    【讨论】:

      猜你喜欢
      • 2012-06-20
      • 1970-01-01
      • 2019-09-25
      • 2015-09-26
      • 2017-11-04
      • 1970-01-01
      • 2012-04-03
      • 2021-11-22
      • 1970-01-01
      相关资源
      最近更新 更多