47#ifndef WIN32_LEAN_AND_MEAN
48#define WIN32_LEAN_AND_MEAN
61#define DT_SENTRY_ENABLED_KEY "sentry/enabled"
62#define DT_SENTRY_ASKED_KEY "sentry/consent_asked"
63#define DT_SENTRY_CLEAN_SESSIONS_KEY "sentry/clean_sessions"
64#define DT_SENTRY_LAST_SESSION_KEY "sentry/last_session_seconds"
65#define DT_SENTRY_TOTAL_SESSION_KEY "sentry/total_session_seconds"
67static gboolean _sentry_inited =
FALSE;
71static volatile sig_atomic_t _sentry_backtrace_captured = 0;
76static GHashTable *_module_usage = NULL;
80static GMutex _processed_image_lock;
81static int32_t _processed_imgid = -1;
82static char _processed_pipeline[32] = { 0 };
85static volatile int _processed_image_count = 0;
89static double _sentry_session_seconds(
void)
92 return (dur > 0.0) ? dur : 0.0;
97static void _sentry_stamp_session_length(sentry_value_t event)
99 const double dur = _sentry_session_seconds();
102 sentry_value_t extra = sentry_value_get_by_key(event,
"extra");
103 if(sentry_value_is_null(extra))
105 extra = sentry_value_new_object();
106 sentry_value_set_by_key(event,
"extra", extra);
108 sentry_value_set_by_key(extra,
"session_seconds", sentry_value_new_double(dur));
109 sentry_value_set_by_key(extra,
"images_processed", sentry_value_new_int32(_processed_image_count));
114 snprintf(buf,
sizeof(buf),
"%.0f", dur);
116 snprintf(ibuf,
sizeof(ibuf),
"%d", _processed_image_count);
117 sentry_value_t tags = sentry_value_get_by_key(event,
"tags");
118 if(sentry_value_is_null(tags))
120 tags = sentry_value_new_object();
121 sentry_value_set_by_key(event,
"tags", tags);
123 sentry_value_set_by_key(tags,
"session_seconds", sentry_value_new_string(buf));
124 sentry_value_set_by_key(tags,
"images_processed", sentry_value_new_string(ibuf));
129static sentry_value_t _sentry_before_send(sentry_value_t event,
void *hint,
void *user_data)
131 _sentry_stamp_session_length(event);
135#if defined(__linux__)
140static char *_sentry_capture_gdb_backtrace(gsize *len)
143 const int fd = g_file_open_tmp(
"ansel_sentry_bt_XXXXXX.txt", &
name, NULL);
144 if(fd == -1)
return NULL;
147 gchar *pid_arg = g_strdup_printf(
"%d", (
int)getpid());
148 gchar *exe_arg = g_strdup_printf(
"/proc/%s/exe", pid_arg);
149 gchar *log_file_arg = g_strdup_printf(
"set logging file %s",
name);
151 char *contents = NULL;
152 const pid_t pid = fork();
156 execlp(
"gdb",
"gdb", exe_arg, pid_arg,
"-batch",
157 "-ex",
"set pagination off",
158 "-ex",
"set confirm off",
160 "-ex",
"set logging overwrite on",
161 "-ex",
"set logging redirect on",
162 "-ex",
"set logging enabled on",
163 "-ex",
"thread apply all bt full",
169 prctl(PR_SET_PTRACER, pid, 0, 0, 0);
170 waitpid(pid, NULL, 0);
173 if(g_file_get_contents(
name, &contents, &
n, NULL))
187 g_free(log_file_arg);
194static void _sentry_module_name(DWORD64 addr,
char *
out,
size_t out_len)
196 g_strlcpy(
out,
"?", out_len);
198 if(GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
199 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
200 (LPCSTR)(uintptr_t)addr, &mod)
204 if(GetModuleFileNameA(mod, path,
sizeof(path)))
206 const char *base = strrchr(path,
'\\');
207 g_strlcpy(
out, base ? base + 1 :
path, out_len);
218static char *_sentry_capture_windows_backtrace(
const sentry_ucontext_t *uctx, gsize *len)
223 const size_t n = sentry_unwind_stack_from_ucontext(uctx, frames, 128);
224 if(
n == 0)
return NULL;
226 const HANDLE
proc = GetCurrentProcess();
227 SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
230 SymInitialize(proc, NULL,
TRUE);
232 GString *
out = g_string_new(NULL);
233 g_string_append_printf(
out,
"this is %s reporting a crash (local DbgHelp backtrace, crashing thread):\n\n",
237 char symbuf[
sizeof(SYMBOL_INFO) + 512];
238 SYMBOL_INFO *sym = (SYMBOL_INFO *)symbuf;
239 memset(symbuf, 0,
sizeof(symbuf));
240 sym->SizeOfStruct =
sizeof(SYMBOL_INFO);
241 sym->MaxNameLen = 512;
243 for(
size_t i = 0;
i <
n;
i++)
245 const DWORD64 addr = (DWORD64)(uintptr_t)frames[
i];
247 char modname[MAX_PATH];
248 _sentry_module_name(addr, modname,
sizeof(modname));
250 const unsigned long long fno = (
unsigned long long)
i;
252 if(SymFromAddr(proc, addr, &disp, sym))
255 IMAGEHLP_LINE64 line;
256 memset(&line, 0,
sizeof(line));
257 line.SizeOfStruct =
sizeof(IMAGEHLP_LINE64);
258 if(SymGetLineFromAddr64(proc, addr, &line_disp, &line))
259 g_string_append_printf(
out,
"#%-2llu 0x%016llx %s+0x%llx (%s:%lu) [%s]\n", fno,
260 (
unsigned long long)addr, sym->Name, (
unsigned long long)disp,
261 line.FileName, (
unsigned long)line.LineNumber, modname);
263 g_string_append_printf(
out,
"#%-2llu 0x%016llx %s+0x%llx [%s]\n", fno, (
unsigned long long)addr,
264 sym->Name, (
unsigned long long)disp, modname);
268 g_string_append_printf(
out,
"#%-2llu 0x%016llx ? [%s]\n", fno, (
unsigned long long)addr, modname);
272 if(len) *len =
out->len;
280static sentry_value_t _sentry_on_crash(
const sentry_ucontext_t *uctx, sentry_value_t event,
void *user_data)
282 _sentry_stamp_session_length(event);
284#if defined(__linux__)
286 char *bt = _sentry_capture_gdb_backtrace(&bt_len);
291 sentry_attach_bytes(bt, bt_len,
"gdb-backtrace.txt");
295 _sentry_backtrace_captured = 1;
300 char *bt = _sentry_capture_windows_backtrace(uctx, &bt_len);
303 sentry_attach_bytes(bt, bt_len,
"windows-backtrace.txt");
306 _sentry_backtrace_captured = 1;
316 return _sentry_backtrace_captured != 0;
321 if(!_sentry_inited || !category || !
name || !*
name)
return;
324 _module_usage = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
328 char *
key = g_strdup_printf(
"%s/%s", category,
name);
329 const int count = GPOINTER_TO_INT(g_hash_table_lookup(_module_usage,
key)) + 1;
330 g_hash_table_insert(_module_usage,
key, GINT_TO_POINTER(count));
334 sentry_value_t obj = sentry_value_new_object();
337 g_hash_table_iter_init(&iter, _module_usage);
338 while(g_hash_table_iter_next(&iter, &
k, &
v))
339 sentry_value_set_by_key(obj, (
const char *)
k, sentry_value_new_int32(GPOINTER_TO_INT(
v)));
340 sentry_set_context(
"module_usage", obj);
345 if(!_sentry_inited || !img)
return;
346 const char *pl = pipeline ? pipeline :
"";
350 g_mutex_lock(&_processed_image_lock);
351 if(img->
id == _processed_imgid && !strcmp(pl, _processed_pipeline))
353 g_mutex_unlock(&_processed_image_lock);
356 _processed_imgid = img->
id;
357 g_strlcpy(_processed_pipeline, pl,
sizeof(_processed_pipeline));
358 _processed_image_count++;
359 g_mutex_unlock(&_processed_image_lock);
362 const char *dot = strrchr(img->
filename,
'.');
364 sentry_value_t o = sentry_value_new_object();
365 sentry_value_set_by_key(o,
"extension", sentry_value_new_string(dot ? dot + 1 :
""));
366 sentry_value_set_by_key(o,
"pipeline", sentry_value_new_string(pl));
367 sentry_value_set_by_key(o,
"raw", sentry_value_new_bool(
dt_image_is_raw(img)));
368 sentry_value_set_by_key(o,
"ldr", sentry_value_new_bool(
dt_image_is_ldr(img)));
369 sentry_value_set_by_key(o,
"hdr", sentry_value_new_bool(
dt_image_is_hdr(img)));
373 sentry_value_set_by_key(o,
"needs_demosaic", sentry_value_new_bool(img->
dsc.
filters != 0));
374 sentry_value_set_by_key(o,
"width", sentry_value_new_int32(img->
width));
375 sentry_value_set_by_key(o,
"height", sentry_value_new_int32(img->
height));
376 sentry_set_context(
"processed_image", o);
381static void _sentry_set_context(
void)
384 sentry_value_t device = sentry_value_new_object();
385 sentry_value_set_by_key(device,
"cpu_logical_cores", sentry_value_new_int32(g_get_num_processors()));
391 sentry_value_set_by_key(device,
"memory_gb", sentry_value_new_double(mem_gb));
395 sentry_value_set_by_key(device,
"opencl_enabled", sentry_value_new_bool(cl_enabled));
401 sentry_value_t gpus = sentry_value_new_list();
405 if(
name) sentry_value_append(gpus, sentry_value_new_string(
name));
407 sentry_value_set_by_key(device,
"opencl_devices", gpus);
413 sentry_set_context(
"device", device);
415#if !defined(_WIN32) && !defined(__APPLE__)
418 const char *session_type = g_getenv(
"XDG_SESSION_TYPE");
419 if(!session_type || !*session_type)
422 if(g_getenv(
"WAYLAND_DISPLAY"))
423 session_type =
"wayland";
424 else if(g_getenv(
"DISPLAY"))
425 session_type =
"x11";
427 if(session_type && *session_type) sentry_set_tag(
"display_server", session_type);
429 const char *desktop = g_getenv(
"XDG_CURRENT_DESKTOP");
430 if(!desktop || !*desktop) desktop = g_getenv(
"DESKTOP_SESSION");
431 if(desktop && *desktop) sentry_set_tag(
"desktop_environment", desktop);
436 GdkDisplay *display = gdk_display_get_default();
437 if(display) sentry_set_tag(
"gdk_backend", G_OBJECT_TYPE_NAME(display));
446 sentry_value_t scr = sentry_value_new_object();
447 sentry_value_set_by_key(scr,
"dpi", sentry_value_new_double(
darktable.
gui->
dpi));
449 sentry_value_set_by_key(scr,
"ppd", sentry_value_new_double(
darktable.
gui->
ppd));
453 if(win_w > 0 && win_h > 0)
455 sentry_value_set_by_key(scr,
"window_width", sentry_value_new_int32(win_w));
456 sentry_value_set_by_key(scr,
"window_height", sentry_value_new_int32(win_h));
460 snprintf(wbuf,
sizeof(wbuf),
"%dx%d", win_w, win_h);
461 sentry_set_tag(
"window_size", wbuf);
465 GdkDisplay *gdkdisp = gdk_display_get_default();
466 GdkMonitor *
mon = gdkdisp ? gdk_display_get_primary_monitor(gdkdisp) : NULL;
467 if(!
mon && gdkdisp && gdk_display_get_n_monitors(gdkdisp) > 0)
468 mon = gdk_display_get_monitor(gdkdisp, 0);
472 gdk_monitor_get_geometry(
mon, &geo);
473 sentry_value_set_by_key(scr,
"screen_width", sentry_value_new_int32(geo.width));
474 sentry_value_set_by_key(scr,
"screen_height", sentry_value_new_int32(geo.height));
475 sentry_value_set_by_key(scr,
"monitor_scale_factor",
476 sentry_value_new_int32(gdk_monitor_get_scale_factor(
mon)));
479 snprintf(sbuf,
sizeof(sbuf),
"%dx%d", geo.width, geo.height);
480 sentry_set_tag(
"screen_size", sbuf);
482 sentry_set_context(
"display", scr);
490 sentry_set_tag(
"asserts",
"disabled");
492 sentry_set_tag(
"asserts",
"enabled");
496 sentry_set_extra(
"build_cflags", sentry_value_new_string(
DT_BUILD_C_FLAGS));
497 sentry_set_tag(
"opencl", cl_enabled ?
"yes" :
"no");
509 sentry_value_t user = sentry_value_new_object();
510 sentry_value_set_by_key(user,
"id", sentry_value_new_string(
dt_install_id()));
511 sentry_set_user(user);
519 sentry_set_extra(
"clean_sessions_local",
520 sentry_value_new_int32(
dt_conf_get_int(DT_SENTRY_CLEAN_SESSIONS_KEY)));
525 sentry_set_extra(
"previous_session_seconds",
527 sentry_set_extra(
"total_session_seconds",
538 if(SENTRY_DSN[0] ==
'\0')
544 sentry_options_t *
options = sentry_options_new();
545 sentry_options_set_dsn(
options, SENTRY_DSN);
550 char *db_path = g_build_filename(cachedir,
"sentry-native", NULL);
551 sentry_options_set_database_path(
options, db_path);
560 sentry_options_set_release(
options, release);
570 sentry_options_set_before_send(
options, _sentry_before_send, NULL);
573 sentry_options_set_on_crash(
options, _sentry_on_crash, NULL);
578 sentry_options_set_auto_session_tracking(
options, 1);
582 _sentry_inited =
TRUE;
583 _sentry_set_context();
594 if(!_sentry_inited)
return;
603 const int dur = (int)_sentry_session_seconds();
609 _sentry_inited =
FALSE;
613 g_hash_table_destroy(_module_usage);
614 _module_usage = NULL;
const dt_colormatrix_t dt_aligned_pixel_t out
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_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)
int64_t dt_conf_get_int64(const char *name)
const char * dt_install_id(void)
const char * dt_session_id(void)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
static double dt_get_wtime(void)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
void dt_loc_get_user_cache_dir(char *cachedir, size_t bufsize)
float *const restrict const size_t k
int dt_opencl_is_enabled(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)
static const char *const mon[12]
int32_t num_openmp_threads
struct dt_gui_gtk_t * gui
struct dt_sys_resources_t dtresources
struct dt_opencl_t * opencl
char filename[DT_MAX_FILENAME_LEN]
typedef double((*spd)(unsigned long int wavelength, double TempK))