4 * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcsignals.h"
23 #include "bcwindowbase.h"
24 #include "bccmodels.h"
25 #include "bckeyboard.h"
26 #include "bcresources.h"
37 #include <sys/ioctl.h>
38 #include <sys/prctl.h>
39 #include <sys/types.h>
41 BC_Signals* BC_Signals::global_signals = 0;
42 static int signal_done = 0;
43 static int table_id = 0;
45 static bc_locktrace_t* new_bc_locktrace(void *ptr,
49 bc_locktrace_t *result = (bc_locktrace_t*)malloc(sizeof(bc_locktrace_t));
51 result->title = title;
52 result->location = location;
54 result->id = table_id++;
55 result->tid = pthread_self();
60 static struct sigaction old_segv = {0, }, old_intr = {0, };
61 static void handle_dump(int n, siginfo_t * info, void *sc);
63 const char *BC_Signals::trap_path = 0;
64 void *BC_Signals::trap_data = 0;
65 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
66 bool BC_Signals::trap_sigsegv = false;
67 bool BC_Signals::trap_sigintr = false;
69 static void uncatch_sig(int sig, struct sigaction &old)
72 sigaction(sig, &old, &act);
76 static void catch_sig(int sig, struct sigaction &old)
79 memset(&act, 0, sizeof(act));
80 act.sa_sigaction = handle_dump;
81 act.sa_flags = SA_SIGINFO;
82 sigaction(sig, &act, (!old.sa_handler ? &old : 0));
85 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
86 static void catch_intr() { catch_sig(SIGINT, old_intr); }
87 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
88 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
90 void BC_Signals::set_trap_path(const char *path)
95 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
101 void BC_Signals::set_catch_segv(bool v) {
102 if( v == trap_sigsegv ) return;
103 if( v ) catch_segv();
108 void BC_Signals::set_catch_intr(bool v) {
109 if( v == trap_sigintr ) return;
110 if( v ) catch_intr();
119 const char *location;
122 static bc_buffertrace_t* new_bc_buffertrace(int size, void *ptr, const char *location)
124 bc_buffertrace_t *result = (bc_buffertrace_t*)malloc(sizeof(bc_buffertrace_t));
127 result->location = location;
131 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
133 va_list ap; va_start(ap, fmt);
134 char bfr[BCTEXTLEN]; vsnprintf(bfr, sizeof(bfr), fmt, ap);
136 FILE *ifp = fopen(bfr,"r");
138 while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
143 // Need our own table to avoid recursion with the memory manager
149 // This points to the next value to replace if the table wraps around
153 static void* append_table(bc_table_t *table, void *ptr)
155 if(table->allocation <= table->size)
157 if(table->allocation)
159 int new_allocation = table->allocation * 2;
160 void **new_values = (void**)calloc(new_allocation, sizeof(void*));
161 memcpy(new_values, table->values, sizeof(void*) * table->size);
163 table->values = new_values;
164 table->allocation = new_allocation;
168 table->allocation = 4096;
169 table->values = (void**)calloc(table->allocation, sizeof(void*));
173 table->values[table->size++] = ptr;
177 // Replace item in table pointed to by current_value and advance
179 static void* overwrite_table(bc_table_t *table, void *ptr)
181 free(table->values[table->current_value]);
182 table->values[table->current_value++] = ptr;
183 if(table->current_value >= table->size) table->current_value = 0;
187 static void clear_table(bc_table_t *table, int delete_objects)
191 for(int i = 0; i < table->size; i++)
193 free(table->values[i]);
199 static void clear_table_entry(bc_table_t *table, int number, int delete_object)
201 if(delete_object) free(table->values[number]);
202 for(int i = number; i < table->size - 1; i++)
204 table->values[i] = table->values[i + 1];
209 // Table of functions currently running.
210 static bc_table_t execution_table = { 0, 0, 0, 0 };
212 // Table of locked positions
213 static bc_table_t lock_table = { 0, 0, 0, 0 };
216 static bc_table_t memory_table = { 0, 0, 0, 0 };
218 static bc_table_t temp_files = { 0, 0, 0, 0 };
220 // Can't use Mutex because it would be recursive
221 static pthread_mutex_t *lock = 0;
222 static pthread_mutex_t *handler_lock = 0;
223 // incase lock set after task ends
224 static pthread_t last_lock_thread = 0;
225 static const char *last_lock_title = 0;
226 static const char *last_lock_location = 0;
227 // Don't trace memory until this is true to avoid initialization
228 static int trace_memory = 0;
231 static const char* signal_titles[] =
251 void BC_Signals::dump_stack(FILE *fp)
254 int total = backtrace (buffer, 256);
255 char **result = backtrace_symbols (buffer, total);
256 fprintf(fp, "BC_Signals::dump_stack\n");
257 for(int i = 0; i < total; i++)
259 fprintf(fp, "%s\n", result[i]);
264 void BC_Signals::kill_subs()
266 // List /proc directory
268 struct dirent64 *new_filename;
270 char path[BCTEXTLEN];
271 char string[BCTEXTLEN];
273 dirstream = opendir("/proc");
274 if(!dirstream) return;
276 while( (new_filename = readdir64(dirstream)) != 0 )
278 // All digits are numbers
279 char *ptr = new_filename->d_name;
283 if(*ptr == '.' || isalpha(*ptr++))
290 if(got_alpha) continue;
292 // Must be a directory
293 sprintf(path, "/proc/%s", new_filename->d_name);
294 if(!stat(path, &ostat))
296 if(S_ISDIR(ostat.st_mode))
299 strcat(path, "/stat");
300 //printf("kill_subs %d %s\n", __LINE__, path);
301 FILE *fd = fopen(path, "r");
303 // Must search forwards because the file is 0 length
309 //printf("kill_subs %d %d\n", __LINE__, c);
312 // Search for 2 spaces
314 while(!feof(fd) && spaces < 2)
321 // Read in parent process
334 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
336 // atoi(new_filename->d_name),
339 int parent_process = atoi(string);
340 int child_process = atoi(new_filename->d_name);
342 // Kill if we're the parent
343 if(getpid() == parent_process)
345 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
346 kill(child_process, SIGKILL);
358 static void signal_entry(int signum)
360 signal(signum, SIG_DFL);
362 pthread_mutex_lock(handler_lock);
365 pthread_mutex_unlock(handler_lock);
370 pthread_mutex_unlock(handler_lock);
373 printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
374 signal_titles[signum],
376 execution_table.size);
378 BC_Signals::kill_subs();
379 BC_Signals::dump_traces();
380 BC_Signals::dump_locks();
381 BC_Signals::dump_buffers();
382 BC_Signals::delete_temps();
384 // Call user defined signal handler
385 BC_Signals::global_signals->signal_handler(signum);
390 static void signal_entry_recoverable(int signum)
392 printf("signal_entry_recoverable: got %s my pid=%d\n",
393 signal_titles[signum],
397 // used to terminate child processes when program terminates
398 static void handle_exit(int signum)
400 //printf("child %d exit\n", getpid());
404 void BC_Signals::set_sighup_exit(int enable)
407 // causes SIGHUP to be generated when parent dies
408 signal(SIGHUP, handle_exit);
409 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
410 // prevents ^C from signalling child when attached to gdb
412 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
415 signal(SIGHUP, signal_entry);
416 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
420 BC_Signals::BC_Signals()
423 BC_Signals::~BC_Signals()
425 BC_CModels::bcxfer_stop_slicers();
428 void BC_Signals::dump_traces(FILE *fp)
431 if(execution_table.size)
433 for(int i = execution_table.current_value; i < execution_table.size; i++)
434 fprintf(fp," %s\n", (char*)execution_table.values[i]);
435 for(int i = 0; i < execution_table.current_value; i++)
436 fprintf(fp," %s\n", (char*)execution_table.values[i]);
441 void BC_Signals::dump_locks(FILE *fp)
445 fprintf(fp,"signal_entry: lock table size=%d\n", lock_table.size);
446 for(int i = 0; i < lock_table.size; i++)
448 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
449 fprintf(fp," %p %s %s %p%s\n", table->ptr,
450 table->title, table->location, (void*)table->tid,
451 table->is_owner ? " *" : "");
456 void BC_Signals::dump_buffers(FILE *fp)
459 pthread_mutex_lock(lock);
461 fprintf(fp,"BC_Signals::dump_buffers: buffer table size=%d\n", memory_table.size);
462 for(int i = 0; i < memory_table.size; i++)
464 bc_buffertrace_t *entry = (bc_buffertrace_t*)memory_table.values[i];
465 fprintf(fp," %d %p %s\n", entry->size, entry->ptr, entry->location);
467 pthread_mutex_unlock(lock);
471 void BC_Signals::delete_temps()
473 pthread_mutex_lock(lock);
474 if(temp_files.size) printf("BC_Signals::delete_temps: deleting %d temp files\n", temp_files.size);
475 for(int i = 0; i < temp_files.size; i++)
477 printf(" %s\n", (char*)temp_files.values[i]);
478 remove((char*)temp_files.values[i]);
480 pthread_mutex_unlock(lock);
483 void BC_Signals::reset_locks()
485 pthread_mutex_unlock(lock);
488 void BC_Signals::set_temp(char *string)
490 char *new_string = strdup(string);
491 append_table(&temp_files, new_string);
494 void BC_Signals::unset_temp(char *string)
496 for(int i = 0; i < temp_files.size; i++)
498 if(!strcmp((char*)temp_files.values[i], string))
500 clear_table_entry(&temp_files, i, 1);
507 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
510 XGetErrorText(event->display, event->error_code, string, 1024);
511 fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d %s\n",
512 event->error_code, event->request_code, event->minor_code, string);
517 void BC_Signals::initialize()
519 BC_Signals::global_signals = this;
520 lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
521 handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
522 pthread_mutex_init(lock, 0);
523 pthread_mutex_init(handler_lock, 0);
524 old_err_handler = XSetErrorHandler(x_error_handler);
528 void BC_Signals::terminate()
530 BC_Signals::global_signals = 0;
531 uncatch_segv(); uncatch_intr();
532 signal(SIGHUP, SIG_DFL);
533 signal(SIGINT, SIG_DFL);
534 signal(SIGQUIT, SIG_DFL);
535 signal(SIGTERM, SIG_DFL);
536 signal(SIGFPE, SIG_DFL);
537 signal(SIGPIPE, SIG_DFL);
538 signal(SIGUSR2, SIG_DFL);
539 XSetErrorHandler(old_err_handler);
542 // callable from debugger
546 BC_Signals::dump_traces();
547 BC_Signals::dump_locks();
548 BC_Signals::dump_buffers();
552 void BC_Signals::signal_dump(int signum)
554 BC_KeyboardHandler::kill_grabs();
556 signal(SIGUSR2, signal_dump);
564 void BC_Signals::initialize2()
566 signal(SIGHUP, signal_entry);
567 signal(SIGINT, signal_entry);
568 signal(SIGQUIT, signal_entry);
569 // SIGKILL cannot be stopped
570 // signal(SIGKILL, signal_entry);
572 signal(SIGTERM, signal_entry);
573 signal(SIGFPE, signal_entry);
574 signal(SIGPIPE, signal_entry_recoverable);
575 signal(SIGUSR2, signal_dump);
579 void BC_Signals::signal_handler(int signum)
581 printf("BC_Signals::signal_handler\n");
585 const char* BC_Signals::sig_to_str(int number)
587 return signal_titles[number];
590 #define TOTAL_TRACES 16
592 void BC_Signals::new_trace(const char *text)
594 if(!global_signals) return;
595 pthread_mutex_lock(lock);
598 if(execution_table.size >= TOTAL_TRACES)
600 overwrite_table(&execution_table, strdup(text));
601 // clear_table(&execution_table, 1);
605 append_table(&execution_table, strdup(text));
607 pthread_mutex_unlock(lock);
610 void BC_Signals::new_trace(const char *file, const char *function, int line)
612 char string[BCTEXTLEN];
613 snprintf(string, BCTEXTLEN, "%s: %s: %d", file, function, line);
617 void BC_Signals::delete_traces()
619 if(!global_signals) return;
620 pthread_mutex_lock(lock);
621 clear_table(&execution_table, 0);
622 pthread_mutex_unlock(lock);
625 // no canceling with lock held
626 void BC_Signals::lock_locks(const char *s)
628 pthread_mutex_lock(lock);
629 last_lock_thread = pthread_self();
631 last_lock_location = 0;
634 void BC_Signals::unlock_locks()
636 pthread_mutex_unlock(lock);
639 #define TOTAL_LOCKS 256
641 int BC_Signals::set_lock(void *ptr,
643 const char *location)
645 if(!global_signals) return 0;
646 bc_locktrace_t *table = 0;
649 pthread_mutex_lock(lock);
650 last_lock_thread = pthread_self();
651 last_lock_title = title;
652 last_lock_location = location;
653 if(lock_table.size >= TOTAL_LOCKS)
654 clear_table(&lock_table, 0);
656 // Put new lock entry
657 table = new_bc_locktrace(ptr, title, location);
658 append_table(&lock_table, table);
659 id_return = table->id;
661 pthread_mutex_unlock(lock);
665 void BC_Signals::set_lock2(int table_id)
667 if(!global_signals) return;
669 bc_locktrace_t *table = 0;
670 pthread_mutex_lock(lock);
671 for(int i = lock_table.size - 1; i >= 0; i--)
673 table = (bc_locktrace_t*)lock_table.values[i];
674 // Got it. Hasn't been unlocked/deleted yet.
675 if(table->id == table_id)
678 table->tid = pthread_self();
679 pthread_mutex_unlock(lock);
683 pthread_mutex_unlock(lock);
686 void BC_Signals::unset_lock2(int table_id)
688 if(!global_signals) return;
690 bc_locktrace_t *table = 0;
691 pthread_mutex_lock(lock);
692 for(int i = lock_table.size - 1; i >= 0; i--)
694 table = (bc_locktrace_t*)lock_table.values[i];
695 if(table->id == table_id)
697 clear_table_entry(&lock_table, i, 1);
701 pthread_mutex_unlock(lock);
704 void BC_Signals::unset_lock(void *ptr)
706 if(!global_signals) return;
708 bc_locktrace_t *table = 0;
709 pthread_mutex_lock(lock);
711 // Take off currently held entry
712 for(int i = 0; i < lock_table.size; i++)
714 table = (bc_locktrace_t*)lock_table.values[i];
715 if(table->ptr == ptr)
719 clear_table_entry(&lock_table, i, 1);
725 pthread_mutex_unlock(lock);
729 void BC_Signals::unset_all_locks(void *ptr)
731 if(!global_signals) return;
732 pthread_mutex_lock(lock);
733 // Take off previous lock entry
734 for(int i = 0; i < lock_table.size; )
736 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
737 if(table->ptr == ptr)
739 clear_table_entry(&lock_table, i, 1);
744 pthread_mutex_unlock(lock);
747 void BC_Signals::clear_locks_tid(pthread_t tid)
749 if(!global_signals) return;
750 pthread_mutex_lock(lock);
751 // Take off previous lock entry
752 for(int i = 0; i < lock_table.size; )
754 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
755 if(table->tid == tid)
757 clear_table_entry(&lock_table, i, 1);
762 pthread_mutex_unlock(lock);
766 void BC_Signals::enable_memory()
771 void BC_Signals::disable_memory()
777 void BC_Signals::set_buffer(int size, void *ptr, const char* location)
779 if(!global_signals) return;
780 if(!trace_memory) return;
782 //printf("BC_Signals::set_buffer %p %s\n", ptr, location);
783 pthread_mutex_lock(lock);
784 append_table(&memory_table, new_bc_buffertrace(size, ptr, location));
785 pthread_mutex_unlock(lock);
788 int BC_Signals::unset_buffer(void *ptr)
790 if(!global_signals) return 0;
791 if(!trace_memory) return 0;
794 pthread_mutex_lock(lock);
795 for(int i = 0; i < memory_table.size; i++)
797 if(((bc_buffertrace_t*)memory_table.values[i])->ptr == ptr)
799 //printf("BC_Signals::unset_buffer %p\n", ptr);
800 clear_table_entry(&memory_table, i, 1);
806 pthread_mutex_unlock(lock);
807 // fprintf(stderr, "BC_Signals::unset_buffer buffer %p not found.\n", ptr);
812 #include <ucontext.h>
813 #include <sys/wait.h>
827 static void handle_dump(int n, siginfo_t * info, void *sc)
829 uncatch_segv(); uncatch_intr();
830 signal(SIGSEGV, SIG_DFL);
831 signal(SIGINT, SIG_DFL);
832 // gotta be root, or the dump is worthless
834 if( uid != 0 ) return;
835 ucontext_t *uc = (ucontext_t *)sc;
836 int pid = getpid(), tid = gettid();
837 struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
838 fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
839 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
840 (void*)c->IP, pid, tid);
843 if( BC_Signals::trap_path ) {
844 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
848 fprintf(stderr,"writing debug data to %s\n", fn);
849 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
850 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
851 (void*)c->IP, pid, tid);
854 strcpy(fn, "stdout");
858 fprintf(fp,"created on %s", ctime(&t));
859 struct passwd *pw = getpwuid(uid);
861 fprintf(fp," by %d:%d %s(%s)\n",
862 pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
864 fprintf(fp,"\nCPUS: %d\n", BC_Resources::get_machine_cpus());
865 fprintf(fp,"\nCPUINFO:\n"); bc_copy_textfile(32, fp,"/proc/cpuinfo");
866 fprintf(fp,"\nTHREADS:\n"); Thread::dump_threads(fp);
867 fprintf(fp,"\nTRACES:\n"); BC_Signals::dump_traces(fp);
868 fprintf(fp,"\nLOCKS:\n"); BC_Signals::dump_locks(fp);
869 fprintf(fp,"\nBUFFERS:\n"); BC_Signals::dump_buffers(fp);
870 if( BC_Signals::trap_hook ) {
871 fprintf(fp,"\nMAIN HOOK:\n");
872 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
874 fprintf(fp,"\nVERSION:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/version");
875 fprintf(fp,"\nMEMINFO:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
876 fprintf(fp,"\nMAPS:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
878 if( fp != stdout ) fclose(fp);
879 char cmd[1024], *cp = cmd;
880 cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
881 "-ex \"thread apply all info registers\" "
882 "-ex \"thread apply all bt full\" "
883 "-ex \"quit\"", pid, pid);
885 cp += sprintf(cp," >> \"%s\"", fn);
886 cp += sprintf(cp," 2>&1");
887 //printf("handle_dump:: pid=%d, cmd='%s' fn='%s'\n",pid,cmd,fn);
890 fprintf(stderr,"** can't start gdb, dump abondoned\n");
895 fprintf(stderr,"** dump complete\n");
898 char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
899 execvp(argv[0], &argv[0]);
908 // void* operator new(size_t size)
910 // //printf("new 1 %d\n", size);
911 // void *result = malloc(size);
912 // BUFFER(size, result, "new");
913 // //printf("new 2 %d\n", size);
917 // void* operator new[](size_t size)
919 // //printf("new [] 1 %d\n", size);
920 // void *result = malloc(size);
921 // BUFFER(size, result, "new []");
922 // //printf("new [] 2 %d\n", size);
926 // void operator delete(void *ptr)
928 // //printf("delete 1 %p\n", ptr);
930 // //printf("delete 2 %p\n", ptr);
934 // void operator delete[](void *ptr)
936 // //printf("delete [] 1 %p\n", ptr);
939 // //printf("delete [] 2 %p\n", ptr);