Cleanup: remove compilation warning in previous commit.
[blender-staging.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
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 #    include <float.h>
35 #    include <windows.h>
36 #  endif
37
38 #  include <errno.h>
39 #  include <stdlib.h>
40 #  include <string.h>
41
42 #  include "BLI_sys_types.h"
43
44 #  ifdef WIN32
45 #    include "BLI_winstuff.h"
46 #  endif
47 #  include "BLI_fileops.h"
48 #  include "BLI_path_util.h"
49 #  include "BLI_string.h"
50 #  include "BLI_system.h"
51 #  include "BLI_utildefines.h"
52 #  include BLI_SYSTEM_PID_H
53
54 #  include "BKE_appdir.h" /* BKE_tempdir_base */
55 #  include "BKE_blender_version.h"
56 #  include "BKE_global.h"
57 #  include "BKE_main.h"
58 #  include "BKE_report.h"
59
60 #  include <signal.h>
61
62 #  ifdef WITH_PYTHON
63 #    include "BPY_extern_python.h" /* BPY_python_backtrace */
64 #  endif
65
66 #  include "creator_intern.h" /* own include */
67
68 // #define USE_WRITE_CRASH_BLEND
69 #  ifdef USE_WRITE_CRASH_BLEND
70 #    include "BKE_undo_system.h"
71 #    include "BLO_undofile.h"
72 #  endif
73
74 /* set breakpoints here when running in debug mode, useful to catch floating point errors */
75 #  if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
76 static void sig_handle_fpe(int UNUSED(sig))
77 {
78   fprintf(stderr, "debug: SIGFPE trapped\n");
79 }
80 #  endif
81
82 /* handling ctrl-c event in console */
83 #  if !defined(WITH_HEADLESS)
84 static void sig_handle_blender_esc(int sig)
85 {
86   static int count = 0;
87
88   G.is_break = true; /* forces render loop to read queue, not sure if its needed */
89
90   if (sig == 2) {
91     if (count) {
92       printf("\nBlender killed\n");
93       exit(2);
94     }
95     printf("\nSent an internal break event. Press ^C again to kill Blender\n");
96     count++;
97   }
98 }
99 #  endif
100
101 static void sig_handle_crash_backtrace(FILE *fp)
102 {
103   fputs("\n# backtrace\n", fp);
104   BLI_system_backtrace(fp);
105 }
106
107 static void sig_handle_crash(int signum)
108 {
109   /* Might be called after WM/Main exit, so needs to be careful about NULL-checking before
110    * de-referencing. */
111
112   wmWindowManager *wm = G_MAIN ? G_MAIN->wm.first : NULL;
113
114 #  ifdef USE_WRITE_CRASH_BLEND
115   if (wm && 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 && G_MAIN->name[0])) {
121         BLI_join_dirfile(fname, sizeof(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 && 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(
154       header, sizeof(header), "# " BLEND_VERSION_FMT ", Unknown revision\n", BLEND_VERSION_ARG);
155 #  else
156   BLI_snprintf(header,
157                sizeof(header),
158                "# " BLEND_VERSION_FMT ", Commit date: %s %s, Hash %s\n",
159                BLEND_VERSION_ARG,
160                build_commit_date,
161                build_commit_time,
162                build_hash);
163 #  endif
164
165   /* open the crash log */
166   errno = 0;
167   fp = BLI_fopen(fname, "wb");
168   if (fp == NULL) {
169     fprintf(stderr,
170             "Unable to save '%s': %s\n",
171             fname,
172             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 #  ifdef WITH_PYTHON
182     /* Generate python back-trace if Python is currently active. */
183     BPY_python_backtrace(fp);
184 #  endif
185
186     fclose(fp);
187   }
188
189   /* Delete content of temp dir! */
190   BKE_tempdir_session_purge();
191
192   /* really crash */
193   signal(signum, SIG_DFL);
194 #  ifndef WIN32
195   kill(getpid(), signum);
196 #  else
197   TerminateProcess(GetCurrentProcess(), signum);
198 #  endif
199 }
200
201 #  ifdef WIN32
202 extern LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo)
203 {
204   /* If this is a stack overflow then we can't walk the stack, so just try to show
205    * where the error happened */
206   if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
207     HMODULE mod;
208     CHAR modulename[MAX_PATH];
209     LPVOID address = ExceptionInfo->ExceptionRecord->ExceptionAddress;
210     fprintf(stderr, "Error   : EXCEPTION_STACK_OVERFLOW\n");
211     fprintf(stderr, "Address : 0x%p\n", address);
212     if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) {
213       if (GetModuleFileName(mod, modulename, MAX_PATH)) {
214         fprintf(stderr, "Module  : %s\n", modulename);
215       }
216     }
217   }
218   else {
219     BLI_windows_handle_exception(ExceptionInfo);
220     sig_handle_crash(SIGSEGV);
221   }
222
223   return EXCEPTION_EXECUTE_HANDLER;
224 }
225 #  endif
226
227 static void sig_handle_abort(int UNUSED(signum))
228 {
229   /* Delete content of temp dir! */
230   BKE_tempdir_session_purge();
231 }
232
233 void main_signal_setup(void)
234 {
235   if (app_state.signal.use_crash_handler) {
236 #  ifdef WIN32
237     SetUnhandledExceptionFilter(windows_exception_handler);
238 #  else
239     /* after parsing args */
240     signal(SIGSEGV, sig_handle_crash);
241 #  endif
242   }
243
244 #  ifdef WIN32
245   /* Prevent any error mode dialogs from hanging the application. */
246   SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX |
247                SEM_NOOPENFILEERRORBOX);
248 #  endif
249
250   if (app_state.signal.use_abort_handler) {
251     signal(SIGABRT, sig_handle_abort);
252   }
253 }
254
255 void main_signal_setup_background(void)
256 {
257   /* for all platforms, even windows has it! */
258   BLI_assert(G.background);
259
260 #  if !defined(WITH_HEADLESS)
261   signal(SIGINT, sig_handle_blender_esc); /* ctrl c out bg render */
262 #  endif
263 }
264
265 void main_signal_setup_fpe(void)
266 {
267 #  if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
268   /* zealous but makes float issues a heck of a lot easier to find!
269    * set breakpoints on sig_handle_fpe */
270   signal(SIGFPE, sig_handle_fpe);
271
272 #    if defined(__linux__) && defined(__GNUC__)
273   feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
274 #    endif /* defined(__linux__) && defined(__GNUC__) */
275 #    if defined(OSX_SSE_FPE)
276   /* OSX uses SSE for floating point by default, so here
277    * use SSE instructions to throw floating point exceptions */
278   _MM_SET_EXCEPTION_MASK(_MM_MASK_MASK &
279                          ~(_MM_MASK_OVERFLOW | _MM_MASK_INVALID | _MM_MASK_DIV_ZERO));
280 #    endif /* OSX_SSE_FPE */
281 #    if defined(_WIN32) && defined(_MSC_VER)
282   /* enables all fp exceptions */
283   _controlfp_s(NULL, 0, _MCW_EM);
284   /* hide the ones we don't care about */
285   _controlfp_s(NULL, _EM_DENORMAL | _EM_UNDERFLOW | _EM_INEXACT, _MCW_EM);
286 #    endif /* _WIN32 && _MSC_VER */
287 #  endif
288 }
289
290 #endif /* WITH_PYTHON_MODULE */