ptrace notes
From Noah.org
Jump to navigationJump to searchContents
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; }