/********************************************************
 * debug_me -- A module to start the debugger from 	*
 * 	a running proram.				*
 * 							*
 * Debugging can be started by:				*
 * 	a) a debug_me call				*
 * 	b) a failed assertion				*
 * 							*
 * Warning: This code is Linux specific.		*
 ********************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/param.h>
#include <stdlib.h>

#include "debug_me.h"



static int in_gdb = 0;    // True if gdb started 

/********************************************************
 * gdb_stop -- A place to stop the debugger		*
 * 							*
 * Note: This is not static so that the debugger can	*
 * easily find it.					*
 ********************************************************/
void gdb_stop(void)
{
    printf("Gdb stop\n");fflush(stdout);
}

/********************************************************
 * start_debugger -- Actually start the debugger	*
 ********************************************************/
static void start_debugger(void)
{
    int pid = getpid();         // Our PID
    char gdb_file_name[MAXPATHLEN];  // The name of the gdb file

    char flag_file[MAXPATHLEN];	// File that's used as a flag 
    				// to signal that gdb is running
				//
    FILE *gdb_file;             // The file with the gdb information in it
    char cmd[MAXPATHLEN+100];   // Command to start xterm

    if (in_gdb)
        return; /* Prevent double debugs */

    /*
     * Create a command file that contains
     * 	  attach <pid>	# Attaches to the process
     *    echo ....	# Echos a welcome message
     *    symbol /proc/<pid>/exe
     *    		# Loads the symbol table
     *    break gdb_stop # Set a breakpoint in 
     *    shell touch /tmp/gdb.flag.<pid>
     *    		# Create a file that tells us 
     *    		# that the debugger is running
     *    continue	# Continue the program
     */
    sprintf(gdb_file_name, "/tmp/gdb.%d", pid);
    gdb_file = fopen(gdb_file_name, "w");
    if (gdb_file == NULL)
    {
        fprintf(stderr, "ERROR: Unable to open %s\n", gdb_file_name);
	abort();
    }
    sprintf(flag_file, "/tmp/gdb.flag.%d", pid);
    fprintf(gdb_file, "attach %d\n", pid);
    fprintf(gdb_file, "echo \"Assert failed.  Debugger gdb started\\n\"\n");
    fprintf(gdb_file, "symbol /proc/%d/exe\n", pid);
    fprintf(gdb_file, "break gdb_stop\n");
    fprintf(gdb_file, "shell touch %s\n", flag_file);
    fprintf(gdb_file, "continue\n");
    fclose(gdb_file);
    /* Start a xterm window with the debugger in it */
    sprintf(cmd, "xterm -fg red -e gdb --command=%s &", gdb_file_name);
    system(cmd);
    
    /* Now sleep until the debugger starts and
     * creates the flag file */
    while (access(flag_file, F_OK) != 0)
    {
        sleep (1);
    }
    in_gdb = 1;
    gdb_stop();
}

/********************************************************
 * debug_me -- Start the debugger			*
 ********************************************************/
void debug_me(void)
{
    start_debugger();
    gdb_stop();
}


/********************************************************
 * __assert_fail -- Called when an assert fails.	*
 * 	Starts the debugger.				*
 *							*
 * Note: gcc specific.  Different compilers use 	*
 * 	different internal routines to handle bad 	*
 * 	asserts.					*
 ********************************************************/
void __assert_fail(
    const char *const what, 
    const char *const file, 
    const int line,
    const char *const funct
)
{
    printf("Assert failed: %s\n", what);
    printf("FAILURE at: %s:%d\n", file, line);
    printf("Function is %s\n", funct);


    debug_me();
    abort();
}
