I'm using ptrace
to trace the syscalls of a process. After forking the process, I use PTRACE_TRACEME
to start trace the the process. The code looks like this:
while (true) {
int status;
int gotPid;
gotPid = waitpid(pid, &status, 0);
if (WIFEXITED(status) || WIFSIGNALED(status)) {
break;
}
if (WIFSTOPPED(status)) {
handleTrace();
}
}
Then there is the handleTrace
function, which looks like this.
long syscall;
syscall = ptrace(PTRACE_PEEKUSER,
pid, 8 * ORIG_RAX, NULL);
// do something with the syscall
// continue program
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
This is all good, but if the program forks (or creates a new thread) I also want to trace the child processes the traced process creates (and also the threads created by the process). I know that it can be done using PTRACE_O_TRACEFORK
, PTRACE_O_TRACEVFORK
and PTRACE_O_TRACECLONE
, but from the man
documentation, it is very hard to figure out how exactly it is done. I need some examples on this.
Edit:
I found a similar question here: How to ptrace a multi-threaded application? I tried it with the following code. This code tracks the system calls of the started process and it is supposed to track the forked processes too. It is run after a fork()
in the parent process (the child calls a PTRACE_TRACEME
and an exec()
).
Edit2:
I made some more modifications on the code, with some more progress.
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
is just a macro that checks the result code and throw an exception with the description of errno
in it.
Apparently, when I get the event of a fork/clone the new process doesn't exist yet, and I get a "Process does not exist" error message if I try to ptrace it. If I put a sleep before trying to ptrace the new process, I don't get an error message. Now when the program gets to the point of fork/clone, it starts to trace the new process, but it never gets to the return point of the clone()
syscall in the parent process, which means that the child finishes successfully, but the parent hangs at its fork point.