procps/ps/stacktrace.c
2002-02-01 22:47:29 +00:00

166 lines
3.3 KiB
C

/*
* Gnu debugger stack trace code provided by Peter Mattis
* <petm@CSUA.Berkeley.EDU> on Thu, 2 Nov 1995
*
* Modified for easy use by Albert Cahalan.
*/
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#define INTERACTIVE 0
#define STACK_TRACE 1
char *stored_prog_name = "You forgot to set \"program\"";
static int stack_trace_done;
/***********/
static void debug_stop(char **args){
execvp (args[0], args);
perror ("exec failed");
_exit (0);
}
/***********/
static void stack_trace_sigchld(int signum){
stack_trace_done = 1;
}
/************/
static void stack_trace(char **args){
pid_t pid;
int in_fd[2];
int out_fd[2];
fd_set fdset;
fd_set readset;
struct timeval tv;
int sel, index, state;
char buffer[256];
char c;
stack_trace_done = 0;
signal(SIGCHLD, stack_trace_sigchld);
if((pipe (in_fd) == -1) || (pipe (out_fd) == -1)){
perror ("could open pipe");
_exit (0);
}
pid = fork ();
if (pid == 0){
close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
close (2); dup (out_fd[1]); /* set the stderr to the out pipe */
execvp (args[0], args); /* exec gdb */
perror ("exec failed");
_exit (0);
} else {
if(pid == (pid_t) -1){
perror ("could not fork");
_exit (0);
}
}
FD_ZERO (&fdset);
FD_SET (out_fd[0], &fdset);
write (in_fd[1], "backtrace\n", 10);
write (in_fd[1], "p x = 0\n", 8);
write (in_fd[1], "quit\n", 5);
index = 0;
state = 0;
for(;;){
readset = fdset;
tv.tv_sec = 1;
tv.tv_usec = 0;
sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
if (sel == -1) break;
if((sel > 0) && (FD_ISSET (out_fd[0], &readset))){
if(read (out_fd[0], &c, 1)){
switch(state){
case 0:
if(c == '#'){
state = 1;
index = 0;
buffer[index++] = c;
}
break;
case 1:
buffer[index++] = c;
if((c == '\n') || (c == '\r')){
buffer[index] = 0;
fprintf (stderr, "%s", buffer);
state = 0;
index = 0;
}
break;
default:
break;
}
}
}
else if(stack_trace_done) break;
}
close (in_fd[0]);
close (in_fd[1]);
close (out_fd[0]);
close (out_fd[1]);
_exit (0);
}
/************/
void debug(int method, char *prog_name){
pid_t pid;
char buf[16];
char *args[4] = { "gdb", NULL, NULL, NULL };
int x;
snprintf (buf, 99, "%d", getpid ());
args[1] = prog_name;
args[2] = buf;
pid = fork ();
if(pid == 0){
switch (method){
case INTERACTIVE:
fprintf (stderr, "debug_stop\n");
debug_stop(args);
break;
case STACK_TRACE:
fprintf (stderr, "stack_trace\n");
stack_trace(args);
break;
}
_exit(0);
} else if(pid == (pid_t) -1){
perror ("could not fork");
return;
}
x = 1;
while(x); /* wait for debugger? */
}
/************/
static void stack_trace_sigsegv(int signum){
debug(STACK_TRACE, stored_prog_name);
}
/************/
void init_stack_trace(char *prog_name){
stored_prog_name = prog_name;
signal(SIGSEGV, stack_trace_sigsegv);
}