#include #include #include #include /* This restarts a program under gdb, so that if it crashes, it can print out a stack backtrace (and the line number where it crashed). It's not 100% foolproof and doesn't correctly handle redirection of stdin and stdout. (I don't think you can get the unglobbed command line on unix except by invoking the program specially, which is what this code is trying to avoid.) Also it's careless with quoted parameters, both single and double quotes. However for a command with no stream redirection and simple arguments, it's rather nice. Remember to compile your programs -g. Inspired by the backtrace mechanism of Edinburgh's Imp compilers. Invoke it by adding the following two lines to main(), after declarations and before any code is run: extern void restart_under_gdb(int argc, char **argv); restart_under_gdb(argc, argv); Bugs: if your program outputs the string "Program exited normally." it will prematurely terminate! */ void restart_under_gdb (int argc, char **argv) { #ifndef _PATH_GDB #define _PATH_GDB "/usr/bin/gdb" #endif #define GDB "/bin/sh -c 'GDB=running; export GDB; %s -q -batch -x %s %s 2>&1'" #define GDB_JUNK "Program exited normally." int i; char *command; char *f; FILE *tmp, *execute; char *s; s = getenv ("GDB"); if ((s != NULL) && (strcmp (s, "running") == 0)) return; tmp = fopen(_PATH_GDB, "r"); if (tmp == NULL) return; /* no gdb, just run as normal */ fclose(tmp); f = tmpnam (NULL); if (f == NULL) { fprintf (stderr, "bad tmpnam\n"); exit (0); } tmp = fopen (f, "w"); if (tmp == NULL) { fprintf (stderr, "bad fopen\n"); exit (0); } fprintf (tmp, "run"); for (i = 1; i < argc; i++) fprintf (tmp, " %s", argv[i]); fprintf (tmp, "\n"); fprintf (tmp, "bt\n"); fprintf (tmp, "kill\n"); fprintf (tmp, "q\n"); fflush (tmp); fclose (tmp); command = malloc (strlen (GDB) + strlen (f) + strlen (argv[0]) + 1); if (command == NULL) { fprintf (stderr, "bad malloc\n"); exit (0); } sprintf (command, GDB, _PATH_GDB, f, argv[0]); fprintf(stderr, "POPEN: %s\n", command); execute = popen (command, "r"); { /* This quick & dirty hack is simply to copy all the output to stdout, until either end of file, or the GDB message "Progam terminated normally." is found. (We deliberately don't display that or anything which follows it) The rest of gdb's output is correctly suppressed by -q. Don't know why that one isn't too. The man page says it should be. This is extracted from a filter I wrote called 'upto' */ int c, i, l = strlen (GDB_JUNK); char *buffer = malloc (l + 1); /* We assume exit() always flushes any files left open */ for (;;) { restart: for (i = 0; i < l; i++) { buffer[i] = c = fgetc (execute); if (c == EOF) { buffer[i] = 0; fprintf (stdout, "%s", buffer); pclose (execute); remove (f); exit (0); } if (strchr (GDB_JUNK, c) == NULL) { buffer[i] = 0; /* newline should cause flush */ fprintf (stdout, "%s%c", buffer, c); fflush (stdout); goto restart; } } buffer[l] = 0; for (;;) { if (strcmp (buffer, GDB_JUNK) == 0) { pclose (execute); remove (f); exit (0); } fputc (*buffer, stdout); fflush (stdout); for (i = 1; i < l; i++) buffer[i - 1] = buffer[i]; i = fgetc (execute); if (strchr (GDB_JUNK, i) == NULL) { /* newline should cause flush */ buffer[l - 1] = 0; fprintf (stdout, "%s%c", buffer, i); fflush (stdout); break; } buffer[l - 1] = i; if (i == EOF) { buffer[l - 1] = 0; fprintf (stdout, "%s", buffer); pclose (execute); remove (f); exit (0); } } } } pclose (execute); remove (f); exit (0); }