ptrace notes
From Noah.org
(Redirected from ptrace)
Contents |
The ptrace API gives you much finer control from what you can get with LD_PRELOAD. Also LD_PRELOAD only lets you hook into shared object library calls. If an application was totally statically linked, then you won't ever have visibility. But ptrace gives single-step process control at the machine instruction level.
Slowing down a subprocess
This adds a usleep after each system call.
Running `time ./slow find /tmp` gave this:
pcpu 0.48 real 0m31.929s user 0m0.040s sys 0m0.116s
Running `time find /tmp` gave this:
pcpu 4.93 real 0m0.081s user 0m0.004s sys 0m0.000s
Note that my environment has TIMEFORMAT=$'\npcpu\t%P\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'.
/**
* This adds a trace around the given program and adds a usleep after each syscall.
* Could also set to sleep after every instruction in single-step mode, but that
* would make the program very slow. In fact, you usually wouldn't need to even
* add sleep. Just having this hook do nothing would be enough to make the child
* program painfully slow. In single-step mode you should note that system calls
* themselves are not traced, so you will still see line output flash one line at
* a time with a pause in between.
*
* To build:
* gcc slow.c -o slow
*
* Example run:
* ./slow find /tmp
*
* Copyright (c) 2010, Noah Spurrier <noah@noah.org>
*/
#include <stdio.h>
#include <sys/ptrace.h>
// #include <asm/ptrace-abi.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/syscall.h>
int main(int argc, char ** argv) {
int child_argv_start;
int status;
pid_t child;
// printf ("argc: %d, argv length: %d\n", argc, argv_length(argv));
child = fork();
if(child == 0) {
// Find end of parent option list to get start of child args.
// For the moment we assume parent has no options, so we
// hard code this to 1.
child_argv_start = 1;
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execvp(argv[child_argv_start], &argv[child_argv_start]);
}
else {
wait(&status);
// Check if child called exit before we even got started.
if(WIFEXITED(status))
_exit (WEXITSTATUS(status));
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
wait(&status);
while (1) {
// I should probably check if the child got terminated by a signal.
if(WIFEXITED(status))
break;
usleep (10000); // in microseconds (millionths of a second)
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
//ptrace(PTRACE_SINGLESTEP, child, NULL, NULL);
wait(&status);
}
}
_exit (WEXITSTATUS(status));
}
/*
* This returns the length of a NULL-terminated argv array.
*/
int argv_length(char **argv) {
int count;
char **p;
if (argv == NULL)
return 0;
for (count = 0, p = argv; *p; count++, p++)
;
return count;
}