30#include <glib/gstdio.h>
51#ifndef WIN32_LEAN_AND_MEAN
52#define WIN32_LEAN_AND_MEAN
65#define DT_SENTRY_ENABLED_KEY "sentry/enabled"
66#define DT_SENTRY_ASKED_KEY "sentry/consent_asked"
67#define DT_SENTRY_CLEAN_SESSIONS_KEY "sentry/clean_sessions"
68#define DT_SENTRY_LAST_SESSION_KEY "sentry/last_session_seconds"
69#define DT_SENTRY_TOTAL_SESSION_KEY "sentry/total_session_seconds"
71static gboolean _sentry_inited =
FALSE;
78#define DT_SENTRY_MAX_CRASH_OBSERVERS 4
80static int _crash_observer_count = 0;
84 if(
IS_NULL_PTR(observer) || _crash_observer_count >= DT_SENTRY_MAX_CRASH_OBSERVERS)
return;
85 _crash_observers[_crash_observer_count++] = observer;
89static void _sentry_notify_crash_observers(
const char *backtrace, gsize backtrace_len)
91 for(
int i = 0;
i < _crash_observer_count;
i++)
92 if(_crash_observers[
i]) _crash_observers[
i](backtrace, backtrace_len);
97static volatile sig_atomic_t _sentry_backtrace_captured = 0;
102static GHashTable *_module_usage = NULL;
106static GMutex _processed_image_lock;
107static int32_t _processed_imgid = -1;
108static char _processed_pipeline[32] = { 0 };
111static volatile int _processed_image_count = 0;
115static double _sentry_session_seconds(
void)
118 return (dur > 0.0) ? dur : 0.0;
123static void _sentry_stamp_session_length(sentry_value_t event)
125 const double dur = _sentry_session_seconds();
128 sentry_value_t extra = sentry_value_get_by_key(event,
"extra");
129 if(sentry_value_is_null(extra))
131 extra = sentry_value_new_object();
132 sentry_value_set_by_key(event,
"extra", extra);
134 sentry_value_set_by_key(extra,
"session_seconds", sentry_value_new_double(dur));
135 sentry_value_set_by_key(extra,
"images_processed", sentry_value_new_int32(_processed_image_count));
140 snprintf(buf,
sizeof(buf),
"%.0f", dur);
142 snprintf(ibuf,
sizeof(ibuf),
"%d", _processed_image_count);
143 sentry_value_t tags = sentry_value_get_by_key(event,
"tags");
144 if(sentry_value_is_null(tags))
146 tags = sentry_value_new_object();
147 sentry_value_set_by_key(event,
"tags", tags);
149 sentry_value_set_by_key(tags,
"session_seconds", sentry_value_new_string(buf));
150 sentry_value_set_by_key(tags,
"images_processed", sentry_value_new_string(ibuf));
155static sentry_value_t _sentry_before_send(sentry_value_t event,
void *hint,
void *user_data)
157 _sentry_stamp_session_length(event);
161#if defined(__linux__)
166static char *_sentry_capture_gdb_backtrace(gsize *len)
169 const int fd = g_file_open_tmp(
"ansel_sentry_bt_XXXXXX.txt", &
name, NULL);
170 if(fd == -1)
return NULL;
173 gchar *pid_arg = g_strdup_printf(
"%d", (
int)getpid());
174 gchar *exe_arg = g_strdup_printf(
"/proc/%s/exe", pid_arg);
175 gchar *log_file_arg = g_strdup_printf(
"set logging file %s",
name);
177 char *contents = NULL;
178 const pid_t pid = fork();
182 execlp(
"gdb",
"gdb", exe_arg, pid_arg,
"-batch",
183 "-ex",
"set pagination off",
184 "-ex",
"set confirm off",
186 "-ex",
"set logging overwrite on",
187 "-ex",
"set logging redirect on",
188 "-ex",
"set logging enabled on",
189 "-ex",
"thread apply all bt full",
196 waitpid(pid, NULL, 0);
199 if(g_file_get_contents(
name, &contents, &
n, NULL))
213 g_free(log_file_arg);
220static void _sentry_module_name(DWORD64 addr,
char *
out,
size_t out_len)
222 g_strlcpy(
out,
"?", out_len);
224 if(GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
225 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
226 (LPCSTR)(uintptr_t)addr, &mod)
230 if(GetModuleFileNameA(mod, path,
sizeof(path)))
232 const char *base = strrchr(path,
'\\');
233 g_strlcpy(
out, base ? base + 1 :
path, out_len);
244static char *_sentry_capture_windows_backtrace(
const sentry_ucontext_t *uctx, gsize *len)
249 const size_t n = sentry_unwind_stack_from_ucontext(uctx, frames, 128);
250 if(
n == 0)
return NULL;
252 const HANDLE
proc = GetCurrentProcess();
253 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
256 SymInitialize(proc, NULL,
TRUE);
258 GString *
out = g_string_new(NULL);
259 g_string_append_printf(
out,
"this is %s reporting a crash (local DbgHelp backtrace, crashing thread):\n\n",
263 char symbuf[
sizeof(SYMBOL_INFO) + 512];
264 SYMBOL_INFO *sym = (SYMBOL_INFO *)symbuf;
265 memset(symbuf, 0,
sizeof(symbuf));
266 sym->SizeOfStruct =
sizeof(SYMBOL_INFO);
267 sym->MaxNameLen = 512;
269 for(
size_t i = 0;
i <
n;
i++)
271 const DWORD64 addr = (DWORD64)(uintptr_t)frames[
i];
273 char modname[MAX_PATH];
274 _sentry_module_name(addr, modname,
sizeof(modname));
276 const unsigned long long fno = (
unsigned long long)
i;
278 if(SymFromAddr(proc, addr, &disp, sym))
281 IMAGEHLP_LINE64 line;
282 memset(&line, 0,
sizeof(line));
283 line.SizeOfStruct =
sizeof(IMAGEHLP_LINE64);
284 if(SymGetLineFromAddr64(proc, addr, &line_disp, &line))
285 g_string_append_printf(
out,
"#%-2llu 0x%016llx %s+0x%llx (%s:%lu) [%s]\n", fno,
286 (
unsigned long long)addr, sym->Name, (
unsigned long long)disp,
287 line.FileName, (
unsigned long)line.LineNumber, modname);
289 g_string_append_printf(
out,
"#%-2llu 0x%016llx %s+0x%llx [%s]\n", fno, (
unsigned long long)addr,
290 sym->Name, (
unsigned long long)disp, modname);
294 g_string_append_printf(
out,
"#%-2llu 0x%016llx ? [%s]\n", fno, (
unsigned long long)addr, modname);
298 if(len) *len =
out->len;
306static sentry_value_t _sentry_on_crash(
const sentry_ucontext_t *uctx, sentry_value_t event,
void *user_data)
308 _sentry_stamp_session_length(event);
316#if defined(__linux__)
317 bt = _sentry_capture_gdb_backtrace(&bt_len);
322 sentry_attach_bytes(bt, bt_len,
"gdb-backtrace.txt");
326 _sentry_backtrace_captured = 1;
329 bt = _sentry_capture_windows_backtrace(uctx, &bt_len);
332 sentry_attach_bytes(bt, bt_len,
"windows-backtrace.txt");
335 _sentry_backtrace_captured = 1;
344 if(bt && bt_len > 0) _sentry_notify_crash_observers(bt, bt_len);
352 return _sentry_backtrace_captured != 0;
357 if(!_sentry_inited || !category || !
name || !*
name)
return;
360 _module_usage = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
364 char *
key = g_strdup_printf(
"%s/%s", category,
name);
365 const int count = GPOINTER_TO_INT(g_hash_table_lookup(_module_usage,
key)) + 1;
366 g_hash_table_insert(_module_usage,
key, GINT_TO_POINTER(count));
370 sentry_value_t obj = sentry_value_new_object();
373 g_hash_table_iter_init(&iter, _module_usage);
374 while(g_hash_table_iter_next(&iter, &
k, &
v))
375 sentry_value_set_by_key(obj, (
const char *)
k, sentry_value_new_int32(GPOINTER_TO_INT(
v)));
376 sentry_set_context(
"module_usage", obj);
381 if(!_sentry_inited || !img)
return;
382 const char *pl = pipeline ? pipeline :
"";
386 g_mutex_lock(&_processed_image_lock);
387 if(img->
id == _processed_imgid && !strcmp(pl, _processed_pipeline))
389 g_mutex_unlock(&_processed_image_lock);
392 _processed_imgid = img->
id;
393 g_strlcpy(_processed_pipeline, pl,
sizeof(_processed_pipeline));
394 _processed_image_count++;
395 g_mutex_unlock(&_processed_image_lock);
398 const char *dot = strrchr(img->
filename,
'.');
400 sentry_value_t o = sentry_value_new_object();
401 sentry_value_set_by_key(o,
"extension", sentry_value_new_string(dot ? dot + 1 :
""));
402 sentry_value_set_by_key(o,
"pipeline", sentry_value_new_string(pl));
403 sentry_value_set_by_key(o,
"raw", sentry_value_new_bool(
dt_image_is_raw(img)));
404 sentry_value_set_by_key(o,
"ldr", sentry_value_new_bool(
dt_image_is_ldr(img)));
405 sentry_value_set_by_key(o,
"hdr", sentry_value_new_bool(
dt_image_is_hdr(img)));
409 sentry_value_set_by_key(o,
"needs_demosaic", sentry_value_new_bool(img->
dsc.
filters != 0));
410 sentry_value_set_by_key(o,
"width", sentry_value_new_int32(img->
width));
411 sentry_value_set_by_key(o,
"height", sentry_value_new_int32(img->
height));
412 sentry_set_context(
"processed_image", o);
417static void _sentry_set_context(
void)
420 sentry_value_t device = sentry_value_new_object();
421 sentry_value_set_by_key(device,
"cpu_logical_cores", sentry_value_new_int32(g_get_num_processors()));
427 sentry_value_set_by_key(device,
"memory_gb", sentry_value_new_double(mem_gb));
431 sentry_value_set_by_key(device,
"opencl_enabled", sentry_value_new_bool(cl_enabled));
437 sentry_value_t gpus = sentry_value_new_list();
441 if(
name) sentry_value_append(gpus, sentry_value_new_string(
name));
443 sentry_value_set_by_key(device,
"opencl_devices", gpus);
449 sentry_set_context(
"device", device);
451#if !defined(_WIN32) && !defined(__APPLE__)
454 const char *session_type = g_getenv(
"XDG_SESSION_TYPE");
455 if(!session_type || !*session_type)
458 if(g_getenv(
"WAYLAND_DISPLAY"))
459 session_type =
"wayland";
460 else if(g_getenv(
"DISPLAY"))
461 session_type =
"x11";
463 if(session_type && *session_type) sentry_set_tag(
"display_server", session_type);
465 const char *desktop = g_getenv(
"XDG_CURRENT_DESKTOP");
466 if(!desktop || !*desktop) desktop = g_getenv(
"DESKTOP_SESSION");
467 if(desktop && *desktop) sentry_set_tag(
"desktop_environment", desktop);
472 GdkDisplay *display = gdk_display_get_default();
473 if(display) sentry_set_tag(
"gdk_backend", G_OBJECT_TYPE_NAME(display));
482 sentry_value_t scr = sentry_value_new_object();
483 sentry_value_set_by_key(scr,
"dpi", sentry_value_new_double(
dt_screen_dpi()));
485 sentry_value_set_by_key(scr,
"ppd", sentry_value_new_double(
dt_screen_ppd()));
489 if(win_w > 0 && win_h > 0)
491 sentry_value_set_by_key(scr,
"window_width", sentry_value_new_int32(win_w));
492 sentry_value_set_by_key(scr,
"window_height", sentry_value_new_int32(win_h));
496 snprintf(wbuf,
sizeof(wbuf),
"%dx%d", win_w, win_h);
497 sentry_set_tag(
"window_size", wbuf);
501 GdkDisplay *gdkdisp = gdk_display_get_default();
502 GdkMonitor *
mon = gdkdisp ? gdk_display_get_primary_monitor(gdkdisp) : NULL;
503 if(!
mon && gdkdisp && gdk_display_get_n_monitors(gdkdisp) > 0)
504 mon = gdk_display_get_monitor(gdkdisp, 0);
508 gdk_monitor_get_geometry(
mon, &geo);
509 sentry_value_set_by_key(scr,
"screen_width", sentry_value_new_int32(geo.width));
510 sentry_value_set_by_key(scr,
"screen_height", sentry_value_new_int32(geo.height));
511 sentry_value_set_by_key(scr,
"monitor_scale_factor",
512 sentry_value_new_int32(gdk_monitor_get_scale_factor(
mon)));
515 snprintf(sbuf,
sizeof(sbuf),
"%dx%d", geo.width, geo.height);
516 sentry_set_tag(
"screen_size", sbuf);
518 sentry_set_context(
"display", scr);
526 sentry_set_tag(
"asserts",
"disabled");
528 sentry_set_tag(
"asserts",
"enabled");
532 sentry_set_extra(
"build_cflags", sentry_value_new_string(
DT_BUILD_C_FLAGS));
533 sentry_set_tag(
"opencl", cl_enabled ?
"yes" :
"no");
546 sentry_value_t user = sentry_value_new_object();
547 sentry_value_set_by_key(user,
"id", sentry_value_new_string(
dt_install_id()));
548 sentry_set_user(user);
556 sentry_set_extra(
"clean_sessions_local",
557 sentry_value_new_int32(
dt_conf_get_int(DT_SENTRY_CLEAN_SESSIONS_KEY)));
562 sentry_set_extra(
"previous_session_seconds",
564 sentry_set_extra(
"total_session_seconds",
569static const char *_sentry_platform(
void)
573#elif defined(__APPLE__)
588 if(SENTRY_DSN[0] ==
'\0')
594 sentry_options_t *
options = sentry_options_new();
595 sentry_options_set_dsn(
options, SENTRY_DSN);
600 char *db_path = g_build_filename(cachedir,
"sentry-native", NULL);
601 sentry_options_set_database_path(
options, db_path);
610 sentry_options_set_release(
options, release);
623 char environment[128];
624 snprintf(environment,
sizeof(environment),
"%s-%s",
DT_BUILD_CHANNEL, _sentry_platform());
625 sentry_options_set_environment(
options, environment);
629 sentry_options_set_before_send(
options, _sentry_before_send, NULL);
632 sentry_options_set_on_crash(
options, _sentry_on_crash, NULL);
637 sentry_options_set_auto_session_tracking(
options, 1);
641 _sentry_inited =
TRUE;
642 _sentry_set_context();
653 if(!_sentry_inited)
return;
662 const int dur = (int)_sentry_session_seconds();
668 _sentry_inited =
FALSE;
672 g_hash_table_destroy(_module_usage);
673 _module_usage = NULL;
const char * dt_install_id(void)
const char * dt_session_id(void)
const dt_colormatrix_t dt_aligned_pixel_t out
int dt_conf_get_bool(const char *name)
void dt_conf_set_int(const char *name, int val)
void dt_conf_set_int64(const char *name, int64_t val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
int64_t dt_conf_get_int64(const char *name)
64-bit integer for name, clamped to its declared bounds.
gboolean dt_image_is_raw(const dt_image_t *img)
gboolean dt_image_is_hdr(const dt_image_t *img)
gboolean dt_image_is_monochrome(const dt_image_t *img)
gboolean dt_image_is_ldr(const dt_image_t *img)
const char darktable_commit_hash[]
const char darktable_package_string[]
const char darktable_package_version[]
int dt_get_num_openmp_threads(void)
Number of OpenMP threads the application decided to use.
size_t dt_get_total_mem(void)
void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
int32_t dt_get_debug_flags(void)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
const char * dt_opencl_get_device_name(const int devid)
Human-readable device name, owned by the OpenCL module.
int dt_opencl_is_enabled(void)
int dt_opencl_get_num_devices(void)
Number of usable OpenCL devices; 0 when OpenCL is unavailable.
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
double dt_screen_dpi(void)
double dt_screen_dpi_factor(void)
gboolean dt_screen_metrics_probed(void)
double dt_screen_ppd(void)
gboolean dt_sentry_backtrace_captured(void)
void dt_sentry_record_module_usage(const char *category, const char *name)
void dt_sentry_shutdown(void)
void dt_sentry_init(const gboolean have_gui)
void dt_sentry_set_processed_image(const struct dt_image_t *img, const char *pipeline)
void(* dt_sentry_crash_observer_t)(const char *backtrace, size_t backtrace_len)
Handed the crash backtrace from inside the crash handler.
void dt_sentry_add_crash_observer(dt_sentry_crash_observer_t observer)
Register a function to be handed the backtrace when the process crashes.
static const char *const mon[12]
char filename[DT_MAX_FILENAME_LEN]
typedef double((*spd)(unsigned long int wavelength, double TempK))
double dt_get_start_wtime(void)
static double dt_get_wtime(void)