Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
system_signal_handling.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016-2018 Peter Budai.
4 Copyright (C) 2016 Roman Lebedev.
5 Copyright (C) 2017 luzpaz.
6 Copyright (C) 2017 Tobias Ellinghaus.
7 Copyright (C) 2020 Pascal Obry.
8 Copyright (C) 2022 Aurélien PIERRE.
9 Copyright (C) 2022 Martin Bařinka.
10
11 darktable is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 darktable is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with darktable. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "system/mem_alloc.h" // for dt_free
30#include "common/sentry.h" // for dt_sentry_backtrace_captured
32#include <errno.h> // for errno
33#include <fcntl.h> // for O_APPEND, O_CREAT, O_WRONLY, open
34#include <glib.h> // for g_free, g_printerr, g_strdup_printf
35#include <glib/gstdio.h> // for g_unlink
36#include <signal.h> // for signal, SIGSEGV, SIG_ERR
37#include <stddef.h> // for NULL
38#include <stdio.h> // for dprintf, fprintf, stderr
39#include <string.h> // for strerror
40#include <unistd.h> // for STDOUT_FILENO, close, execlp, fork
41
42#ifdef __linux__
43#include <sys/prctl.h> // for PR_SET_PTRACER, prctl
44#endif
45
46#ifndef _WIN32
47#include <sys/wait.h> // for waitpid
48#endif
49
50#ifdef _WIN32
51#include <exchndl.h>
52#endif //_WIN32
53
54#if defined(__linux__) && !defined(PR_SET_PTRACER)
55#define PR_SET_PTRACER 0x59616d61
56#endif
57
59
60#if !defined(__APPLE__) && !defined(_WIN32)
61static dt_signal_handler_t *_dt_sigsegv_old_handler = NULL;
62#endif
63
64// Signals whose original disposition is saved before we install ours, so a library that
65// overwrites them cannot leave the process without a handler. Historically this existed because
66// GraphicsMagick's InitializeMagick() replaced every one of them; that dependency is gone, and
67// the preservation is now generic insurance against any library doing the same.
68#if !defined(_WIN32)
69static const int _signals_to_preserve[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGFPE,
70 SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGXCPU, SIGXFSZ };
71#else
72static const int _signals_to_preserve[] = { SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM };
73static LPTOP_LEVEL_EXCEPTION_FILTER _dt_exceptionfilter_old_handler = NULL;
74#endif
75
76#define _NUM_SIGNALS_TO_PRESERVE (sizeof(_signals_to_preserve) / sizeof(_signals_to_preserve[0]))
78
79#if(defined(__FreeBSD_version) && (__FreeBSD_version < 800071)) || (defined(OpenBSD) && (OpenBSD < 201305)) \
80 || defined(__SUNOS__)
81static int dprintf(int fd, const char *fmt, ...) __attribute__((format(printf, 2, 3)))
82{
83 va_list ap;
84 FILE *f = fdopen(fd, "a");
85 va_start(ap, fmt);
86 int rc = vfprintf(f, fmt, ap);
87 fclose(f);
88 va_end(ap);
89 return rc;
90}
91#endif
92
93#if !defined(__APPLE__) && !defined(_WIN32)
94static void _dt_sigsegv_handler(int param)
95{
96 // Sentry's crash handler runs first and chains here. If it already produced a
97 // gdb backtrace (attached to the crash report), don't run gdb a second time;
98 // just pass the signal on to the original handler.
100 {
101 _dt_sigsegv_old_handler(param);
102 return;
103 }
104
105 pid_t pid;
106 gchar *name_used;
107 int fout;
108 gboolean delete_file = FALSE;
109
110 if((fout = g_file_open_tmp("ansel_bt_XXXXXX.txt", &name_used, NULL)) == -1)
111 fout = STDOUT_FILENO; // just print everything to stdout
112
113 dprintf(fout, "this is %s reporting a segfault:\n\n", darktable_package_string);
114
115 if(fout != STDOUT_FILENO) close(fout);
116
117 gchar *pid_arg = g_strdup_printf("%d", (int)getpid());
118 gchar *exe_arg = g_strdup_printf("/proc/%s/exe", pid_arg);
119 gchar *log_file_arg = g_strdup_printf("set logging file %s", name_used);
120 const char *log_overwrite_arg = "set logging overwrite on";
121 const char *log_redirect_arg = "set logging redirect on";
122 const char *log_enabled_arg = "set logging enabled on";
123 const char *pagination_arg = "set pagination off";
124 const char *confirm_arg = "set confirm off";
125 const char *where_arg = "where";
126 const char *current_bt_arg = "bt full";
127 const char *current_thread_arg = "thread";
128 const char *info_registers_arg = "info registers";
129 const char *disassemble_pc_arg = "x/16i $pc";
130 const char *stack_words_arg = "x/16gx $sp";
131 const char *sharedlibrary_arg = "info sharedlibrary";
132 const char *mappings_arg = "info proc mappings";
133 const char *info_threads_arg = "info threads";
134 const char *thread_bt_arg = "thread apply all bt full";
135 const char *separator_a_arg = "echo \\n=========\\n\\n";
136 const char *separator_b_arg = "echo \\n=========\\n";
137 const char *separator_c_arg = "echo \\n========= current thread =========\\n";
138 const char *separator_d_arg = "echo \\n========= registers =========\\n";
139 const char *separator_e_arg = "echo \\n========= disassembly =========\\n";
140 const char *separator_f_arg = "echo \\n========= stack =========\\n";
141 const char *separator_g_arg = "echo \\n========= shared libraries =========\\n";
142 const char *separator_h_arg = "echo \\n========= mappings =========\\n";
143
144 if((pid = fork()) != -1)
145 {
146 if(pid)
147 {
148#ifdef __linux__
149 // Allow the child to ptrace us
150 prctl(PR_SET_PTRACER, pid, 0, 0, 0);
151#endif
152 waitpid(pid, NULL, 0);
153 g_printerr("backtrace written to %s\n", name_used);
154 }
155 else
156 {
157 if(fout != STDOUT_FILENO)
158 {
159 const int log_fd = open(name_used, O_WRONLY | O_APPEND);
160 if(log_fd != -1)
161 {
162 dup2(log_fd, STDOUT_FILENO);
163 dup2(log_fd, STDERR_FILENO);
164 close(log_fd);
165 }
166 }
167
168 if(execlp("gdb", "gdb", exe_arg, pid_arg, "-batch", "-ex", pagination_arg, "-ex", confirm_arg, "-ex",
169 log_file_arg, "-ex", log_overwrite_arg, "-ex", log_redirect_arg, "-ex", log_enabled_arg, "-ex",
170 where_arg, "-ex", separator_c_arg, "-ex", current_thread_arg, "-ex", current_bt_arg, "-ex",
171 separator_d_arg, "-ex", info_registers_arg, "-ex", separator_e_arg, "-ex", disassemble_pc_arg,
172 "-ex", separator_f_arg, "-ex", stack_words_arg, "-ex", separator_g_arg, "-ex",
173 sharedlibrary_arg, "-ex", separator_h_arg, "-ex", mappings_arg, "-ex", separator_a_arg, "-ex",
174 info_threads_arg, "-ex", separator_b_arg, "-ex", thread_bt_arg, NULL))
175 {
176 delete_file = TRUE;
177 g_printerr("an error occurred while trying to execute gdb. please check if gdb is installed on your "
178 "system.\n");
179 }
180 }
181 }
182 else
183 {
184 delete_file = TRUE;
185 g_printerr("an error occurred while trying to execute gdb.\n");
186 }
187
188 if(delete_file) g_unlink(name_used);
189 dt_free(pid_arg);
190 dt_free(exe_arg);
191 dt_free(log_file_arg);
192 dt_free(name_used);
193
194 /* pass it further to the old handler*/
195 _dt_sigsegv_old_handler(param);
196}
197#endif
198
200
201#if defined(_WIN32)
202
203static LONG WINAPI dt_toplevel_exception_handler(PEXCEPTION_POINTERS pExceptionInfo)
204{
205 // Sentry's crash handler runs first and chains here. If it already captured a
206 // (locally symbolicated) backtrace attached to the crash report, don't write
207 // a second one nor pop the dialog; just pass the exception on.
209 return _dt_exceptionfilter_old_handler(pExceptionInfo);
210
211 gchar *name_used;
212 int fout;
213 BOOL ok;
214
215 // Find a filename for the backtrace file
216 if((fout = g_file_open_tmp("ansel_bt_XXXXXX.txt", &name_used, NULL)) == -1)
217 fout = STDOUT_FILENO; // just print everything to stdout
218
219 FILE *fd = fdopen(fout, "wb");
220 fprintf(fd, "this is %s reporting an exception:\n\n", darktable_package_string);
221 fclose(fd);
222
223 if(fout != STDOUT_FILENO) close(fout);
224
225
226 // Set up logfile name
227 ok = ExcHndlSetLogFileNameA(name_used);
228 if(!ok)
229 {
230 g_printerr("backtrace logfile cannot be set to %s\n", name_used);
231 }
232 else
233 {
234 gchar *exception_message = g_strdup_printf("An unhandled exception occurred.\nBacktrace will be written to: %s "
235 "after you click on the OK button.\nIf you report this issue, "
236 "please share this backtrace with the developers.\n",
237 name_used);
238 wchar_t *wexception_message = g_utf8_to_utf16(exception_message, -1, NULL, NULL, NULL);
239 MessageBoxW(0, wexception_message, L"Error!", MB_OK);
240 dt_free(exception_message);
241 dt_free(wexception_message);
242 }
243
244 dt_free(name_used);
245
246 // finally call the original exception handler (which should be drmingw's exception handler)
247 return _dt_exceptionfilter_old_handler(pExceptionInfo);
248}
249
251{
252 // Set up drming's exception handler
253 ExcHndlInit();
254}
255#endif // defined(_WIN32)
256
257
259{
261
263
265 {
266 // save original handlers
267 for(int i = 0; i < _NUM_SIGNALS_TO_PRESERVE; i++)
268 {
269 const int signum = _signals_to_preserve[i];
270
271 prev = signal(signum, SIG_DFL);
272
273 if(SIG_ERR == prev) prev = SIG_DFL;
274
275 _orig_sig_handlers[i] = prev;
276 }
277 }
278
279 // restore handlers
280 for(int i = 0; i < _NUM_SIGNALS_TO_PRESERVE; i++)
281 {
282 const int signum = _signals_to_preserve[i];
283
284 (void)signal(signum, _orig_sig_handlers[i]);
285 }
286
287#if !defined(__APPLE__) && !defined(_WIN32)
288 // now, set our SIGSEGV handler.
289 // FIXME: what about SIGABRT?
290 prev = signal(SIGSEGV, &_dt_sigsegv_handler);
291
292 if(SIG_ERR != prev)
293 {
294 // we want the most original previous signal handler.
295 if(1 == _times_handlers_were_set) _dt_sigsegv_old_handler = prev;
296 }
297 else
298 {
299 const int errsv = errno;
300 fprintf(stderr, "[dt_set_signal_handlers] error: signal(SIGSEGV) returned SIG_ERR: %i (%s)\n", errsv,
301 strerror(errsv));
302 }
303#elif !defined(__APPLE__)
304 /*
305 Set up exception handler for backtrace on Windows
306 Works when there is NO SIGSEGV handler installed
307
308 SetUnhandledExceptionFilter handler must be saved on the first invocation, so that a library
309 which installs its own filter behind our back cannot displace ours permanently.
310 */
311
314 {
315 // Save UnhandledExceptionFilter handler which just has been set up
316 // This should be drmingw's exception handler
318 }
319 // Restore our UnhandledExceptionFilter handler no matter what anything else installed
320 SetUnhandledExceptionFilter(dt_toplevel_exception_handler);
321
322#endif
323}
324
325// clang-format off
326// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
327// vim: shiftwidth=2 expandtab tabstop=2 cindent
328// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
329// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
const float f
const char darktable_package_string[]
#define LONG
Definition imageio_dng.h:56
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
gboolean dt_sentry_backtrace_captured(void)
Definition sentry.c:687
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
const float const float param
void dt_set_signal_handlers()
void dt_set_unhandled_exception_handler_win()
#define PR_SET_PTRACER
static LONG WINAPI dt_toplevel_exception_handler(PEXCEPTION_POINTERS pExceptionInfo)
static int _times_handlers_were_set
static dt_signal_handler_t * _orig_sig_handlers[(sizeof(_signals_to_preserve)/sizeof(_signals_to_preserve[0]))]
#define _NUM_SIGNALS_TO_PRESERVE
defined (_WIN32)
static const int _signals_to_preserve[]
static LPTOP_LEVEL_EXCEPTION_FILTER _dt_exceptionfilter_old_handler
void() dt_signal_handler_t(int)