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