technovelty

weblog of Ian Wienand

RSS  |  technovelty home  |  page of ian  |  ianw@ieee.org

Stopping at program at exit

Today I wanted a way to run a program to completion but stop it just before it is destroyed, therefore allowing me to inspect some /proc variables I was interested in.

As it happens the ptrace option PTRACE_O_TRACEEXIT is designed for just that. To use it, you first either attach to the process, or if forking a new process call PTRACE_TRACEME and exec as usual.

Then you use the ptrace call PTRACE_SETOPTIONS with the PTRACE_O_TRACEEXIT option in the data pointer, and wait for the traced process as usual (unfortunately due to a bug you will have to clag the #defines from the kernel header for now). There are now two conditions to handle:

  1. The child received a SIGTRAP with the PTRACE_EVENT_EXIT status set, signalling to us that it has exited. This is a terminal state; you can inspect the registers, etc, but the process can not come back to life now. This status is "overloaded" in the status returned by wait, e.g.
    /* wait for signal from child */
    waitpid(pid, &status, 0);
    
    /* any signal stops the child, so check that the incoming signal is a
    SIGTRAP and the event_exit flag is set */
    
    if ((WSTOPSIG(status) == SIGTRAP) &&
        (status & (PTRACE_EVENT_EXIT << 8)))
         ...
    
  2. Any other signal will have also caused the tracing parent to be woken, so we need to pass that signal through to the child process with a PTRACE_CONT call specifing the signal in the data pointer; e.g.
    /* if not, pass the original signal onto the child */
    if (ptrace(PTRACE_CONT, pid, 0, WSTOPSIG(status))) {
    	   perror("stopper: ptrace(PTRACE_CONT, ...)");
               exit(1);
    }
    

A straight forward example is stopper.c. The program allows you to run a program (or attach to a running program) and effecitvley pause it just before it exits, allowing you to inspect it and then manually dispose of it once you have the data you need.

posted at: Wed, 16 May 2007 13:32 | in /linux | permalink | add comment (1 others)

Posted by John Carter at Tue Jul 10 15:13:06 2007

Hmm. I was trying to use /usr/bin/time -v to get various resource usage stats about a process...

Alas it uses wait4 which seems (on ubuntu feisty fawn) returns rubbish.

So I wanted away to catch a process before exit and cat /proc/pid/status

Being lazy... A quick google brought me here.

Being impatient and having a measure of Hubris I tweaked your code slightly....

Try
stopper -f status -c ls

diff -u /tmp/stopper.c stopper.c
--- /tmp/stopper.c 2007-07-10 16:47:42.000000000 +1200
+++ stopper.c 2007-07-10 17:07:51.000000000 +1200
@@ -43,7 +43,7 @@

pid_t pid;

-void sigint_handler(int signum)
+void sigint_handler(int signum _attribute_((unused)))
{
/* let the child die */
if (ptrace(PTRACE_CONT, pid, 0, 0)) {
@@ -54,9 +54,11 @@
}

/* wait for the pid to exit */
-void wait_for_exit(void)
+void wait_for_exit( bool carryOn, char * fileName)
{
int status;
+  char command[80];
+
while (1) {
/* wait for signal from child */
waitpid(pid, &status, 0);
@@ -79,16 +81,43 @@
* (previously we just want a normal exit */
signal(SIGINT, sigint_handler);

- printf("\n-----------------------------------------------------\n");
- printf("\n\t  STOPPED PID %d\n", pid);
- printf("\t ctrl-c to continue\n");
- printf("\n-----------------------------------------------------\n");
- while (1)
- sleep(1000);
+  if( strcmp( fileName, "") != 0) { // Have a filename
+  if( strcmp( fileName, "list") == 0 || strcmp( fileName, "*") == 0) {
+  sprintf( command, "ls -l /proc/%d", pid);
+  system( command);
+  } else {
+  sprintf( command, "cat /proc/%d/%s", pid, fileName);
+  system( command);
+  }
+  }
+
+  if (!carryOn) {
+  printf("\n-----------------------------------------------------\n");
+  printf("\n\t  STOPPED PID %d\n", pid);
+  printf("\t ctrl-c to continue\n");
+  printf("\n-----------------------------------------------------\n");
+  while (1)
+  sleep(1000);
+  }
}


-char *usage = "Usage: stopper [ [-p pid] | command ]";
+char *usage = "Usage:
+  stopper [-f file] [-c] [ [-p pid] | command ]
+
+-f file\t\tCopy 'file' to stdout
+\t\t\tIf file is list, ls -l /proc/pid
+
+-c Just dump file and carry on.
+
+-p pid  Attach to process pid
+
+command Exec this command.
+
+
+Will stop the process just before exit to allow you to peek into /proc/pid
+
+";

int main(int argc, char *argv[])
{
@@ -96,15 +125,23 @@
extern int optind, opterr, optopt;
char c;
bool attaching = false;
+  char fileName[80]="";
+  bool carryOn = false;

while ((c = getopt(argc, argv,
-   "+p:")) != -1) {
+   "+f:cp:")) != -1) {
switch (c) {

case 'p':
pid = atoi(optarg);
attaching = true;
break;
+  case 'f':
+  strcpy( fileName, optarg);
+  break;
+  case 'c' :
+  carryOn = true;
+  break;
default:
printf("%s", usage);
exit(1);
@@ -133,7 +170,8 @@
}
else {
char *path;
- int m, n, len;
+ int m, len;
+  unsigned int n;

for (path = getenv("PATH"); path && *path; path += m) {
if (strchr(path, ':')) {
@@ -201,7 +239,7 @@
return -1;
}

- wait_for_exit();
+ wait_for_exit( carryOn, fileName);
}

} else {
@@ -218,7 +256,7 @@
return -1;
}

- wait_for_exit();
+ wait_for_exit(carryOn, fileName);
}

/* shouldn't get here */

Add a comment
*Name
*Email (not shown)
Website
*Comment:
*Word above?
* denotes required field

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.