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