merge hv v6, rework trace methods
[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 #include "cstrdup.h"
28
29 #include <ctype.h>
30 #include <dirent.h>
31 #include <execinfo.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <sys/ioctl.h>
40 #include <sys/prctl.h>
41 #include <sys/types.h>
42
43 BC_Signals* BC_Signals::global_signals = 0;
44 static int signal_done = 0;
45
46 static struct sigaction old_segv = {0, }, old_intr = {0, };
47 static void handle_dump(int n, siginfo_t * info, void *sc);
48
49 const char *BC_Signals::trap_path = 0;
50 void *BC_Signals::trap_data = 0;
51 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
52 bool BC_Signals::trap_sigsegv = false;
53 bool BC_Signals::trap_sigintr = false;
54
55 static void uncatch_sig(int sig, struct sigaction &old)
56 {
57         struct sigaction act;
58         sigaction(sig, &old, &act);
59         old.sa_handler = 0;
60 }
61
62 static void catch_sig(int sig, struct sigaction &old)
63 {
64         struct sigaction act;
65         memset(&act, 0, sizeof(act));
66         act.sa_sigaction = handle_dump;
67         act.sa_flags = SA_SIGINFO;
68         sigaction(sig, &act, (!old.sa_handler ? &old : 0));
69 }
70
71 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
72 static void catch_intr() { catch_sig(SIGINT, old_intr); }
73 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
74 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
75
76 void BC_Signals::set_trap_path(const char *path)
77 {
78         trap_path = path;
79 }
80
81 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
82 {
83         trap_data = data;
84         trap_hook = hook;
85 }
86
87 void BC_Signals::set_catch_segv(bool v) {
88         if( v == trap_sigsegv ) return;
89         if( v ) catch_segv();
90         else uncatch_segv();
91         v = trap_sigsegv;
92 }
93
94 void BC_Signals::set_catch_intr(bool v) {
95         if( v == trap_sigintr ) return;
96         if( v ) catch_intr();
97         else uncatch_intr();
98         v = trap_sigintr;
99 }
100
101 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
102 {
103         va_list ap;    va_start(ap, fmt);
104         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
105         va_end(ap);
106         FILE *ifp = fopen(bfr,"r");
107         if( !ifp ) return;
108         while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
109         fclose(ifp);
110 }
111
112 // Can't use Mutex because it would be recursive
113 static pthread_mutex_t *handler_lock = 0;
114
115
116 static const char* signal_titles[] =
117 {
118         "NULL",
119         "SIGHUP",
120         "SIGINT",
121         "SIGQUIT",
122         "SIGILL",
123         "SIGTRAP",
124         "SIGABRT",
125         "SIGBUS",
126         "SIGFPE",
127         "SIGKILL",
128         "SIGUSR1",
129         "SIGSEGV",
130         "SIGUSR2",
131         "SIGPIPE",
132         "SIGALRM",
133         "SIGTERM"
134 };
135
136 void BC_Signals::dump_stack(FILE *fp)
137 {
138         void *buffer[256];
139         int total = backtrace (buffer, 256);
140         char **result = backtrace_symbols (buffer, total);
141         fprintf(fp, "BC_Signals::dump_stack\n");
142         for(int i = 0; i < total; i++)
143         {
144                 fprintf(fp, "%s\n", result[i]);
145         }
146 }
147
148 // Kill subprocesses
149 void BC_Signals::kill_subs()
150 {
151 // List /proc directory
152         struct dirent64 *new_filename;
153         struct stat ostat;
154         char path[BCTEXTLEN], string[BCTEXTLEN];
155         DIR *dirstream = opendir("/proc");
156         if( !dirstream ) return;
157         pid_t ppid = getpid();
158
159         while( (new_filename = readdir64(dirstream)) != 0 ) {
160                 char *ptr = new_filename->d_name;
161                 while( *ptr && *ptr != '.' && !isalpha(*ptr) ) ++ptr;
162 // All digits are numbers
163                 if( *ptr ) continue;
164
165 // Must be a directory
166                 sprintf(path, "/proc/%s", new_filename->d_name);
167                 if( stat(path, &ostat) ) continue;
168                 if( !S_ISDIR(ostat.st_mode) ) continue;
169                 strcat(path, "/stat");
170                 FILE *fd = fopen(path, "r");
171                 if( !fd ) continue;
172                 while( !feof(fd) && fgetc(fd)!=')' );
173 //printf("kill_subs %d %d\n", __LINE__, c);
174                 for( int sp=2; !feof(fd) && sp>0; )
175                         if( fgetc(fd) == ' ' ) --sp;
176 // Read in parent process
177                 for( ptr=string; !feof(fd) && (*ptr=fgetc(fd))!=' '; ++ptr );
178                         if( (*ptr=fgetc(fd)) == ' ' ) break;
179                 *ptr = 0;
180
181 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
182 // __LINE__, atoi(new_filename->d_name), getpid(), atoi(string));
183                 int parent_process = atoi(string);
184 // Kill if we're the parent
185                 if( ppid == parent_process ) {
186                         int child_process = atoi(new_filename->d_name);
187 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
188                         kill(child_process, SIGKILL);
189                 }
190                 fclose(fd);
191         }
192 }
193
194 static void signal_entry(int signum)
195 {
196         signal(signum, SIG_DFL);
197
198         pthread_mutex_lock(handler_lock);
199         int done = signal_done;
200         signal_done = 1;
201         pthread_mutex_unlock(handler_lock);
202         if( done ) exit(0);
203
204         printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
205                 signal_titles[signum], getpid(), execution_table.size);
206
207         BC_Signals::kill_subs();
208         BC_Trace::dump_traces();
209         BC_Trace::dump_locks();
210         BC_Trace::dump_buffers();
211         BC_Trace::delete_temps();
212
213 // Call user defined signal handler
214         BC_Signals::global_signals->signal_handler(signum);
215
216         abort();
217 }
218
219 static void signal_entry_recoverable(int signum)
220 {
221         printf("signal_entry_recoverable: got %s my pid=%d\n",
222                 signal_titles[signum],
223                 getpid());
224 }
225
226 // used to terminate child processes when program terminates
227 static void handle_exit(int signum)
228 {
229 //printf("child %d exit\n", getpid());
230         exit(0);
231 }
232
233 void BC_Signals::set_sighup_exit(int enable)
234 {
235         if( enable ) {
236 // causes SIGHUP to be generated when parent dies
237                 signal(SIGHUP, handle_exit);
238                 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
239 // prevents ^C from signalling child when attached to gdb
240                 setpgid(0, 0);
241                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
242         }
243         else {
244                 signal(SIGHUP, signal_entry);
245                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
246         }
247 }
248
249 BC_Signals::BC_Signals()
250 {
251 }
252 BC_Signals::~BC_Signals()
253 {
254   BC_CModels::bcxfer_stop_slicers();
255 }
256
257
258 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
259 {
260         char string[1024];
261         XGetErrorText(event->display, event->error_code, string, 1024);
262         fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
263                 event->error_code, event->request_code, event->minor_code,
264                 (int64_t)event->resourceid, string);
265         return 0;
266 }
267
268
269 void BC_Signals::initialize()
270 {
271         BC_Signals::global_signals = this;
272         BC_Trace::global_trace = this;
273         handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
274         pthread_mutex_init(handler_lock, 0);
275         old_err_handler = XSetErrorHandler(x_error_handler);
276         initialize2();
277 }
278
279 void BC_Signals::terminate()
280 {
281         BC_Signals::global_signals = 0;
282         BC_Trace::global_trace = 0;
283         uncatch_segv();  uncatch_intr();
284         signal(SIGHUP, SIG_DFL);
285         signal(SIGINT, SIG_DFL);
286         signal(SIGQUIT, SIG_DFL);
287         signal(SIGTERM, SIG_DFL);
288         signal(SIGFPE, SIG_DFL);
289         signal(SIGPIPE, SIG_DFL);
290         signal(SIGUSR2, SIG_DFL);
291         XSetErrorHandler(old_err_handler);
292 }
293
294 // kill SIGUSR2
295 void BC_Signals::signal_dump(int signum)
296 {
297         BC_KeyboardHandler::kill_grabs();
298         dump();
299         signal(SIGUSR2, signal_dump);
300 }
301
302
303
304
305
306
307 void BC_Signals::initialize2()
308 {
309         signal(SIGHUP, signal_entry);
310         signal(SIGINT, signal_entry);
311         signal(SIGQUIT, signal_entry);
312         // SIGKILL cannot be stopped
313         // signal(SIGKILL, signal_entry);
314         catch_segv();
315         signal(SIGTERM, signal_entry);
316         signal(SIGFPE, signal_entry);
317         signal(SIGPIPE, signal_entry_recoverable);
318         signal(SIGUSR2, signal_dump);
319 }
320
321
322 void BC_Signals::signal_handler(int signum)
323 {
324 printf("BC_Signals::signal_handler\n");
325 //      exit(0);
326 }
327
328 const char* BC_Signals::sig_to_str(int number)
329 {
330         return signal_titles[number];
331 }
332
333
334 #include <ucontext.h>
335 #include <sys/wait.h>
336 #include "thread.h"
337
338 #if __i386__
339 #define IP eip
340 #endif
341 #if __x86_64__
342 #define IP rip
343 #endif
344 #ifndef IP
345 #error gotta have IP
346 #endif
347
348
349 static void handle_dump(int n, siginfo_t * info, void *sc)
350 {
351         uncatch_segv();  uncatch_intr();
352         signal(SIGSEGV, SIG_DFL);
353         signal(SIGINT, SIG_DFL);
354         // gotta be root, or the dump is worthless
355         int uid = getuid();
356         if( uid != 0 ) return;
357         ucontext_t *uc = (ucontext_t *)sc;
358         int pid = getpid(), tid = gettid();
359         struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
360         uint8_t *ip = (uint8_t *)c->IP;
361         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
362                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
363                 (void*)ip, pid, tid);
364         FILE *fp = 0;
365         char fn[PATH_MAX];
366         if( BC_Signals::trap_path ) {
367                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
368                 fp = fopen(fn,"w");
369         }
370         if( fp ) {
371                 fprintf(stderr,"writing debug data to %s\n", fn);
372                 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
373                         n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
374                         (void*)c->IP, pid, tid);
375         }
376         else {
377                 strcpy(fn, "stdout");
378                 fp = stdout;
379         }
380         time_t t;  time(&t);
381         fprintf(fp,"created on %s", ctime(&t));
382         struct passwd *pw = getpwuid(uid);
383         if( pw ) {
384                 fprintf(fp,"        by %d:%d %s(%s)\n",
385                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
386         }
387         fprintf(fp,"\nCPUS: %d\n",   BC_Resources::get_machine_cpus());
388         fprintf(fp,"\nCPUINFO:\n");  bc_copy_textfile(32, fp,"/proc/cpuinfo");
389         fprintf(fp,"\nTHREADS:\n");  BC_Trace::dump_threads(fp);
390         fprintf(fp,"\nTRACES:\n");   BC_Trace::dump_traces(fp);
391         fprintf(fp,"\nLOCKS:\n");    BC_Trace::dump_locks(fp);
392         fprintf(fp,"\nBUFFERS:\n");  BC_Trace::dump_buffers(fp);
393         if( BC_Signals::trap_hook ) {
394                 fprintf(fp,"\nMAIN HOOK:\n");
395                 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
396         }
397         fprintf(fp,"\nVERSION:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/version");
398         fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
399         fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
400         char proc_mem[64];
401         if( tid > 0 && tid != pid )
402                 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
403         else
404                 sprintf(proc_mem,"/proc/%d/mem",pid);
405         int pfd = open(proc_mem,O_RDONLY);
406         if( pfd >= 0 ) {
407                 fprintf(fp,"\nCODE:\n");
408                 for( int i=-32; i<32; ) {
409                         uint8_t v;  void *vp = (void *)(ip + i);
410                         if( !(i & 7) ) fprintf(fp,"%p:  ", vp);
411                         if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
412                         fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
413                         if( !(++i & 7) ) fprintf(fp,"\n");
414                 }
415                 fprintf(fp,"\n");
416                 close(pfd);
417         }
418         else
419                 fprintf(fp,"err opening: %s, %m\n", proc_mem);
420
421         fprintf(fp,"\n\n");
422         if( fp != stdout ) fclose(fp);
423         char cmd[1024], *cp = cmd;
424         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
425                 "-ex \"thread apply all info registers\" "
426                 "-ex \"thread apply all bt full\" "
427                 "-ex \"quit\"", pid, pid);
428         if( fp != stdout )
429                 cp += sprintf(cp," >> \"%s\"", fn);
430         cp += sprintf(cp," 2>&1");
431 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
432         pid = vfork();
433         if( pid < 0 ) {
434                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
435                 return;
436         }
437         if( pid > 0 ) {
438                 waitpid(pid,0,0);
439                 fprintf(stderr,"** dump complete\n");
440                 return;
441         }
442         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
443         execvp(argv[0], &argv[0]);
444 }
445