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