Merge branch 'master' into blender2.8
[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
68 /* for passing information between creator and gameengine */
69 #ifdef WITH_GAMEENGINE
70 #  include "BL_System.h"
71 #else /* dummy */
72 #  define SYS_SystemHandle int
73 #endif
74
75 #include <signal.h>
76
77 #include "creator_intern.h"  /* own include */
78
79 // #define USE_WRITE_CRASH_BLEND
80 #ifdef USE_WRITE_CRASH_BLEND
81 #  include "BKE_undo_system.h"
82 #  include "BLO_undofile.h"
83 #endif
84
85 /* set breakpoints here when running in debug mode, useful to catch floating point errors */
86 #if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
87 static void sig_handle_fpe(int UNUSED(sig))
88 {
89         fprintf(stderr, "debug: SIGFPE trapped\n");
90 }
91 #endif
92
93 /* handling ctrl-c event in console */
94 #if !defined(WITH_HEADLESS)
95 static void sig_handle_blender_esc(int sig)
96 {
97         static int count = 0;
98
99         G.is_break = true;  /* forces render loop to read queue, not sure if its needed */
100
101         if (sig == 2) {
102                 if (count) {
103                         printf("\nBlender killed\n");
104                         exit(2);
105                 }
106                 printf("\nSent an internal break event. Press ^C again to kill Blender\n");
107                 count++;
108         }
109 }
110 #endif
111
112 static void sig_handle_crash_backtrace(FILE *fp)
113 {
114         fputs("\n# backtrace\n", fp);
115         BLI_system_backtrace(fp);
116 }
117
118 static void sig_handle_crash(int signum)
119 {
120         wmWindowManager *wm = G.main->wm.first;
121
122 #ifdef USE_WRITE_CRASH_BLEND
123         if (wm->undo_stack) {
124                 struct MemFile *memfile = BKE_undosys_stack_memfile_get_active(wm->undo_stack);
125                 if (memfile) {
126                         char fname[FILE_MAX];
127
128                         if (!G.main->name[0]) {
129                                 BLI_make_file_string("/", fname, BKE_tempdir_base(), "crash.blend");
130                         }
131                         else {
132                                 BLI_strncpy(fname, G.main->name, sizeof(fname));
133                                 BLI_replace_extension(fname, sizeof(fname), ".crash.blend");
134                         }
135
136                         printf("Writing: %s\n", fname);
137                         fflush(stdout);
138
139                         BLO_memfile_write_file(memfile, fname);
140                 }
141         }
142 #endif
143
144         FILE *fp;
145         char header[512];
146
147         char fname[FILE_MAX];
148
149         if (!G.main->name[0]) {
150                 BLI_join_dirfile(fname, sizeof(fname), BKE_tempdir_base(), "blender.crash.txt");
151         }
152         else {
153                 BLI_join_dirfile(fname, sizeof(fname), BKE_tempdir_base(), BLI_path_basename(G.main->name));
154                 BLI_replace_extension(fname, sizeof(fname), ".crash.txt");
155         }
156
157         printf("Writing: %s\n", fname);
158         fflush(stdout);
159
160 #ifndef BUILD_DATE
161         BLI_snprintf(header, sizeof(header), "# " BLEND_VERSION_FMT ", Unknown revision\n", BLEND_VERSION_ARG);
162 #else
163         BLI_snprintf(header, sizeof(header), "# " BLEND_VERSION_FMT ", Commit date: %s %s, Hash %s\n",
164                      BLEND_VERSION_ARG, build_commit_date, build_commit_time, build_hash);
165 #endif
166
167         /* open the crash log */
168         errno = 0;
169         fp = BLI_fopen(fname, "wb");
170         if (fp == NULL) {
171                 fprintf(stderr, "Unable to save '%s': %s\n",
172                         fname, errno ? strerror(errno) : "Unknown error opening file");
173         }
174         else {
175                 if (wm) {
176                         BKE_report_write_file_fp(fp, &wm->reports, header);
177                 }
178
179                 sig_handle_crash_backtrace(fp);
180
181                 fclose(fp);
182         }
183
184         /* Delete content of temp dir! */
185         BKE_tempdir_session_purge();
186
187         /* really crash */
188         signal(signum, SIG_DFL);
189 #ifndef WIN32
190         kill(getpid(), signum);
191 #else
192         TerminateProcess(GetCurrentProcess(), signum);
193 #endif
194 }
195
196 #ifdef WIN32
197 LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
198 {
199         switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
200                 case EXCEPTION_ACCESS_VIOLATION:
201                         fputs("Error   : EXCEPTION_ACCESS_VIOLATION\n", stderr);
202                         break;
203                 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
204                         fputs("Error   : EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr);
205                         break;
206                 case EXCEPTION_BREAKPOINT:
207                         fputs("Error   : EXCEPTION_BREAKPOINT\n", stderr);
208                         break;
209                 case EXCEPTION_DATATYPE_MISALIGNMENT:
210                         fputs("Error   : EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr);
211                         break;
212                 case EXCEPTION_FLT_DENORMAL_OPERAND:
213                         fputs("Error   : EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr);
214                         break;
215                 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
216                         fputs("Error   : EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr);
217                         break;
218                 case EXCEPTION_FLT_INEXACT_RESULT:
219                         fputs("Error   : EXCEPTION_FLT_INEXACT_RESULT\n", stderr);
220                         break;
221                 case EXCEPTION_FLT_INVALID_OPERATION:
222                         fputs("Error   : EXCEPTION_FLT_INVALID_OPERATION\n", stderr);
223                         break;
224                 case EXCEPTION_FLT_OVERFLOW:
225                         fputs("Error   : EXCEPTION_FLT_OVERFLOW\n", stderr);
226                         break;
227                 case EXCEPTION_FLT_STACK_CHECK:
228                         fputs("Error   : EXCEPTION_FLT_STACK_CHECK\n", stderr);
229                         break;
230                 case EXCEPTION_FLT_UNDERFLOW:
231                         fputs("Error   : EXCEPTION_FLT_UNDERFLOW\n", stderr);
232                         break;
233                 case EXCEPTION_ILLEGAL_INSTRUCTION:
234                         fputs("Error   : EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr);
235                         break;
236                 case EXCEPTION_IN_PAGE_ERROR:
237                         fputs("Error   : EXCEPTION_IN_PAGE_ERROR\n", stderr);
238                         break;
239                 case EXCEPTION_INT_DIVIDE_BY_ZERO:
240                         fputs("Error   : EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr);
241                         break;
242                 case EXCEPTION_INT_OVERFLOW:
243                         fputs("Error   : EXCEPTION_INT_OVERFLOW\n", stderr);
244                         break;
245                 case EXCEPTION_INVALID_DISPOSITION:
246                         fputs("Error   : EXCEPTION_INVALID_DISPOSITION\n", stderr);
247                         break;
248                 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
249                         fputs("Error   : EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr);
250                         break;
251                 case EXCEPTION_PRIV_INSTRUCTION:
252                         fputs("Error   : EXCEPTION_PRIV_INSTRUCTION\n", stderr);
253                         break;
254                 case EXCEPTION_SINGLE_STEP:
255                         fputs("Error   : EXCEPTION_SINGLE_STEP\n", stderr);
256                         break;
257                 case EXCEPTION_STACK_OVERFLOW:
258                         fputs("Error   : EXCEPTION_STACK_OVERFLOW\n", stderr);
259                         break;
260                 default:
261                         fputs("Error   : Unrecognized Exception\n", stderr);
262                         break;
263         }
264
265         fflush(stderr);
266
267         /* If this is a stack overflow then we can't walk the stack, so just show
268          * where the error happened */
269         if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) {
270                 HMODULE mod;
271                 CHAR modulename[MAX_PATH];
272                 LPVOID address = ExceptionInfo->ExceptionRecord->ExceptionAddress;
273
274                 fprintf(stderr, "Address : 0x%p\n", address);
275                 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) {
276                         if (GetModuleFileName(mod, modulename, MAX_PATH)) {
277                                 fprintf(stderr, "Module  : %s\n", modulename);
278                         }
279                 }
280
281                 fflush(stderr);
282
283 #ifdef NDEBUG
284                 TerminateProcess(GetCurrentProcess(), SIGSEGV);
285 #else
286                 sig_handle_crash(SIGSEGV);
287 #endif
288         }
289
290         return EXCEPTION_EXECUTE_HANDLER;
291 }
292 #endif
293
294 static void sig_handle_abort(int UNUSED(signum))
295 {
296         /* Delete content of temp dir! */
297         BKE_tempdir_session_purge();
298 }
299
300
301 void main_signal_setup(void)
302 {
303         if (app_state.signal.use_crash_handler) {
304 #ifdef WIN32
305                 SetUnhandledExceptionFilter(windows_exception_handler);
306 #else
307                 /* after parsing args */
308                 signal(SIGSEGV, sig_handle_crash);
309 #endif
310         }
311
312         if (app_state.signal.use_abort_handler) {
313                 signal(SIGABRT, sig_handle_abort);
314         }
315 }
316
317 void main_signal_setup_background(void)
318 {
319         /* for all platforms, even windos has it! */
320         BLI_assert(G.background);
321
322 #if !defined(WITH_HEADLESS)
323         signal(SIGINT, sig_handle_blender_esc);  /* ctrl c out bg render */
324 #endif
325 }
326
327
328 void main_signal_setup_fpe(void)
329 {
330 #if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
331         /* zealous but makes float issues a heck of a lot easier to find!
332          * set breakpoints on sig_handle_fpe */
333         signal(SIGFPE, sig_handle_fpe);
334
335 # if defined(__linux__) && defined(__GNUC__)
336         feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
337 # endif /* defined(__linux__) && defined(__GNUC__) */
338 # if defined(OSX_SSE_FPE)
339         /* OSX uses SSE for floating point by default, so here
340          * use SSE instructions to throw floating point exceptions */
341         _MM_SET_EXCEPTION_MASK(_MM_MASK_MASK & ~
342                                (_MM_MASK_OVERFLOW | _MM_MASK_INVALID | _MM_MASK_DIV_ZERO));
343 # endif /* OSX_SSE_FPE */
344 # if defined(_WIN32) && defined(_MSC_VER)
345         _controlfp_s(NULL, 0, _MCW_EM); /* enables all fp exceptions */
346         _controlfp_s(NULL, _EM_DENORMAL | _EM_UNDERFLOW | _EM_INEXACT, _MCW_EM); /* hide the ones we don't care about */
347 # endif /* _WIN32 && _MSC_VER */
348 #endif
349 }
350
351 #endif  /* WITH_PYTHON_MODULE */