initial commit
[goodguy/history.git] / cinelerra-5.0 / guicast / bcsignals.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "bcsignals.h"
23 #include "bcwindowbase.h"
24 #include "bckeyboard.h"
25
26 #include <ctype.h>
27 #include <dirent.h>
28 #include <execinfo.h>
29 #include <pwd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <sys/ioctl.h>
36 #include <sys/prctl.h>
37 #include <sys/types.h>
38
39 BC_Signals* BC_Signals::global_signals = 0;
40 static int signal_done = 0;
41 static int table_id = 0;
42
43 static bc_locktrace_t* new_bc_locktrace(void *ptr,
44         const char *title,
45         const char *location)
46 {
47         bc_locktrace_t *result = (bc_locktrace_t*)malloc(sizeof(bc_locktrace_t));
48         result->ptr = ptr;
49         result->title = title;
50         result->location = location;
51         result->is_owner = 0;
52         result->id = table_id++;
53         result->tid = pthread_self();
54         return result;
55 }
56
57
58 static struct sigaction old_segv = {0, }, old_intr = {0, };
59 static void handle_dump(int n, siginfo_t * info, void *sc);
60
61 const char *BC_Signals::trap_path = 0;
62 void *BC_Signals::trap_data = 0;
63 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
64 bool BC_Signals::trap_sigsegv = false;
65 bool BC_Signals::trap_sigintr = false;
66
67 static void uncatch_sig(int sig, struct sigaction &old)
68 {
69         struct sigaction act;
70         sigaction(sig, &old, &act);
71         old.sa_handler = 0;
72 }
73
74 static void catch_sig(int sig, struct sigaction &old)
75 {
76         struct sigaction act;
77         memset(&act, 0, sizeof(act));
78         act.sa_sigaction = handle_dump;
79         act.sa_flags = SA_SIGINFO;
80         sigaction(sig, &act, (!old.sa_handler ? &old : 0));
81 }
82
83 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
84 static void catch_intr() { catch_sig(SIGINT, old_intr); }
85 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
86 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
87
88 void BC_Signals::set_trap_path(const char *path)
89 {
90         trap_path = path;
91 }
92
93 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
94 {
95         trap_data = data;
96         trap_hook = hook;
97 }
98
99 void BC_Signals::set_catch_segv(bool v) {
100         if( v == trap_sigsegv ) return;
101         if( v ) catch_segv();
102         else uncatch_segv();
103         v = trap_sigsegv;
104 }
105
106 void BC_Signals::set_catch_intr(bool v) {
107         if( v == trap_sigintr ) return;
108         if( v ) catch_intr();
109         else uncatch_intr();
110         v = trap_sigintr;
111 }
112
113 typedef struct
114 {
115         int size;
116         void *ptr;
117         const char *location;
118 } bc_buffertrace_t;
119
120 static bc_buffertrace_t* new_bc_buffertrace(int size, void *ptr, const char *location)
121 {
122         bc_buffertrace_t *result = (bc_buffertrace_t*)malloc(sizeof(bc_buffertrace_t));
123         result->size = size;
124         result->ptr = ptr;
125         result->location = location;
126         return result;
127 }
128
129 static void bc_copy_textfile(FILE *ofp, const char *fmt,...)
130 {
131         va_list ap;    va_start(ap, fmt);
132         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
133         va_end(ap);
134         FILE *ifp = fopen(bfr,"r");
135         if( !ifp ) return;
136         while( fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
137         fclose(ifp);
138 }
139
140
141 // Need our own table to avoid recursion with the memory manager
142 typedef struct
143 {
144         void **values;
145         int size;
146         int allocation;
147 // This points to the next value to replace if the table wraps around
148         int current_value;
149 } bc_table_t;
150
151 static void* append_table(bc_table_t *table, void *ptr)
152 {
153         if(table->allocation <= table->size)
154         {
155                 if(table->allocation)
156                 {
157                         int new_allocation = table->allocation * 2;
158                         void **new_values = (void**)calloc(new_allocation, sizeof(void*));
159                         memcpy(new_values, table->values, sizeof(void*) * table->size);
160                         free(table->values);
161                         table->values = new_values;
162                         table->allocation = new_allocation;
163                 }
164                 else
165                 {
166                         table->allocation = 4096;
167                         table->values = (void**)calloc(table->allocation, sizeof(void*));
168                 }
169         }
170
171         table->values[table->size++] = ptr;
172         return ptr;
173 }
174
175 // Replace item in table pointed to by current_value and advance
176 // current_value
177 static void* overwrite_table(bc_table_t *table, void *ptr)
178 {
179         free(table->values[table->current_value]);
180         table->values[table->current_value++] = ptr;
181         if(table->current_value >= table->size) table->current_value = 0;
182         return 0;
183 }
184
185 static void clear_table(bc_table_t *table, int delete_objects)
186 {
187         if(delete_objects)
188         {
189                 for(int i = 0; i < table->size; i++)
190                 {
191                         free(table->values[i]);
192                 }
193         }
194         table->size = 0;
195 }
196
197 static void clear_table_entry(bc_table_t *table, int number, int delete_object)
198 {
199         if(delete_object) free(table->values[number]);
200         for(int i = number; i < table->size - 1; i++)
201         {
202                 table->values[i] = table->values[i + 1];
203         }
204         table->size--;
205 }
206
207 // Table of functions currently running.
208 static bc_table_t execution_table = { 0, 0, 0, 0 };
209
210 // Table of locked positions
211 static bc_table_t lock_table = { 0, 0, 0, 0 };
212
213 // Table of buffers
214 static bc_table_t memory_table = { 0, 0, 0, 0 };
215
216 static bc_table_t temp_files = { 0, 0, 0, 0 };
217
218 // Can't use Mutex because it would be recursive
219 static pthread_mutex_t *lock = 0;
220 static pthread_mutex_t *handler_lock = 0;
221 // incase lock set after task ends
222 static pthread_t last_lock_thread = 0;
223 static const char *last_lock_title = 0;
224 static const char *last_lock_location = 0;
225 // Don't trace memory until this is true to avoid initialization
226 static int trace_memory = 0;
227
228
229 static const char* signal_titles[] =
230 {
231         "NULL",
232         "SIGHUP",
233         "SIGINT",
234         "SIGQUIT",
235         "SIGILL",
236         "SIGTRAP",
237         "SIGABRT",
238         "SIGBUS",
239         "SIGFPE",
240         "SIGKILL",
241         "SIGUSR1",
242         "SIGSEGV",
243         "SIGUSR2",
244         "SIGPIPE",
245         "SIGALRM",
246         "SIGTERM"
247 };
248
249 void BC_Signals::dump_stack(FILE *fp)
250 {
251         void *buffer[256];
252         int total = backtrace (buffer, 256);
253         char **result = backtrace_symbols (buffer, total);
254         fprintf(fp, "BC_Signals::dump_stack\n");
255         for(int i = 0; i < total; i++)
256         {
257                 fprintf(fp, "%s\n", result[i]);
258         }
259 }
260
261 // Kill subprocesses
262 void BC_Signals::kill_subs()
263 {
264 // List /proc directory
265         DIR *dirstream;
266         struct dirent64 *new_filename;
267         struct stat ostat;
268         char path[BCTEXTLEN];
269         char string[BCTEXTLEN];
270
271         dirstream = opendir("/proc");
272         if(!dirstream) return;
273
274         while( (new_filename = readdir64(dirstream)) != 0 )
275         {
276 // All digits are numbers
277                 char *ptr = new_filename->d_name;
278                 int got_alpha = 0;
279                 while(*ptr)
280                 {
281                         if(*ptr == '.' || isalpha(*ptr++))
282                         {
283                                 got_alpha = 1;
284                                 break;
285                         }
286                 }
287
288                 if(got_alpha) continue;
289
290 // Must be a directory
291                 sprintf(path, "/proc/%s", new_filename->d_name);
292                 if(!stat(path, &ostat))
293                 {
294                         if(S_ISDIR(ostat.st_mode))
295                         {
296 // Read process stat
297                                 strcat(path, "/stat");
298 //printf("kill_subs %d %s\n", __LINE__, path);
299                                 FILE *fd = fopen(path, "r");
300
301 // Must search forwards because the file is 0 length
302                                 if(fd)
303                                 {
304                                         while(!feof(fd))
305                                         {
306                                                 char c = fgetc(fd);
307 //printf("kill_subs %d %d\n", __LINE__, c);
308                                                 if(c == ')')
309                                                 {
310 // Search for 2 spaces
311                                                         int spaces = 0;
312                                                         while(!feof(fd) && spaces < 2)
313                                                         {
314                                                                 c = fgetc(fd);
315                                                                 if(c == ' ')
316                                                                         spaces++;
317                                                         }
318
319 // Read in parent process
320                                                         ptr = string;
321                                                         while(!feof(fd))
322                                                         {
323                                                                 *ptr = fgetc(fd);
324                                                                 if(*ptr == ' ')
325                                                                 {
326                                                                         *ptr = 0;
327                                                                         break;
328                                                                 }
329                                                                 ptr++;
330                                                         }
331
332 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
333 // __LINE__,
334 // atoi(new_filename->d_name),
335 // getpid(),
336 // atoi(string));
337                                                         int parent_process = atoi(string);
338                                                         int child_process = atoi(new_filename->d_name);
339
340 // Kill if we're the parent
341                                                         if(getpid() == parent_process)
342                                                         {
343 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
344                                                                 kill(child_process, SIGKILL);
345                                                         }
346                                                 }
347                                         }
348
349                                         fclose(fd);
350                                 }
351                         }
352                 }
353         }
354 }
355
356 static void signal_entry(int signum)
357 {
358         signal(signum, SIG_DFL);
359
360         pthread_mutex_lock(handler_lock);
361         if(signal_done)
362         {
363                 pthread_mutex_unlock(handler_lock);
364                 exit(0);
365         }
366
367         signal_done = 1;
368         pthread_mutex_unlock(handler_lock);
369
370
371         printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
372                 signal_titles[signum],
373                 getpid(),
374                 execution_table.size);
375
376         BC_Signals::kill_subs();
377         BC_Signals::dump_traces();
378         BC_Signals::dump_locks();
379         BC_Signals::dump_buffers();
380         BC_Signals::delete_temps();
381
382 // Call user defined signal handler
383         BC_Signals::global_signals->signal_handler(signum);
384
385         abort();
386 }
387
388 static void signal_entry_recoverable(int signum)
389 {
390         printf("signal_entry_recoverable: got %s my pid=%d\n",
391                 signal_titles[signum],
392                 getpid());
393 }
394
395 // used to terminate child processes when program terminates
396 static void handle_exit(int signum)
397 {
398 //printf("child %d exit\n", getpid());
399         exit(0);
400 }
401
402 void BC_Signals::set_sighup_exit(int enable)
403 {
404         if( enable ) {
405 // causes SIGHUP to be generated when parent dies
406                 signal(SIGHUP, handle_exit);
407                 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
408 // prevents ^C from signalling child when attached to gdb
409                 setpgid(0, 0);
410                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
411         }
412         else {
413                 signal(SIGHUP, signal_entry);
414                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
415         }
416 }
417
418 BC_Signals::BC_Signals()
419 {
420 }
421
422 void BC_Signals::dump_traces(FILE *fp)
423 {
424 // Dump trace table
425         if(execution_table.size)
426         {
427                 for(int i = execution_table.current_value; i < execution_table.size; i++)
428                         fprintf(fp,"    %s\n", (char*)execution_table.values[i]);
429                 for(int i = 0; i < execution_table.current_value; i++)
430                         fprintf(fp,"    %s\n", (char*)execution_table.values[i]);
431         }
432
433 }
434
435 void BC_Signals::dump_locks(FILE *fp)
436 {
437 // Dump lock table
438 #ifdef TRACE_LOCKS
439         fprintf(fp,"signal_entry: lock table size=%d\n", lock_table.size);
440         for(int i = 0; i < lock_table.size; i++)
441         {
442                 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
443                 fprintf(fp,"    %p %s %s %p%s\n", table->ptr,
444                         table->title, table->location, (void*)table->tid,
445                         table->is_owner ? " *" : "");
446         }
447 #endif
448 }
449
450 void BC_Signals::dump_buffers(FILE *fp)
451 {
452 #ifdef TRACE_MEMORY
453         pthread_mutex_lock(lock);
454 // Dump buffer table
455         fprintf(fp,"BC_Signals::dump_buffers: buffer table size=%d\n", memory_table.size);
456         for(int i = 0; i < memory_table.size; i++)
457         {
458                 bc_buffertrace_t *entry = (bc_buffertrace_t*)memory_table.values[i];
459                 fprintf(fp,"    %d %p %s\n", entry->size, entry->ptr, entry->location);
460         }
461         pthread_mutex_unlock(lock);
462 #endif
463 }
464
465 void BC_Signals::delete_temps()
466 {
467         pthread_mutex_lock(lock);
468         if(temp_files.size) printf("BC_Signals::delete_temps: deleting %d temp files\n", temp_files.size);
469         for(int i = 0; i < temp_files.size; i++)
470         {
471                 printf("    %s\n", (char*)temp_files.values[i]);
472                 remove((char*)temp_files.values[i]);
473         }
474         pthread_mutex_unlock(lock);
475 }
476
477 void BC_Signals::reset_locks()
478 {
479         pthread_mutex_unlock(lock);
480 }
481
482 void BC_Signals::set_temp(char *string)
483 {
484         char *new_string = strdup(string);
485         append_table(&temp_files, new_string);
486 }
487
488 void BC_Signals::unset_temp(char *string)
489 {
490         for(int i = 0; i < temp_files.size; i++)
491         {
492                 if(!strcmp((char*)temp_files.values[i], string))
493                 {
494                         clear_table_entry(&temp_files, i, 1);
495                         break;
496                 }
497         }
498 }
499
500
501 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
502 {
503         char string[1024];
504         XGetErrorText(event->display, event->error_code, string, 1024);
505         fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d %s\n",
506                 event->error_code, event->request_code, event->minor_code, string);
507         return 0;
508 }
509
510
511 void BC_Signals::initialize()
512 {
513         BC_Signals::global_signals = this;
514         lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
515         handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
516         pthread_mutex_init(lock, 0);
517         pthread_mutex_init(handler_lock, 0);
518         old_err_handler = XSetErrorHandler(x_error_handler);
519         initialize2();
520 }
521
522 void BC_Signals::terminate()
523 {
524         BC_Signals::global_signals = 0;
525         uncatch_segv();  uncatch_intr();
526         signal(SIGHUP, SIG_DFL);
527         signal(SIGINT, SIG_DFL);
528         signal(SIGQUIT, SIG_DFL);
529         signal(SIGTERM, SIG_DFL);
530         signal(SIGFPE, SIG_DFL);
531         signal(SIGPIPE, SIG_DFL);
532         signal(SIGUSR2, SIG_DFL);
533         XSetErrorHandler(old_err_handler);
534 }
535
536 // callable from debugger
537 extern "C"
538 void dump()
539 {
540         BC_Signals::dump_traces();
541         BC_Signals::dump_locks();
542         BC_Signals::dump_buffers();
543 }
544
545 // kill SIGUSR2
546 void BC_Signals::signal_dump(int signum)
547 {
548         BC_KeyboardHandler::kill_grabs();
549         dump();
550         signal(SIGUSR2, signal_dump);
551 }
552
553
554
555
556
557
558 void BC_Signals::initialize2()
559 {
560         signal(SIGHUP, signal_entry);
561         signal(SIGINT, signal_entry);
562         signal(SIGQUIT, signal_entry);
563         // SIGKILL cannot be stopped
564         // signal(SIGKILL, signal_entry);
565         catch_segv();
566         signal(SIGTERM, signal_entry);
567         signal(SIGFPE, signal_entry);
568         signal(SIGPIPE, signal_entry_recoverable);
569         signal(SIGUSR2, signal_dump);
570 }
571
572
573 void BC_Signals::signal_handler(int signum)
574 {
575 printf("BC_Signals::signal_handler\n");
576 //      exit(0);
577 }
578
579 const char* BC_Signals::sig_to_str(int number)
580 {
581         return signal_titles[number];
582 }
583
584 #define TOTAL_TRACES 16
585
586 void BC_Signals::new_trace(const char *text)
587 {
588         if(!global_signals) return;
589         pthread_mutex_lock(lock);
590
591 // Wrap around
592         if(execution_table.size >= TOTAL_TRACES)
593         {
594                 overwrite_table(&execution_table, strdup(text));
595 //              clear_table(&execution_table, 1);
596         }
597         else
598         {
599                 append_table(&execution_table, strdup(text));
600         }
601         pthread_mutex_unlock(lock);
602 }
603
604 void BC_Signals::new_trace(const char *file, const char *function, int line)
605 {
606         char string[BCTEXTLEN];
607         snprintf(string, BCTEXTLEN, "%s: %s: %d", file, function, line);
608         new_trace(string);
609 }
610
611 void BC_Signals::delete_traces()
612 {
613         if(!global_signals) return;
614         pthread_mutex_lock(lock);
615         clear_table(&execution_table, 0);
616         pthread_mutex_unlock(lock);
617 }
618
619 // no canceling with lock held
620 void BC_Signals::lock_locks(const char *s)
621 {
622         pthread_mutex_lock(lock);
623         last_lock_thread = pthread_self();
624         last_lock_title = s;
625         last_lock_location = 0;
626 }
627
628 void BC_Signals::unlock_locks()
629 {
630         pthread_mutex_unlock(lock);
631 }
632
633 #define TOTAL_LOCKS 256
634
635 int BC_Signals::set_lock(void *ptr,
636         const char *title,
637         const char *location)
638 {
639         if(!global_signals) return 0;
640         bc_locktrace_t *table = 0;
641         int id_return = 0;
642
643         pthread_mutex_lock(lock);
644         last_lock_thread = pthread_self();
645         last_lock_title = title;
646         last_lock_location = location;
647         if(lock_table.size >= TOTAL_LOCKS)
648                 clear_table(&lock_table, 0);
649
650 // Put new lock entry
651         table = new_bc_locktrace(ptr, title, location);
652         append_table(&lock_table, table);
653         id_return = table->id;
654
655         pthread_mutex_unlock(lock);
656         return id_return;
657 }
658
659 void BC_Signals::set_lock2(int table_id)
660 {
661         if(!global_signals) return;
662
663         bc_locktrace_t *table = 0;
664         pthread_mutex_lock(lock);
665         for(int i = lock_table.size - 1; i >= 0; i--)
666         {
667                 table = (bc_locktrace_t*)lock_table.values[i];
668 // Got it.  Hasn't been unlocked/deleted yet.
669                 if(table->id == table_id)
670                 {
671                         table->is_owner = 1;
672                         table->tid = pthread_self();
673                         pthread_mutex_unlock(lock);
674                         return;
675                 }
676         }
677         pthread_mutex_unlock(lock);
678 }
679
680 void BC_Signals::unset_lock2(int table_id)
681 {
682         if(!global_signals) return;
683
684         bc_locktrace_t *table = 0;
685         pthread_mutex_lock(lock);
686         for(int i = lock_table.size - 1; i >= 0; i--)
687         {
688                 table = (bc_locktrace_t*)lock_table.values[i];
689                 if(table->id == table_id)
690                 {
691                         clear_table_entry(&lock_table, i, 1);
692                         break;
693                 }
694         }
695         pthread_mutex_unlock(lock);
696 }
697
698 void BC_Signals::unset_lock(void *ptr)
699 {
700         if(!global_signals) return;
701
702         bc_locktrace_t *table = 0;
703         pthread_mutex_lock(lock);
704
705 // Take off currently held entry
706         for(int i = 0; i < lock_table.size; i++)
707         {
708                 table = (bc_locktrace_t*)lock_table.values[i];
709                 if(table->ptr == ptr)
710                 {
711                         if(table->is_owner)
712                         {
713                                 clear_table_entry(&lock_table, i, 1);
714                                 break;
715                         }
716                 }
717         }
718
719         pthread_mutex_unlock(lock);
720 }
721
722
723 void BC_Signals::unset_all_locks(void *ptr)
724 {
725         if(!global_signals) return;
726         pthread_mutex_lock(lock);
727 // Take off previous lock entry
728         for(int i = 0; i < lock_table.size; )
729         {
730                 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
731                 if(table->ptr == ptr)
732                 {
733                         clear_table_entry(&lock_table, i, 1);
734                         continue;
735                 }
736                 ++i;
737         }
738         pthread_mutex_unlock(lock);
739 }
740
741 void BC_Signals::clear_locks_tid(pthread_t tid)
742 {
743         if(!global_signals) return;
744         pthread_mutex_lock(lock);
745 // Take off previous lock entry
746         for(int i = 0; i < lock_table.size; )
747         {
748                 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
749                 if(table->tid == tid)
750                 {
751                         clear_table_entry(&lock_table, i, 1);
752                         continue;
753                 }
754                 ++i;
755         }
756         pthread_mutex_unlock(lock);
757 }
758
759
760 void BC_Signals::enable_memory()
761 {
762         trace_memory = 1;
763 }
764
765 void BC_Signals::disable_memory()
766 {
767         trace_memory = 0;
768 }
769
770
771 void BC_Signals::set_buffer(int size, void *ptr, const char* location)
772 {
773         if(!global_signals) return;
774         if(!trace_memory) return;
775
776 //printf("BC_Signals::set_buffer %p %s\n", ptr, location);
777         pthread_mutex_lock(lock);
778         append_table(&memory_table, new_bc_buffertrace(size, ptr, location));
779         pthread_mutex_unlock(lock);
780 }
781
782 int BC_Signals::unset_buffer(void *ptr)
783 {
784         if(!global_signals) return 0;
785         if(!trace_memory) return 0;
786
787         int ret = 1;
788         pthread_mutex_lock(lock);
789         for(int i = 0; i < memory_table.size; i++)
790         {
791                 if(((bc_buffertrace_t*)memory_table.values[i])->ptr == ptr)
792                 {
793 //printf("BC_Signals::unset_buffer %p\n", ptr);
794                         clear_table_entry(&memory_table, i, 1);
795                         ret = 0;
796                         break;
797                 }
798         }
799
800         pthread_mutex_unlock(lock);
801 //      fprintf(stderr, "BC_Signals::unset_buffer buffer %p not found.\n", ptr);
802         return ret;
803 }
804
805
806 #include <ucontext.h>
807 #include <sys/wait.h>
808 #include "thread.h"
809
810 #if __i386__
811 #define IP eip
812 #endif
813 #if __x86_64__
814 #define IP rip
815 #endif
816 #ifndef IP
817 #error gotta have IP
818 #endif
819
820
821 static void handle_dump(int n, siginfo_t * info, void *sc)
822 {
823         uncatch_segv();  uncatch_intr();
824         signal(SIGSEGV, SIG_DFL);
825         signal(SIGINT, SIG_DFL);
826         // gotta be root, or the dump is worthless
827         int uid = getuid();
828         if( uid != 0 ) return;
829         ucontext_t *uc = (ucontext_t *)sc;
830         int pid = getpid(), tid = gettid();
831         struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
832         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
833                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
834                 (void*)c->IP, pid, tid);
835         FILE *fp = 0;
836         char fn[PATH_MAX];
837         if( BC_Signals::trap_path ) {
838                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
839                 fp = fopen(fn,"w");
840         }
841         if( fp ) {
842                 fprintf(stderr,"writing debug data to %s\n", fn);
843                 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
844                         n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
845                         (void*)c->IP, pid, tid);
846         }
847         else {
848                 strcpy(fn, "stdout");
849                 fp = stdout;
850         }
851         time_t t;  time(&t);
852         fprintf(fp,"created on %s", ctime(&t));
853         struct passwd *pw = getpwuid(uid);
854         if( pw ) {
855                 fprintf(fp,"        by %d:%d %s(%s)\n",
856                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
857         }
858         fprintf(fp,"\nTHREADS:\n");  Thread::dump_threads(fp);
859         fprintf(fp,"\nTRACES:\n");   BC_Signals::dump_traces(fp);
860         fprintf(fp,"\nLOCKS:\n");    BC_Signals::dump_locks(fp);
861         fprintf(fp,"\nBUFFERS:\n");  BC_Signals::dump_buffers(fp);
862         if( BC_Signals::trap_hook ) {
863                 fprintf(fp,"\nMAIN HOOK:\n");
864                 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
865         }
866         fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(fp,"/proc/meminfo");
867         fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(fp,"/proc/%d/maps",pid);
868         fprintf(fp,"\n\n");
869         if( fp != stdout ) fclose(fp);
870         char cmd[1024], *cp = cmd;
871         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
872                 "-ex \"thread apply all info registers\" "
873                 "-ex \"thread apply all bt full\" "
874                 "-ex \"quit\"", pid, pid);
875         if( fp != stdout )
876                 cp += sprintf(cp," >> \"%s\"", fn);
877         cp += sprintf(cp," 2>&1");
878 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
879         pid = vfork();
880         if( pid < 0 ) {
881                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
882                 return;
883         }
884         if( pid > 0 ) {
885                 waitpid(pid,0,0);
886                 fprintf(stderr,"** dump complete\n");
887                 return;
888         }
889         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
890         execvp(argv[0], &argv[0]);
891 }
892
893
894
895
896
897 #ifdef TRACE_MEMORY
898
899 // void* operator new(size_t size)
900 // {
901 // //printf("new 1 %d\n", size);
902 //     void *result = malloc(size);
903 //      BUFFER(size, result, "new");
904 // //printf("new 2 %d\n", size);
905 //      return result;
906 // }
907 //
908 // void* operator new[](size_t size)
909 // {
910 // //printf("new [] 1 %d\n", size);
911 //     void *result = malloc(size);
912 //      BUFFER(size, result, "new []");
913 // //printf("new [] 2 %d\n", size);
914 //      return result;
915 // }
916 //
917 // void operator delete(void *ptr)
918 // {
919 // //printf("delete 1 %p\n", ptr);
920 //      UNBUFFER(ptr);
921 // //printf("delete 2 %p\n", ptr);
922 //     free(ptr);
923 // }
924 //
925 // void operator delete[](void *ptr)
926 // {
927 // //printf("delete [] 1 %p\n", ptr);
928 //      UNBUFFER(ptr);
929 //     free(ptr);
930 // //printf("delete [] 2 %p\n", ptr);
931 // }
932
933
934 #endif