diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.ui b/src/libsysprof-ui/sysprof-profiler-assistant.ui index e0f52c96..c0f43f80 100644 --- a/src/libsysprof-ui/sysprof-profiler-assistant.ui +++ b/src/libsysprof-ui/sysprof-profiler-assistant.ui @@ -27,6 +27,18 @@ GNOME Shell sysprof-library + + Speedtrack + sysprof-gtk + + + + + + libsysprof-speedtrack-3.so + + + GTK sysprof-gtk @@ -201,6 +213,14 @@ true + + + Track slow operations on your applications main loop + speedtrack_aid + false + true + + diff --git a/src/libsysprof/preload/meson.build b/src/libsysprof/preload/meson.build index 0714f551..dd237a3a 100644 --- a/src/libsysprof/preload/meson.build +++ b/src/libsysprof/preload/meson.build @@ -1,19 +1,25 @@ -libsysprof_memory_preload_deps = [ - cc.find_library('dl', required: false), +libdl_dep = cc.find_library('dl', required: false) + +preload_deps = [ libsysprof_capture_dep, + libdl_dep, ] if get_option('libunwind') - libsysprof_memory_preload_deps += [libunwind_dep] + preload_deps += [libunwind_dep] endif -libsysprof_memory_preload_sources = [ - 'sysprof-memory-collector.c', -] libsysprof_memory_preload = shared_library('sysprof-memory-@0@'.format(libsysprof_api_version), - libsysprof_memory_preload_sources, - dependencies: libsysprof_memory_preload_deps, + ['sysprof-memory-collector.c'], + dependencies: preload_deps, + install: true, + install_dir: get_option('libexecdir'), +) + +libsysprof_speedtrack_preload = shared_library('sysprof-speedtrack-@0@'.format(libsysprof_api_version), + ['sysprof-speedtrack-collector.c'], + dependencies: preload_deps, install: true, install_dir: get_option('libdir'), ) diff --git a/src/libsysprof/preload/sysprof-speedtrack-collector.c b/src/libsysprof/preload/sysprof-speedtrack-collector.c new file mode 100644 index 00000000..36572508 --- /dev/null +++ b/src/libsysprof/preload/sysprof-speedtrack-collector.c @@ -0,0 +1,368 @@ +/* sysprof-speedtrack-collector.c + * + * Copyright 2020 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define _GNU_SOURCE + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "backtrace-helper.h" + +static void hook_func (void **addr, const char *name); +static int hook_open (const char *filename, int flags, ...); +static int hook_close (int fd); +static int hook_fsync (int fd); +static int hook_fdatasync (int fd); +static int hook_msync (void *addr, size_t length, int flags); +static ssize_t hook_read (int fd, void *buf, size_t nbyte); +static ssize_t hook_write (int fd, const void *buf, size_t nbyte); + +static __thread gboolean rec_guard; +static int (*real_open) (const char *filename, int flags, ...) = hook_open; +static int (*real_close) (int fd) = hook_close; +static int (*real_fsync) (int fd) = hook_fsync; +static int (*real_fdatasync) (int fd) = hook_fdatasync; +static int (*real_msync) (void *addr, size_t length, int flags) = hook_msync; +static ssize_t (*real_read) (int fd, void *buf, size_t nbyte) = hook_read; +static ssize_t (*real_write) (int fd, const void *buf, size_t nbyte) = hook_write; + +static inline gboolean +is_capturing (void) +{ + static __thread int tid = 0; + static int pid = 0; + + if (rec_guard) + return FALSE; + + if G_UNLIKELY (tid == 0) + tid = syscall (__NR_gettid, 0); + + if G_UNLIKELY (pid == 0) + pid = getpid (); + + return tid == pid; +} + +static void +hook_func (void **addr, + const char *name) +{ + static GRecMutex m; + static gboolean did_init; + + g_rec_mutex_lock (&m); + if (!did_init) + { + did_init = TRUE; + backtrace_init (); + } + g_rec_mutex_unlock (&m); + + *addr = dlsym (RTLD_NEXT, name); +} + +int +open (const char *filename, + int flags, + ...) +{ + va_list args; + mode_t mode; + + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + + if (is_capturing ()) + { + gchar str[1024]; + gint64 begin; + gint64 end; + int ret; + + rec_guard = TRUE; + + begin = SYSPROF_CAPTURE_CURRENT_TIME; + ret = real_open (filename, flags, mode); + end = SYSPROF_CAPTURE_CURRENT_TIME; + + g_snprintf (str, sizeof str, + "flags = 0x%x, mode = 0%o, filename = %s => %d", + flags, mode, filename, ret); + sysprof_collector_sample (backtrace_func, NULL); + sysprof_collector_mark (begin, end - begin, "speedtrack", "open", str); + + rec_guard = FALSE; + + return ret; + } + + return real_open (filename, flags, mode); +} + +static int +hook_open (const char *filename, int flags, ...) +{ + va_list args; + mode_t mode; + + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + + hook_func ((void **)&real_open, "open"); + + return real_open (filename, flags, mode); +} + +int +close (int fd) +{ + if (is_capturing ()) + { + gint64 begin; + gint64 end; + gchar fdstr[32]; + int ret; + + rec_guard = TRUE; + + begin = SYSPROF_CAPTURE_CURRENT_TIME; + ret = real_close (fd); + end = SYSPROF_CAPTURE_CURRENT_TIME; + + g_snprintf (fdstr, sizeof fdstr, "fd = %d => %d", fd, ret); + sysprof_collector_sample (backtrace_func, NULL); + sysprof_collector_mark (begin, end - begin, "speedtrack", "close", fdstr); + + rec_guard = FALSE; + + return ret; + } + + return real_close (fd); +} + +static int +hook_close (int fd) +{ + hook_func ((void **)&real_close, "close"); + return real_close (fd); +} + +int +fsync (int fd) +{ + if (is_capturing ()) + { + gint64 begin; + gint64 end; + gchar fdstr[32]; + int ret; + + rec_guard = TRUE; + + begin = SYSPROF_CAPTURE_CURRENT_TIME; + ret = real_fsync (fd); + end = SYSPROF_CAPTURE_CURRENT_TIME; + + g_snprintf (fdstr, sizeof fdstr, "fd = %d => %d", fd, ret); + sysprof_collector_sample (backtrace_func, NULL); + sysprof_collector_mark (begin, end - begin, "speedtrack", "fsync", fdstr); + + rec_guard = FALSE; + + return ret; + } + + return real_fsync (fd); +} + +static int +hook_fsync (int fd) +{ + hook_func ((void **)&real_fsync, "fsync"); + return real_fsync (fd); +} + +int +fdatasync (int fd) +{ + if (is_capturing ()) + { + gint64 begin; + gint64 end; + gchar fdstr[32]; + int ret; + + rec_guard = TRUE; + + begin = SYSPROF_CAPTURE_CURRENT_TIME; + ret = real_fdatasync (fd); + end = SYSPROF_CAPTURE_CURRENT_TIME; + + g_snprintf (fdstr, sizeof fdstr, "fd = %d => %d", fd, ret); + sysprof_collector_sample (backtrace_func, NULL); + sysprof_collector_mark (begin, end - begin, "speedtrack", "fdatasync", fdstr); + + rec_guard = FALSE; + + return ret; + } + + return real_fdatasync (fd); +} + +static int +hook_fdatasync (int fd) +{ + hook_func ((void **)&real_fdatasync, "fdatasync"); + return real_fdatasync (fd); +} + +int +msync (void *addr, + size_t length, + int flags) +{ + if (is_capturing ()) + { + gint64 begin; + gint64 end; + gchar str[64]; + int ret; + + rec_guard = TRUE; + + begin = SYSPROF_CAPTURE_CURRENT_TIME; + ret = real_msync (addr, length, flags); + end = SYSPROF_CAPTURE_CURRENT_TIME; + + g_snprintf (str, sizeof str, "addr = %p, length = %"G_GSIZE_FORMAT", flags = %d => %d", + addr, length, flags, ret); + sysprof_collector_sample (backtrace_func, NULL); + sysprof_collector_mark (begin, end - begin, "speedtrack", "msync", str); + + rec_guard = FALSE; + + return ret; + } + + return real_msync (addr, length, flags); +} + +static int +hook_msync (void *addr, + size_t length, + int flags) +{ + hook_func ((void **)&real_msync, "msync"); + return real_msync (addr, length, flags); +} + +ssize_t +read (int fd, + void *buf, + size_t nbyte) +{ + if (is_capturing ()) + { + gint64 begin; + gint64 end; + gchar str[64]; + ssize_t ret; + + rec_guard = TRUE; + + begin = SYSPROF_CAPTURE_CURRENT_TIME; + ret = real_read (fd, buf, nbyte); + end = SYSPROF_CAPTURE_CURRENT_TIME; + + g_snprintf (str, sizeof str, "fd = %d, buf = %p, nbyte = %"G_GSIZE_FORMAT" => %"G_GSSIZE_FORMAT, + fd, buf, nbyte, ret); + sysprof_collector_sample (backtrace_func, NULL); + sysprof_collector_mark (begin, end - begin, "speedtrack", "read", str); + + rec_guard = FALSE; + + return ret; + } + + return real_read (fd, buf, nbyte); +} + +static ssize_t +hook_read (int fd, + void *buf, + size_t nbyte) +{ + hook_func ((void **)&real_read, "read"); + return real_read (fd, buf, nbyte); +} + +ssize_t +write (int fd, + const void *buf, + size_t nbyte) +{ + if (is_capturing ()) + { + gint64 begin; + gint64 end; + gchar str[64]; + ssize_t ret; + + rec_guard = TRUE; + + begin = SYSPROF_CAPTURE_CURRENT_TIME; + ret = real_write (fd, buf, nbyte); + end = SYSPROF_CAPTURE_CURRENT_TIME; + + g_snprintf (str, sizeof str, "fd = %d, buf = %p, nbyte = %"G_GSIZE_FORMAT" => %"G_GSSIZE_FORMAT, + fd, buf, nbyte, ret); + sysprof_collector_sample (backtrace_func, NULL); + sysprof_collector_mark (begin, end - begin, "speedtrack", "write", str); + + rec_guard = FALSE; + + return ret; + } + + return real_write (fd, buf, nbyte); +} + +static ssize_t +hook_write (int fd, + const void *buf, + size_t nbyte) +{ + hook_func ((void **)&real_write, "write"); + return real_write (fd, buf, nbyte); +}