medfall

A super great game engine
Log | Files | Refs

autogdb.h (2887B)


      1 #pragma once
      2 
      3 #if PLATFORM_LINUX
      4 
      5 #include <sys/ptrace.h>
      6 #include <sys/wait.h>
      7 
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <signal.h>
     11 #include <unistd.h>
     12 #include <err.h>
     13 
     14 static void pause_forever( int signal ) {
     15 	while( true ) {
     16 		pause();
     17 	}
     18 }
     19 
     20 static void uninstall_debug_signal_handlers() {
     21 	signal( SIGQUIT, SIG_IGN );
     22 	signal( SIGILL, pause_forever );
     23 	signal( SIGTRAP, SIG_IGN );
     24 	signal( SIGABRT, pause_forever );
     25 	signal( SIGSEGV, pause_forever );
     26 }
     27 
     28 static void reset_debug_signal_handlers() {
     29 	signal( SIGQUIT, SIG_DFL );
     30 	signal( SIGILL, SIG_DFL );
     31 	signal( SIGTRAP, SIG_DFL );
     32 	signal( SIGABRT, SIG_DFL );
     33 	signal( SIGSEGV, SIG_DFL );
     34 }
     35 
     36 static void prompt_to_run_gdb( int signal ) {
     37 	uninstall_debug_signal_handlers();
     38 
     39 	const char * signal_names[ NSIG ];
     40 	signal_names[ SIGQUIT ] = "SIGQUIT";
     41 	signal_names[ SIGILL ] = "SIGILL";
     42 	signal_names[ SIGTRAP ] = "SIGTRAP";
     43 	signal_names[ SIGABRT ] = "SIGABRT";
     44 	signal_names[ SIGSEGV ] = "SIGSEGV";
     45 
     46 	char crashed_pid[ 16 ];
     47 	snprintf( crashed_pid, sizeof( crashed_pid ), "%d", getpid() );
     48 	fprintf( stderr, "\nPID %s received %s. Debug? (y/n)\n", crashed_pid, signal_names[ signal ] );
     49 
     50 	char buf[ 2 ];
     51 	read( STDIN_FILENO, &buf, sizeof( buf ) );
     52 	if( buf[ 0 ] != 'y' ) {
     53 		exit( 1 );
     54 	}
     55 
     56 	// fork off and run gdb
     57 	pid_t child_pid = fork();
     58 	if( child_pid == -1 ) {
     59 		err( 1, "fork" );
     60 	}
     61 	reset_debug_signal_handlers();
     62 
     63 	if( child_pid == 0 ) {
     64 		// execlp( "cgdb", "cgdb", "--", "-q", "-p", crashed_pid, ( char * ) 0 );
     65 		execlp( "gdb", "gdb", "-q", "-p", crashed_pid, ( char * ) 0 );
     66 		err( 1, "execlp" );
     67 	}
     68 
     69 	if( signal != SIGQUIT && signal != SIGTRAP ) {
     70 		waitpid( child_pid, NULL, 0 );
     71 		exit( 1 );
     72 	}
     73 }
     74 
     75 static bool being_debugged() {
     76 	pid_t parent_pid = getpid();
     77 	pid_t child_pid = fork();
     78 	if( child_pid == -1 ) {
     79 		err( 1, "fork" );
     80 	}
     81 
     82 	if( child_pid == 0 ) {
     83 		// if we can't ptrace the parent then gdb is already there
     84 		if( ptrace( PTRACE_ATTACH, parent_pid, NULL, NULL ) != 0 ) {
     85 			if( errno == EPERM ) {
     86 				printf( "! echo 0 > /proc/sys/kernel/yama/ptrace_scope\n" );
     87 				printf( "! or\n" );
     88 				printf( "! sysctl kernel.yama.ptrace_scope=0\n" );
     89 			}
     90 			exit( 1 );
     91 		}
     92 
     93 		// ptrace automatically stops the process so wait for SIGSTOP and send PTRACE_CONT
     94 		waitpid( parent_pid, NULL, 0 );
     95 		ptrace( PTRACE_CONT, NULL, NULL );
     96 
     97 		// detach
     98 		ptrace( PTRACE_DETACH, parent_pid, NULL, NULL );
     99 		exit( 0 );
    100 	}
    101 
    102 	int status;
    103 	waitpid( child_pid, &status, 0 );
    104 	if( !WIFEXITED( status ) ) {
    105 		err( 1, "WIFEXITED" );
    106 	}
    107 
    108 	return WEXITSTATUS( status ) == 1;
    109 }
    110 
    111 static void install_debug_signal_handlers() {
    112 	if( being_debugged() ) return;
    113 
    114 	signal( SIGQUIT, prompt_to_run_gdb );
    115 	signal( SIGILL, prompt_to_run_gdb );
    116 	signal( SIGTRAP, prompt_to_run_gdb );
    117 	signal( SIGABRT, prompt_to_run_gdb );
    118 	signal( SIGSEGV, prompt_to_run_gdb );
    119 }
    120 
    121 #else
    122 
    123 static void install_debug_signal_handlers( bool ) { }
    124 
    125 #endif