Difference between revisions of "ptrace notes"
m |
m |
||
Line 2: | Line 2: | ||
__TOC__ | __TOC__ | ||
+ | == See Also == | ||
+ | |||
+ | [[LD_PRELOAD_notes]] is the classic way to manipulate the reality of a program. It is less powerful than '''ptrace''', but it is faster | ||
The '''ptrace''' API gives you much finer control from what you can get with [[LD_PRELOAD_notes|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. | The '''ptrace''' API gives you much finer control from what you can get with [[LD_PRELOAD_notes|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 == | == Slowing down a subprocess == | ||
− | This adds a '''usleep''' after each system call. | + | This adds a '''usleep''' after each system call. Build the small C program given and test with the following examples. Note that my environment has '''export TIMEFORMAT=$'\npcpu\t%P\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS''''. This environment variable causes the Bash built-in version of '''time''' to also display the percentage of CPU used by a process. If you are using a different shell then you may not see the '''pcpu''' row output. |
− | Running `time ./slow find /tmp` gave this: | + | Running a normal baseline `time find /tmp` gave this: |
+ | <pre> | ||
+ | pcpu 4.93 | ||
+ | real 0m0.081s | ||
+ | user 0m0.004s | ||
+ | sys 0m0.000s | ||
+ | </pre> | ||
+ | Running the same command under '''slow''', `time ./slow find /tmp`, gave this: | ||
<pre> | <pre> | ||
pcpu 0.48 | pcpu 0.48 | ||
Line 15: | Line 25: | ||
sys 0m0.116s | sys 0m0.116s | ||
</pre> | </pre> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | == source code to 'slow' == | ||
<pre> | <pre> | ||
/** | /** |
Revision as of 12:24, 9 April 2014
See Also
LD_PRELOAD_notes is the classic way to manipulate the reality of a program. It is less powerful than ptrace, but it is faster 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. Build the small C program given and test with the following examples. Note that my environment has export TIMEFORMAT=$'\npcpu\t%P\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS'. This environment variable causes the Bash built-in version of time to also display the percentage of CPU used by a process. If you are using a different shell then you may not see the pcpu row output.
Running a normal baseline `time find /tmp` gave this:
pcpu 4.93 real 0m0.081s user 0m0.004s sys 0m0.000s
Running the same command under slow, `time ./slow find /tmp`, gave this:
pcpu 0.48 real 0m31.929s user 0m0.040s sys 0m0.116s
source code to 'slow'
/** * 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; }