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