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