Merge branch 'blender2.7'
[blender.git] / source / creator / creator_signals.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file \ingroup creator
18  */
19
20 #ifndef WITH_PYTHON_MODULE
21
22 #if defined(__linux__) && defined(__GNUC__)
23 #  define _GNU_SOURCE
24 #  include <fenv.h>
25 #endif
26
27 #if (defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__)))
28 #  define OSX_SSE_FPE
29 #  include <xmmintrin.h>
30 #endif
31
32 #ifdef WIN32
33 #  if defined(_MSC_VER) && defined(_M_X64)
34 #    include <math.h> /* needed for _set_FMA3_enable */
35 #  endif
36 #  include <windows.h>
37 #  include <float.h>
38 #endif
39
40 #include <stdlib.h>
41 #include <string.h>
42 #include <errno.h>
43
44 #include "BLI_sys_types.h"
45
46 #ifdef WIN32
47 #  include "BLI_winstuff.h"
48 #endif
49 #include "BLI_utildefines.h"
50 #include "BLI_string.h"
51 #include "BLI_path_util.h"
52 #include "BLI_fileops.h"
53 #include "BLI_system.h"
54 #include BLI_SYSTEM_PID_H
55
56 #include "BKE_appdir.h"  /* BKE_tempdir_base */
57 #include "BKE_blender_version.h"
58 #include "BKE_global.h"
59 #include "BKE_main.h"
60 #include "BKE_report.h"
61
62 #include <signal.h>
63
64 #include "creator_intern.h"  /* own include */
65
66 // #define USE_WRITE_CRASH_BLEND
67 #ifdef USE_WRITE_CRASH_BLEND
68 #  include "BKE_undo_system.h"
69 #  include "BLO_undofile.h"
70 #endif
71
72 /* set breakpoints here when running in debug mode, useful to catch floating point errors */
73 #if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
74 static void sig_handle_fpe(int UNUSED(sig))
75 {
76         fprintf(stderr, "debug: SIGFPE trapped\n");
77 }
78 #endif
79
80 /* handling ctrl-c event in console */
81 #if !defined(WITH_HEADLESS)
82 static void sig_handle_blender_esc(int sig)
83 {
84         static int count = 0;
85
86         G.is_break = true;  /* forces render loop to read queue, not sure if its needed */
87
88         if (sig == 2) {
89                 if (count) {
90                         printf("\nBlender killed\n");
91                         exit(2);
92                 }
93                 printf("\nSent an internal break event. Press ^C again to kill Blender\n");
94                 count++;
95         }
96 }
97 #endif
98
99 static void sig_handle_crash_backtrace(FILE *fp)
100 {
101         fputs("\n# backtrace\n", fp);
102         BLI_system_backtrace(fp);
103 }
104
105 static void sig_handle_crash(int signum)
106 {
107         wmWindowManager *wm = G_MAIN->wm.first;
108
109 #ifdef USE_WRITE_CRASH_BLEND
110         if (wm->undo_stack) {
111                 struct MemFile *memfile = BKE_undosys_stack_memfile_get_active(wm->undo_stack);
112                 if (memfile) {
113                         char fname[FILE_MAX];
114
115                         if (!G_MAIN->name[0]) {
116                                 BLI_make_file_string("/", fname, BKE_tempdir_base(), "crash.blend");
117                         }
118                         else {
119                                 BLI_strncpy(fname, G_MAIN->name, sizeof(fname));
120                                 BLI_path_extension_replace(fname, sizeof(fname), ".crash.blend");
121                         }
122
123                         printf("Writing: %s\n", fname);
124                         fflush(stdout);
125
126                         BLO_memfile_write_file(memfile, fname);
127                 }
128         }
129 #endif
130
131         FILE *fp;
132         char header[512];
133
134         char fname[FILE_MAX];
135
136         if (!G_MAIN->name[0]) {
137                 BLI_join_dirfile(fname, sizeof(fname), BKE_tempdir_base(), "blender.crash.txt");
138         }
139         else {
140                 BLI_join_dirfile(fname, sizeof(fname), BKE_tempdir_base(), BLI_path_basename(G_MAIN->name));
141                 BLI_path_extension_replace(fname, sizeof(fname), ".crash.txt");
142         }
143
144         printf("Writing: %s\n", fname);
145         fflush(stdout);
146
147 #ifndef BUILD_DATE
148         BLI_snprintf(header, sizeof(header), "# " BLEND_VERSION_FMT ", Unknown revision\n", BLEND_VERSION_ARG);
149 #else
150         BLI_snprintf(header, sizeof(header), "# " BLEND_VERSION_FMT ", Commit date: %s %s, Hash %s\n",
151                      BLEND_VERSION_ARG, build_commit_date, build_commit_time, build_hash);
152 #endif
153
154         /* open the crash log */
155         errno = 0;
156         fp = BLI_fopen(fname, "wb");
157         if (fp == NULL) {
158                 fprintf(stderr, "Unable to save '%s': %s\n",
159                         fname, errno ? strerror(errno) : "Unknown error opening file");
160         }
161         else {
162                 if (wm) {
163                         BKE_report_write_file_fp(fp, &wm->reports, header);
164                 }
165
166                 sig_handle_crash_backtrace(fp);
167
168                 fclose(fp);
169         }
170
171         /* Delete content of temp dir! */
172         BKE_tempdir_session_purge();
173
174         /* really crash */
175         signal(signum, SIG_DFL);
176 #ifndef WIN32
177         kill(getpid(), signum);
178 #else
179         TerminateProcess(GetCurrentProcess(), signum);
180 #endif
181 }
182
183 #ifdef WIN32
184 LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
185 {
186         switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
187                 case EXCEPTION_ACCESS_VIOLATION:
188                         fputs("Error   : EXCEPTION_ACCESS_VIOLATION\n", stderr);
189                         break;
190                 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
191                         fputs("Error   : EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr);
192                         break;
193                 case EXCEPTION_BREAKPOINT:
194                         fputs("Error   : EXCEPTION_BREAKPOINT\n", stderr);
195                         break;
196                 case EXCEPTION_DATATYPE_MISALIGNMENT:
197                         fputs("Error   : EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr);
198                         break;
199                 case EXCEPTION_FLT_DENORMAL_OPERAND:
200                         fputs("Error   : EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr);
201                         break;
202                 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
203                         fputs("Error   : EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr);
204                         break;
205                 case EXCEPTION_FLT_INEXACT_RESULT:
206                         fputs("Error   : EXCEPTION_FLT_INEXACT_RESULT\n", stderr);
207                         break;
208                 case EXCEPTION_FLT_INVALID_OPERATION:
209                         fputs("Error   : EXCEPTION_FLT_INVALID_OPERATION\n", stderr);
210                         break;
211                 case EXCEPTION_FLT_OVERFLOW:
212                         fputs("Error   : EXCEPTION_FLT_OVERFLOW\n", stderr);
213                         break;
214                 case EXCEPTION_FLT_STACK_CHECK:
215                         fputs("Error   : EXCEPTION_FLT_STACK_CHECK\n", stderr);
216                         break;
217                 case EXCEPTION_FLT_UNDERFLOW:
218                         fputs("Error   : EXCEPTION_FLT_UNDERFLOW\n", stderr);
219                         break;
220                 case EXCEPTION_ILLEGAL_INSTRUCTION:
221                         fputs("Error   : EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr);
222                         break;
223                 case EXCEPTION_IN_PAGE_ERROR:
224                         fputs("Error   : EXCEPTION_IN_PAGE_ERROR\n", stderr);
225                         break;
226                 case EXCEPTION_INT_DIVIDE_BY_ZERO:
227                         fputs("Error   : EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr);
228                         break;
229                 case EXCEPTION_INT_OVERFLOW:
230                         fputs("Error   : EXCEPTION_INT_OVERFLOW\n", stderr);
231                         break;
232                 case EXCEPTION_INVALID_DISPOSITION:
233                         fputs("Error   : EXCEPTION_INVALID_DISPOSITION\n", stderr);
234                         break;
235                 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
236                         fputs("Error   : EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr);
237                         break;
238                 case EXCEPTION_PRIV_INSTRUCTION:
239                         fputs("Error   : EXCEPTION_PRIV_INSTRUCTION\n", stderr);
240                         break;
241                 case EXCEPTION_SINGLE_STEP:
242                         fputs("Error   : EXCEPTION_SINGLE_STEP\n", stderr);
243                         break;
244                 case EXCEPTION_STACK_OVERFLOW:
245                         fputs("Error   : EXCEPTION_STACK_OVERFLOW\n", stderr);
246                         break;
247                 default:
248                         fputs("Error   : Unrecognized Exception\n", stderr);
249                         break;
250         }
251
252         fflush(stderr);
253
254         /* If this is a stack overflow then we can't walk the stack, so just show
255          * where the error happened */
256         if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
257                 HMODULE mod;
258                 CHAR modulename[MAX_PATH];
259                 LPVOID address = ExceptionInfo->ExceptionRecord->ExceptionAddress;
260
261                 fprintf(stderr, "Address : 0x%p\n", address);
262                 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) {
263                         if (GetModuleFileName(mod, modulename, MAX_PATH)) {
264                                 fprintf(stderr, "Module  : %s\n", modulename);
265                         }
266                 }
267
268                 fflush(stderr);
269
270 #ifdef NDEBUG
271                 TerminateProcess(GetCurrentProcess(), SIGSEGV);
272 #else
273                 sig_handle_crash(SIGSEGV);
274 #endif
275         }
276
277         return EXCEPTION_EXECUTE_HANDLER;
278 }
279 #endif
280
281 static void sig_handle_abort(int UNUSED(signum))
282 {
283         /* Delete content of temp dir! */
284         BKE_tempdir_session_purge();
285 }
286
287
288 void main_signal_setup(void)
289 {
290         if (app_state.signal.use_crash_handler) {
291 #ifdef WIN32
292                 SetUnhandledExceptionFilter(windows_exception_handler);
293 #else
294                 /* after parsing args */
295                 signal(SIGSEGV, sig_handle_crash);
296 #endif
297         }
298
299         if (app_state.signal.use_abort_handler) {
300                 signal(SIGABRT, sig_handle_abort);
301         }
302 }
303
304 void main_signal_setup_background(void)
305 {
306         /* for all platforms, even windos has it! */
307         BLI_assert(G.background);
308
309 #if !defined(WITH_HEADLESS)
310         signal(SIGINT, sig_handle_blender_esc);  /* ctrl c out bg render */
311 #endif
312 }
313
314
315 void main_signal_setup_fpe(void)
316 {
317 #if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
318         /* zealous but makes float issues a heck of a lot easier to find!
319          * set breakpoints on sig_handle_fpe */
320         signal(SIGFPE, sig_handle_fpe);
321
322 # if defined(__linux__) && defined(__GNUC__)
323         feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
324 # endif /* defined(__linux__) && defined(__GNUC__) */
325 # if defined(OSX_SSE_FPE)
326         /* OSX uses SSE for floating point by default, so here
327          * use SSE instructions to throw floating point exceptions */
328         _MM_SET_EXCEPTION_MASK(_MM_MASK_MASK & ~
329                                (_MM_MASK_OVERFLOW | _MM_MASK_INVALID | _MM_MASK_DIV_ZERO));
330 # endif /* OSX_SSE_FPE */
331 # if defined(_WIN32) && defined(_MSC_VER)
332         /* enables all fp exceptions */
333         _controlfp_s(NULL, 0, _MCW_EM);
334         /* hide the ones we don't care about */
335         _controlfp_s(NULL, _EM_DENORMAL | _EM_UNDERFLOW | _EM_INEXACT, _MCW_EM);
336 # endif /* _WIN32 && _MSC_VER */
337 #endif
338 }
339
340 #endif  /* WITH_PYTHON_MODULE */